mirror of https://github.com/schollz/croc.git
235 lines
4.9 KiB
Go
235 lines
4.9 KiB
Go
package utils
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"crypto/md5"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"math"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/cespare/xxhash"
|
|
"github.com/kalafut/imohash"
|
|
"github.com/schollz/mnemonicode"
|
|
)
|
|
|
|
// Exists reports whether the named file or directory exists.
|
|
func Exists(name string) bool {
|
|
if _, err := os.Stat(name); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// GetInput returns the input with a given prompt
|
|
func GetInput(prompt string) string {
|
|
reader := bufio.NewReader(os.Stdin)
|
|
fmt.Fprintf(os.Stderr, "%s", prompt)
|
|
text, _ := reader.ReadString('\n')
|
|
return strings.TrimSpace(text)
|
|
}
|
|
|
|
// HashFile returns the hash of a file
|
|
func HashFile(fname string) (hash256 []byte, err error) {
|
|
return IMOHashFile(fname)
|
|
}
|
|
|
|
// MD5HashFile returns MD5 hash
|
|
func MD5HashFile(fname string) (hash256 []byte, err error) {
|
|
f, err := os.Open(fname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
h := md5.New()
|
|
if _, err = io.Copy(h, f); err != nil {
|
|
return
|
|
}
|
|
|
|
hash256 = h.Sum(nil)
|
|
return
|
|
}
|
|
|
|
// IMOHashFile returns imohash
|
|
func IMOHashFile(fname string) (hash []byte, err error) {
|
|
b, err := imohash.SumFile(fname)
|
|
hash = b[:]
|
|
return
|
|
}
|
|
|
|
// XXHashFile returns the xxhash of a file
|
|
func XXHashFile(fname string) (hash256 []byte, err error) {
|
|
f, err := os.Open(fname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
h := xxhash.New()
|
|
if _, err = io.Copy(h, f); err != nil {
|
|
return
|
|
}
|
|
|
|
hash256 = h.Sum(nil)
|
|
return
|
|
}
|
|
|
|
// SHA256 returns sha256 sum
|
|
func SHA256(s string) string {
|
|
sha := sha256.New()
|
|
sha.Write([]byte(s))
|
|
return fmt.Sprintf("%x", sha.Sum(nil))
|
|
}
|
|
|
|
// PublicIP returns public ip address
|
|
func PublicIP() (ip string, err error) {
|
|
resp, err := http.Get("https://canhazip.com")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode == http.StatusOK {
|
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
ip = strings.TrimSpace(string(bodyBytes))
|
|
}
|
|
return
|
|
}
|
|
|
|
// LocalIP returns local ip address
|
|
func LocalIP() string {
|
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer conn.Close()
|
|
|
|
localAddr := conn.LocalAddr().(*net.UDPAddr)
|
|
|
|
return localAddr.IP.String()
|
|
}
|
|
|
|
// GetRandomName returns mnemoicoded random name
|
|
func GetRandomName() string {
|
|
result := []string{}
|
|
bs := make([]byte, 4)
|
|
rand.Read(bs)
|
|
result = mnemonicode.EncodeWordList(result, bs)
|
|
return strings.Join(result, "-")
|
|
}
|
|
|
|
// ByteCountDecimal converts bytes to human readable byte string
|
|
func ByteCountDecimal(b int64) string {
|
|
const unit = 1000
|
|
if b < unit {
|
|
return fmt.Sprintf("%d B", b)
|
|
}
|
|
div, exp := int64(unit), 0
|
|
for n := b / unit; n >= unit; n /= unit {
|
|
div *= unit
|
|
exp++
|
|
}
|
|
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
|
|
}
|
|
|
|
// MissingChunks returns the positions of missing chunks.
|
|
// If file doesn't exist, it returns an empty chunk list (all chunks).
|
|
// If the file size is not the same as requested, it returns an empty chunk list (all chunks).
|
|
func MissingChunks(fname string, fsize int64, chunkSize int) (chunkRanges []int64) {
|
|
f, err := os.Open(fname)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer f.Close()
|
|
|
|
fstat, err := os.Stat(fname)
|
|
if fstat.Size() != fsize || err != nil {
|
|
return
|
|
}
|
|
|
|
emptyBuffer := make([]byte, chunkSize)
|
|
chunkNum := 0
|
|
chunks := make([]int64, int64(math.Ceil(float64(fsize)/float64(chunkSize))))
|
|
var currentLocation int64
|
|
for {
|
|
buffer := make([]byte, chunkSize)
|
|
bytesread, err := f.Read(buffer)
|
|
if err != nil {
|
|
break
|
|
}
|
|
if bytes.Equal(buffer[:bytesread], emptyBuffer[:bytesread]) {
|
|
chunks[chunkNum] = currentLocation
|
|
chunkNum++
|
|
}
|
|
currentLocation += int64(bytesread)
|
|
}
|
|
if chunkNum == 0 {
|
|
chunkRanges = []int64{}
|
|
} else {
|
|
chunks = chunks[:chunkNum]
|
|
chunkRanges = []int64{int64(chunkSize), chunks[0]}
|
|
curCount := 0
|
|
for i, chunk := range chunks {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
curCount++
|
|
if chunk-chunks[i-1] > int64(chunkSize) {
|
|
chunkRanges = append(chunkRanges, int64(curCount))
|
|
chunkRanges = append(chunkRanges, chunk)
|
|
curCount = 0
|
|
}
|
|
}
|
|
chunkRanges = append(chunkRanges, int64(curCount+1))
|
|
chunks = chunkRanges
|
|
}
|
|
return
|
|
}
|
|
|
|
// ChunkRangesToChunks converts chunk ranges to list
|
|
func ChunkRangesToChunks(chunkRanges []int64) (chunks []int64) {
|
|
if len(chunkRanges) == 0 {
|
|
return
|
|
}
|
|
chunkSize := chunkRanges[0]
|
|
chunks = []int64{}
|
|
for i := 1; i < len(chunkRanges); i += 2 {
|
|
for j := int64(0); j < (chunkRanges[i+1]); j++ {
|
|
chunks = append(chunks, chunkRanges[i]+j*chunkSize)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// GetLocalIPs returns all local ips
|
|
func GetLocalIPs() (ips []string, err error) {
|
|
addrs, err := net.InterfaceAddrs()
|
|
if err != nil {
|
|
return
|
|
}
|
|
ips = []string{}
|
|
for _, address := range addrs {
|
|
// check the address type and if it is not a loopback the display it
|
|
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
|
if ipnet.IP.To4() != nil {
|
|
ips = append(ips, ipnet.IP.String())
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|