commit 1ea024e28f42d77ff697dd773fe1f130560d3c08 Author: 兔子 Date: Tue Apr 4 14:11:09 2023 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fae3642 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +bin +.idea \ No newline at end of file diff --git a/aes/aes.go b/aes/aes.go new file mode 100644 index 0000000..c7dc15d --- /dev/null +++ b/aes/aes.go @@ -0,0 +1,115 @@ +package aes + +import ( + "errors" + "fmt" + "io" + "os" + + "b612.me/starcrypto" + "b612.me/staros" +) + +func EncodeStr(str string, key []byte) string { + if len(key) < 16 { + key = starcrypto.Md5(key) + } else { + key = key[:len(key)/16*16] + if len(key) > 32 { + key = key[:32] + } + } + ensdata := starcrypto.AesEncryptCFBNoBlock([]byte(str), key) + return starcrypto.Base91EncodeToString(ensdata) +} + +func DecodeStr(str string, key []byte) string { + if len(key) < 16 { + key = starcrypto.Md5(key) + } else { + key = key[:len(key)/16*16] + } + strtmp := starcrypto.Base91DecodeString(str) + str = string(strtmp) + return string(starcrypto.AesDecryptCFBNoBlock([]byte(str), key)) +} + +func EncodeFile(fpath, out string, key []byte) error { + if len(key) < 16 { + key = starcrypto.Md5(key) + } else { + key = key[:len(key)/16*16] + } + if !staros.Exists(fpath) { + return errors.New("SrcFile Not Exists") + } + fpsrc, err := os.Open(fpath) + if err != nil { + return err + } + defer fpsrc.Close() + fpdst, err := os.Create(out) + if err != nil { + return err + } + defer fpdst.Close() + bufsize := 1024 * 1024 //1MB + stat, _ := fpsrc.Stat() + buf := make([]byte, bufsize) + var sumAll int64 + for { + n, err := fpsrc.Read(buf) + if err != nil && err != io.EOF { + return err + } + fmt.Printf("已完成:%.2f%%\r", float64(sumAll)/float64(stat.Size())*100) + sumAll += int64(n) + encodeBytes := starcrypto.AesEncryptCFBNoBlock(buf[:n], key) + fpdst.Write(encodeBytes) + if err == io.EOF { + fmt.Print("已完成:100% \n") + break + } + } + return nil +} + +func DecodeFile(fpath, out string, key []byte) error { + if len(key) < 16 { + key = starcrypto.Md5(key) + } else { + key = key[:len(key)/16*16] + } + if !staros.Exists(fpath) { + return errors.New("SrcFile Not Exists") + } + fpsrc, err := os.Open(fpath) + if err != nil { + return err + } + defer fpsrc.Close() + fpdst, err := os.Create(out) + if err != nil { + return err + } + defer fpdst.Close() + bufsize := 1024 * 1024 //1MB + stat, _ := fpsrc.Stat() + buf := make([]byte, bufsize) + var sumAll int64 + for { + n, err := fpsrc.Read(buf) + if err != nil && err != io.EOF { + return err + } + fmt.Printf("已完成:%.2f%%\r", float64(sumAll)/float64(stat.Size())*100) + sumAll += int64(n) + encodeBytes := starcrypto.AesDecryptCFBNoBlock(buf[:n], key) + fpdst.Write(encodeBytes) + if err == io.EOF { + fmt.Print("已完成:100% \n") + break + } + } + return nil +} diff --git a/aes/aes_test.go b/aes/aes_test.go new file mode 100644 index 0000000..afcfb1d --- /dev/null +++ b/aes/aes_test.go @@ -0,0 +1,20 @@ +package aes + +import ( + "fmt" + "testing" + + "b612.me/starcrypto" +) + +func Test_EncodeStr(t *testing.T) { + //fmt.Println(EncodeStr("我喜欢你", "sakurasaiteruyogugugug")) +} + +func Test_DecodeStr(t *testing.T) { + //fmt.Println(DecodeStr("Z_8aILbog@Kjm$P", "sakurasaiteruyogugugug")) +} + +func Test_Base91(t *testing.T) { + fmt.Println(starcrypto.Base91EncodeToString([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})) +} diff --git a/aes/cmd.go b/aes/cmd.go new file mode 100644 index 0000000..225a69a --- /dev/null +++ b/aes/cmd.go @@ -0,0 +1,93 @@ +package aes + +import ( + "encoding/hex" + "fmt" + "os" + "path/filepath" + + "b612.me/starcrypto" + + "b612.me/starlog" + "b612.me/staros" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "aes-cfb", + Short: "使用aes-cfb处理文件或字符串", + Long: "使用aes-cfb处理文件或字符串", + Run: func(this *cobra.Command, args []string) { + var err error + ok, _ := this.Flags().GetBool("file") + de, _ := this.Flags().GetBool("decode") + key, _ := this.Flags().GetString("key") + keyfile, _ := this.Flags().GetString("keyfile") + if len(key) == 0 && len(keyfile) == 0 { + starlog.Errorln("请指定指定Key或KeyFile") + os.Exit(1) + } + if len(key) != 0 && len(keyfile) != 0 { + starlog.Errorln("不能同时指定Key与KeyFile") + os.Exit(1) + } + if len(args) != 1 { + starlog.Criticalln("参数不足,请输入文件地址或字符串") + this.Help() + os.Exit(2) + } + var byteKey []byte + if len(key) != 0 { + byteKey = []byte(key) + } else { + if !staros.Exists(keyfile) { + starlog.Errorln("keyfile不存在:", keyfile) + os.Exit(3) + } + fmt.Println("读取Key文件中……") + tmpstr, err := starcrypto.FileSum(keyfile, "sha256", func(ok float64) { fmt.Printf("完成读取%.2f\r", ok) }) + if err != nil { + starlog.Errorln("keyfile读取失败:", err) + os.Exit(4) + } + fmt.Println() + byteKey, _ = hex.DecodeString(tmpstr) + } + if ok { + path, _ := this.Flags().GetString("out") + if path == "" { + ext := filepath.Ext(args[0]) + if ext != "" { + path = args[0][:len(args[0])-len(ext)] + ".aescfb" + ext + } else { + path = args[0] + ".aescfb" + } + } + if !de { + err = EncodeFile(args[0], path, byteKey) + } else { + err = DecodeFile(args[0], path, byteKey) + } + } else { + if !de { + data := EncodeStr(args[0], byteKey) + fmt.Println(data) + } else { + data := DecodeStr(args[0], byteKey) + fmt.Println(string(data)) + } + } + if err != nil { + starlog.Criticalln(err) + return + } + }, +} + +func init() { + Cmd.Flags().BoolP("file", "f", false, "aes-cfb处理文件") + Cmd.Flags().StringP("out", "o", "", "文件加解密输出地址") + Cmd.Flags().StringP("key", "k", "", "加解密Key字符串") + Cmd.Flags().StringP("keyfile", "s", "", "加解密Key文件(不能与key字符串同时选择)") + Cmd.Flags().BoolP("decode", "d", false, "进行aes-cfb解码") +} diff --git a/attach/attach.go b/attach/attach.go new file mode 100644 index 0000000..33ee241 --- /dev/null +++ b/attach/attach.go @@ -0,0 +1,78 @@ +package attach + +import ( + "io" + "os" + + "b612.me/starlog" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "attach", + Short: "合并多个文件", + Long: "合并多个文件", + Run: func(this *cobra.Command, args []string) { + src, _ := this.Flags().GetStringArray("src") + out, _ := this.Flags().GetString("out") + if len(src) < 2 || out == "" { + starlog.Criticalln("请输入至少2个输入路径,一个输出路径") + os.Exit(1) + } + os.Exit(runAttach(src, out)) + }, +} + +func runAttach(src []string, out string) int { + fpOut, err := os.Create(out) + if err != nil { + starlog.Errorln("Err:无法创建输出文件!", err) + return 2 + } + defer fpOut.Close() + for _, file := range src { + fpFile, err := os.OpenFile(file, os.O_RDONLY, 0644) + if err != nil { + starlog.Errorln("Err:Cannot Openfile:", err) + return 3 + } + stats, err := fpFile.Stat() + if err != nil { + starlog.Errorln("Err:Cannot Get File Stats:", err) + return 4 + } + if stats.Size() == 0 { + starlog.Warningf("文件:%s为空文件,跳过!\n", stats.Name()) + continue + } + buf := make([]byte, 65535) + sumAll := 0 + for { + n, err := fpFile.Read(buf) + if err != nil && err != io.EOF { + starlog.Errorln("Err:Error Occured While ReadFile:", err) + fpFile.Close() + return 5 + } + sumAll += n + _, errW := fpOut.Write(buf[:n]) + if errW != nil { + starlog.Errorln("Error While Write Data to OutFile", errW) + return 6 + } + starlog.StdPrintf([]starlog.Attr{starlog.FgGreen}, "文件%s,已完成:%.2f%%\r", stats.Name(), (float64(sumAll) / float64(stats.Size()) * 100.0000)) + if err == io.EOF { + starlog.StdPrintf([]starlog.Attr{starlog.FgGreen}, "文件:%v,已完成:100.00%% \n", stats.Name()) + fpFile.Close() + break + } + } + } + starlog.StdPrintln([]starlog.Attr{starlog.FgGreen}, "Ok!文件合并完成") + return 0 +} + +func init() { + Cmd.Flags().StringArrayP("src", "s", []string{}, "源文件路径") + Cmd.Flags().StringP("out", "o", "", "输出文件路径") +} diff --git a/base64/base64.go b/base64/base64.go new file mode 100644 index 0000000..2a87b7c --- /dev/null +++ b/base64/base64.go @@ -0,0 +1,68 @@ +package base64 + +import ( + "fmt" + "path/filepath" + + "b612.me/starcrypto" + "b612.me/starlog" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "base64", + Short: "使用base64处理文件或字符串", + Long: "使用base64处理文件或字符串", + Run: func(this *cobra.Command, args []string) { + var err error + ok, _ := this.Flags().GetBool("file") + de, _ := this.Flags().GetBool("decode") + if len(args) != 1 { + starlog.Criticalln("参数不足,请输入文件地址或字符串") + this.Help() + return + } + shell := func(pect float64) { + if pect == 100 { + fmt.Println("已处理:100.000000%") + } else { + fmt.Printf("已处理:%f%%\r", pect) + } + } + if ok { + path, _ := this.Flags().GetString("out") + if path == "" { + ext := filepath.Ext(args[0]) + if ext != "" { + path = args[0][:len(args[0])-len(ext)] + ".b64" + ext + } else { + path = args[0] + ".b64" + } + } + if !de { + err = starcrypto.Base64EncodeFile(args[0], path, shell) + } else { + err = starcrypto.Base64DecodeFile(args[0], path, shell) + } + } else { + if !de { + data := starcrypto.Base64Encode([]byte(args[0])) + fmt.Println(data) + } else { + var data []byte + data, err = starcrypto.Base64Decode(args[0]) + fmt.Println(string(data)) + } + } + if err != nil { + starlog.Criticalln(err) + return + } + }, +} + +func init() { + Cmd.Flags().BoolP("file", "f", false, "base64处理文件") + Cmd.Flags().StringP("out", "o", "", "指定加解码输出地址") + Cmd.Flags().BoolP("decode", "d", false, "base64解码") +} diff --git a/base85/base85.go b/base85/base85.go new file mode 100644 index 0000000..5c1f44d --- /dev/null +++ b/base85/base85.go @@ -0,0 +1,67 @@ +package base85 + +import ( + "fmt" + "path/filepath" + + "b612.me/starcrypto" + "b612.me/starlog" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "base85", + Short: "使用base85处理文件或字符串", + Long: "使用base85处理文件或字符串", + Run: func(this *cobra.Command, args []string) { + var err error + ok, _ := this.Flags().GetBool("file") + de, _ := this.Flags().GetBool("decode") + if len(args) != 1 { + starlog.Criticalln("参数不足,请输入文件地址或字符串") + return + } + shell := func(pec float64) { + if pec == 100 { + fmt.Println("已处理:100.000000%") + } else { + fmt.Printf("已处理:%f%%\r", pec) + } + } + if ok { + path, _ := this.Flags().GetString("out") + if path == "" { + ext := filepath.Ext(args[0]) + if ext != "" { + path = args[0][:len(args[0])-len(ext)] + ".b85" + ext + } else { + path = args[0] + ".b85" + } + } + if !de { + err = starcrypto.Base85EncodeFile(args[0], path, shell) + } else { + err = starcrypto.Base85DecodeFile(args[0], path, shell) + } + } else { + if !de { + data := starcrypto.Base85Encode([]byte(args[0])) + fmt.Println(data) + } else { + var data []byte + data, err = starcrypto.Base85Decode(args[0]) + fmt.Println(string(data)) + } + } + if err != nil { + starlog.Criticalln(err) + return + } + }, +} + +func init() { + Cmd.Flags().BoolP("file", "f", false, "base85处理文件") + Cmd.Flags().StringP("out", "o", "", "指定加解码输出地址") + Cmd.Flags().BoolP("decode", "d", false, "base85解码") +} diff --git a/base91/base91.go b/base91/base91.go new file mode 100644 index 0000000..0962cc1 --- /dev/null +++ b/base91/base91.go @@ -0,0 +1,35 @@ +package base91 + +import ( + "fmt" + + "b612.me/starcrypto" + "b612.me/starlog" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "base91", + Short: "使用base91处理文件或字符串", + Long: "使用base91处理文件或字符串", + Run: func(this *cobra.Command, args []string) { + de, _ := this.Flags().GetBool("decode") + if len(args) != 1 { + starlog.Criticalln("参数不足,请输入文件地址或字符串") + return + } + if !de { + data := starcrypto.Base91EncodeToString([]byte(args[0])) + fmt.Println(data) + } else { + var data []byte + data = starcrypto.Base91DecodeString(args[0]) + fmt.Println(string(data)) + } + }, +} + +func init() { + Cmd.Flags().StringP("out", "o", "", "指定加解码输出地址") + Cmd.Flags().BoolP("decode", "d", false, "base91解码") +} diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..81652eb --- /dev/null +++ b/build.bat @@ -0,0 +1,38 @@ +mkdir bin +set GOOS=windows +set GOARCH=amd64 +go build -o .\bin\b612_x86_64.exe -ldflags "-w -s" . +upx -9 .\bin\b612_x86_64.exe +set GOARCH=386 +go build -o .\bin\b612_x86.exe -ldflags "-w -s" . +upx -9 .\bin\b612_x86.exe +set GOARCH=arm64 +go build -o .\bin\b612_arm64.exe -ldflags "-w -s" . +upx -9 .\bin\b612_arm64.exe +set GOOS=linux +set GOARCH=amd64 +go build -o .\bin\b612_x86_64 -ldflags "-w -s" . +upx -9 .\bin\b612_x86_64 +set GOARCH=386 +go build -o .\bin\b612_x86 -ldflags "-w -s" . +upx -9 .\bin\b612_x86 +set GOARCH=arm64 +go build -o .\bin\b612_arm64 -ldflags "-w -s" . +upx -9 .\bin\b612_arm64 +set GOARCH=mips +go build -o .\bin\b612_mips -ldflags "-w -s" . +set GOARCH=mipsle +go build -o .\bin\b612_mipsle -ldflags "-w -s" . +set GOARCH=mips64 +go build -o .\bin\b612_mips64 -ldflags "-w -s" . +set GOARCH=mips64le +go build -o .\bin\b612_mips64le -ldflags "-w -s" . +set GOOS=darwin +set GOARCH=amd64 +go build -o .\bin\b612_darwin_x64 -ldflags "-w -s" . +upx -9 .\bin\b612_darwin_x64 +set GOARCH=arm64 +go build -o .\bin\b612_darwin_arm64 -ldflags "-w -s" . +upx -9 .\bin\b612_darwin_arm64 + + diff --git a/detach/detach.go b/detach/detach.go new file mode 100644 index 0000000..b4bba67 --- /dev/null +++ b/detach/detach.go @@ -0,0 +1,46 @@ +package detach + +import ( + "b612.me/starcrypto" + "fmt" + + "b612.me/starlog" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "detach", + Short: "分离两个文件", + Long: "分离两个文件", + Run: func(this *cobra.Command, args []string) { + var src, dst, out string + if len(args) == 3 { + src = args[0] + dst = args[1] + out = args[2] + } else { + src, _ = this.Flags().GetString("src") + dst, _ = this.Flags().GetString("dst") + out, _ = this.Flags().GetString("out") + } + num, _ := this.Flags().GetInt("num") + if src == "" || dst == "" { + starlog.Criticalln("ERROR PATH") + this.Help() + return + } + err := starcrypto.Detach(src, num, dst, out) + if err != nil { + starlog.Criticalln(err.Error) + } else { + fmt.Println("完成") + } + }, +} + +func init() { + Cmd.Flags().StringP("src", "s", "", "源文件路径") + Cmd.Flags().StringP("dst", "d", "", "目标文件路径1") + Cmd.Flags().StringP("out", "o", "", "目标文件路径2") + Cmd.Flags().IntP("num", "n", 0, "分割开始字节") +} diff --git a/df/df.go b/df/df.go new file mode 100644 index 0000000..9b4c758 --- /dev/null +++ b/df/df.go @@ -0,0 +1,116 @@ +package df + +import ( + "b612.me/starlog" + "b612.me/staros" + "b612.me/wincmd/ntfs/mft" + "fmt" + "github.com/spf13/cobra" + "io/ioutil" + "math" + "os" + "path/filepath" + "sort" + "strings" +) + +var Cmd = &cobra.Command{ + Use: "df", + Short: "分析nfts磁盘文件占用", + Long: "分析nfts磁盘文件占用", + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + os.Exit(folderSize("./")) + } + os.Exit(folderSize(args[0])) + }, +} + +func folderSize(path string) int { + fullPath, err := filepath.Abs(path) + if err != nil { + starlog.Criticalln("filepath not a vaild path", path, err) + return 1 + } + vol := filepath.VolumeName(fullPath) + `\` + fmt.Println(vol) + if staros.IsFile(fullPath) { + fullPath = filepath.Dir(fullPath) + } + fmt.Println("consider folder is", fullPath) + folderLists, err := ioutil.ReadDir(fullPath) + if err != nil { + starlog.Criticalln("read folder failed", err) + return 2 + } + targetFolders := make(map[string]uint64) + for _, v := range folderLists { + if v.IsDir() { + //fmt.Println(filepath.Join(fullPath, v.Name())) + targetFolders[filepath.Join(fullPath, v.Name())] = 0 + } + } + fileLists, err := mft.GetFileListsByMft(vol) + if err != nil { + starlog.Errorln("read mft failed", err) + return 3 + } + var totalSize uint64 + var fc1, fc2 int + for _, v := range fileLists { + if strings.Contains(v.Path, fullPath) { + if v.IsDir { + fc2++ + } else { + fc1++ + } + totalSize += v.Aszie + } + for k, _ := range targetFolders { + if strings.Contains(v.Path, k) { + targetFolders[k] += v.Aszie + } + } + } + getSize := func(size uint64) string { + floatSize := float64(size) + var sizeC = []string{"byte", "KB", "MB", "GB", "TB"} + if floatSize < 1024 { + return "" + } + for i := 0; i < 4; i++ { + if floatSize/math.Pow(1024, float64(i+1)) < 1024 { + return fmt.Sprintf("%.4f%s", floatSize/math.Pow(1024, float64(i+1)), sizeC[i+1]) + } + } + return "" + } + target := sortMapByValue(targetFolders) + for _, v := range target { + fmt.Printf("%-20s %-10d %s\n", v.Key, v.Value, getSize(v.Value)) + } + fmt.Printf("%-20s %-10d %s\n", fullPath, totalSize, getSize(totalSize)) + fmt.Println("file count:", fc1, "folder count:", fc2) + return 0 +} + +type Pair struct { + Key string + Value uint64 +} +type PairList []Pair + +func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p PairList) Len() int { return len(p) } +func (p PairList) Less(i, j int) bool { return p[i].Value < p[j].Value } + +func sortMapByValue(m map[string]uint64) PairList { + p := make(PairList, len(m)) + i := 0 + for k, v := range m { + p[i] = Pair{k, v} + i++ + } + sort.Sort(p) + return p +} diff --git a/dfinder/dfinder.go b/dfinder/dfinder.go new file mode 100644 index 0000000..3a21413 --- /dev/null +++ b/dfinder/dfinder.go @@ -0,0 +1,113 @@ +package dfinder + +import ( + "b612.me/starlog" + "b612.me/staros" + "b612.me/wincmd/ntfs/mft" + "fmt" + "github.com/spf13/cobra" + "math" + "os" + "path/filepath" + "regexp" + "strings" +) + +var dfpath, dfreg, dfoutpath string +var dfonlyname, dfshow bool + +func init() { + Cmd.Flags().StringVarP(&dfoutpath, "outpath", "o", "", "outpath") + Cmd.Flags().StringVarP(&dfpath, "path", "p", "./", "path you want to search") + Cmd.Flags().StringVarP(&dfreg, "regexp", "r", ".*", "search regexp") + Cmd.Flags().BoolVarP(&dfonlyname, "only-filter-name", "f", false, "only regexp for name not path") + Cmd.Flags().BoolVarP(&dfshow, "show", "s", true, "show on the stdout") +} + +var Cmd = &cobra.Command{ + Use: "dfinder", + Short: "查找nfts磁盘文件", + Long: "查找nfts磁盘文件", + Run: func(cmd *cobra.Command, args []string) { + os.Exit(fileFinder(dfpath, dfreg, dfonlyname, dfoutpath, dfshow)) + }, +} + +func fileFinder(path string, reg string, onlyFilterName bool, outpath string, show bool) int { + fullPath, err := filepath.Abs(path) + if err != nil { + starlog.Criticalln("filepath not a vaild path", path, err) + return 1 + } + vol := filepath.VolumeName(fullPath) + `\` + fmt.Println(vol) + if staros.IsFile(fullPath) { + fullPath = filepath.Dir(fullPath) + } + fmt.Println("consider folder is", fullPath) + + fileLists, err := mft.GetFileListsByMftFn(vol, func(name string, isFolder bool) bool { + if isFolder { + return true + } + if onlyFilterName { + if matched, _ := regexp.MatchString(reg, name); matched { + return true + } + return false + } + return true + }) + if err != nil { + starlog.Errorln("read mft failed", err) + return 3 + } + getSize := func(size uint64) string { + floatSize := float64(size) + var sizeC = []string{"byte", "KB", "MB", "GB", "TB"} + if floatSize < 1024 { + return "" + } + for i := 0; i < 4; i++ { + if floatSize/math.Pow(1024, float64(i+1)) < 1024 { + return fmt.Sprintf("%.4f%s", floatSize/math.Pow(1024, float64(i+1)), sizeC[i+1]) + } + } + return "" + } + fp := new(os.File) + if outpath != "" { + fp, err = os.Create(outpath) + if err != nil { + starlog.Criticalln(err) + } else { + defer fp.Close() + fp.WriteString(`名称,路径,大小,大小(格式化),修改时间,是否是文件夹` + "\n") + } + } + newReg := regexp.MustCompile(reg) + for _, v := range fileLists { + if !strings.Contains(v.Path, fullPath) { + continue + } + if onlyFilterName { + if show { + fmt.Printf("name:%-10s path:%-20s size:%-10s modTime:%v\n", v.Name, v.Path, getSize(v.Size), v.ModTime) + } + if fp != nil { + fp.WriteString(fmt.Sprintf("%s,%s,%v,%s,%v,%v\n", v.Name, v.Path, v.Size, getSize(v.Size), v.ModTime, v.IsDir)) + } + } else { + if newReg.MatchString(v.Path) { + if show { + fmt.Printf("name:%-10s path:%-20s size:%-10s modTime:%v\n", v.Name, v.Path, getSize(v.Size), v.ModTime) + } + if fp != nil { + fp.WriteString(fmt.Sprintf("%s,%s,%v,%s,%v,%v\n", v.Name, v.Path, v.Size, getSize(v.Size), v.ModTime, v.IsDir)) + } + } + } + + } + return 0 +} diff --git a/ftp/ftp.go b/ftp/ftp.go new file mode 100644 index 0000000..8591fe0 --- /dev/null +++ b/ftp/ftp.go @@ -0,0 +1,50 @@ +package ftp + +import ( + "log" + "path/filepath" + + filedriver "github.com/goftp/file-driver" + "github.com/goftp/server" + "github.com/spf13/cobra" +) + +var ports int +var username, pwd string +var path, ip string + +// ftpCmd represents the ftp command +var Cmd = &cobra.Command{ + Use: "ftp", + Short: `FTP文件服务器`, + Long: `FTP文件服务器`, + Run: func(cmd *cobra.Command, args []string) { + path, _ = filepath.Abs(path) + factory := &filedriver.FileDriverFactory{ + RootPath: path, + Perm: server.NewSimplePerm("user", "group"), + } + opts := &server.ServerOpts{ + Factory: factory, + Port: ports, + Hostname: ip, + Auth: &server.SimpleAuth{Name: username, Password: pwd}, + } + + log.Printf("Starting ftp server on %v:%v", opts.Hostname, opts.Port) + log.Printf("Username %v, Password %v", username, pwd) + server := server.NewServer(opts) + err := server.ListenAndServe() + if err != nil { + log.Fatal("Error starting server:", err) + } + }, +} + +func init() { + Cmd.Flags().IntVarP(&ports, "port", "p", 21, "监听端口") + Cmd.Flags().StringVarP(&ip, "ip", "i", "0.0.0.0", "监听地址") + Cmd.Flags().StringVarP(&username, "user", "u", "1", "用户名,默认为1") + Cmd.Flags().StringVarP(&pwd, "pwd", "k", "1", "密码,默认为1") + Cmd.Flags().StringVarP(&path, "folder", "f", "./", "本地文件地址") +} diff --git a/generate/generate.go b/generate/generate.go new file mode 100644 index 0000000..9756c41 --- /dev/null +++ b/generate/generate.go @@ -0,0 +1,95 @@ +package generate + +import ( + "bufio" + "fmt" + "math/rand" + "os" + "time" + + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "generate", + Short: "生成随机文件", + Long: "生成指定大小的随机文件", + Run: func(this *cobra.Command, args []string) { + sum, _ := this.Flags().GetInt("sum") + num, _ := this.Flags().GetInt("num") + cap, _ := this.Flags().GetInt("cap") + if len(args) != 1 { + this.Help() + return + } + if num <= 0 { + fmt.Println("num不合法,不应该小于1!") + os.Exit(2) + } + if sum < 0 { + fmt.Println("sum不合法,不应该小于0!") + os.Exit(2) + } + if cap <= 0 { + fmt.Println("cap不合法,不应该小于1!") + os.Exit(2) + } + err := FillWithRandom(args[0], num, cap, sum, func(pect float64) { + fmt.Printf("文件已处理:%f%%\r", pect) + }) + if err != nil { + fmt.Println("err:" + err.Error()) + } + fmt.Println("文件已处理:100.0000000%") + time.Sleep(time.Millisecond * 10) + }, +} + +func init() { + Cmd.Flags().IntP("sum", "s", 3, "随机的种子组数") + Cmd.Flags().IntP("num", "n", 1024, "生成的文件大小") + Cmd.Flags().IntP("cap", "c", 1048576, "bufcap大小") + Cmd.MarkFlagRequired("num") +} + +// FillWithRandom 随机写filesize大小的文件,每次buf大小为bufcap,随机bufnum个字符 +func FillWithRandom(filepath string, filesize int, bufcap int, bufnum int, shell func(float64)) error { + var buf [][]byte + var buftmp []byte + rand.Seed(time.Now().Unix()) + if bufnum <= 0 { + bufnum = 1 + } + if bufcap > filesize { + bufcap = filesize + } + myfile, err := os.Create(filepath) + if err != nil { + return err + } + defer myfile.Close() + writer := bufio.NewWriter(myfile) + for i := 0; i < bufnum; i++ { + buftmp = []byte{} + for j := 0; j < bufcap; j++ { + buftmp = append(buftmp, byte(rand.Intn(256))) + } + buf = append(buf, buftmp) + } + sum := 0 + for { + if filesize-sum < bufcap { + writer.Write(buf[rand.Intn(bufnum)][0 : filesize-sum]) + sum += filesize - sum + } else { + writer.Write(buf[rand.Intn(bufnum)]) + sum += bufcap + } + go shell(float64(sum) / float64(filesize) * 100) + if sum >= filesize { + break + } + } + writer.Flush() + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..397a2ae --- /dev/null +++ b/go.mod @@ -0,0 +1,36 @@ +module b612.me/apps/b612 + +go 1.19 + +require ( + b612.me/notify v1.2.4 + b612.me/starcrypto v0.0.2 + b612.me/stario v0.0.8 + b612.me/starlog v1.3.2 + b612.me/staros v1.1.6 + b612.me/starssh v0.0.2 + b612.me/startext v0.0.0-20220314043758-22c6d5e5b1cd + b612.me/wincmd v0.0.2 + github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 + github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42 + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 + github.com/spf13/cobra v1.6.1 +) + +require ( + b612.me/starmap v1.2.3 // indirect + b612.me/starnet v0.1.7 // indirect + b612.me/win32api v0.0.1 // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/jlaffaye/ftp v0.1.0 // indirect + github.com/kr/fs v0.1.0 // indirect + github.com/pkg/sftp v1.13.4 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/testify v1.8.0 // indirect + golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect + golang.org/x/image v0.6.0 // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/term v0.5.0 // indirect + golang.org/x/text v0.8.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..6c396c1 --- /dev/null +++ b/go.sum @@ -0,0 +1,108 @@ +b612.me/notify v1.2.4 h1:cjP80V9FeM+ib1DztZdykusakcbjNI4dAB1pXE8U6bo= +b612.me/notify v1.2.4/go.mod h1:SlCrG1kPRVhYUrIkwY/j0zAwCU4VeTHubcZoQXW8Anw= +b612.me/starcrypto v0.0.2 h1:aRf1HcqK8GqHYxLAhWfFC4W/EqQLEFNEmxsBu3wG30o= +b612.me/starcrypto v0.0.2/go.mod h1:hz0xRnfWNpYOlVrIPoGrQOWPibq4YiUZ7qN5tsQbzPo= +b612.me/stario v0.0.7/go.mod h1:or4ssWcxQSjMeu+hRKEgtp0X517b3zdlEOAms8Qscvw= +b612.me/stario v0.0.8 h1:kaA4pszAKLZJm2D9JmiuYSpgjTeE3VaO74vm+H0vBGM= +b612.me/stario v0.0.8/go.mod h1:or4ssWcxQSjMeu+hRKEgtp0X517b3zdlEOAms8Qscvw= +b612.me/starlog v1.3.2 h1:bFUJyZEpcOcBwPlzlhPBwlYxq7aDcR8pJNoaDk+SUNE= +b612.me/starlog v1.3.2/go.mod h1:bxSvBSzlJoLfrZJ5b9CJFuQaXjFi8PYUbGWitNO1FYA= +b612.me/starmap v1.2.3 h1:+ao++KgbSGMA4UzcHm/EXJoukbUudk8t5ac7rjwV9KA= +b612.me/starmap v1.2.3/go.mod h1:K+exTSWg8i/taoUyGR6DPW6Ja0k6aIdpcniqByOf4O0= +b612.me/starnet v0.1.7 h1:k3CUfYNRolC/xw5Ekus2NVWHlqeykSyAH8USGTPKA5o= +b612.me/starnet v0.1.7/go.mod h1:DNC4i/ezgVLlmxnquf8AeljsL4mQ5vAyxh8vGPQqsys= +b612.me/staros v1.1.6 h1:m3QaEmPyvPcJVomjWs8cDeauDYFNKv7cLHTiOHClKqM= +b612.me/staros v1.1.6/go.mod h1:O657LC3qag4VSsHNmt5RM8gKJvzoEGq8IF8WegcRgq0= +b612.me/starssh v0.0.2 h1:cYlrXjd7ZTesdZG+7XcoLsEEMROaeWMTYonScBLnvyY= +b612.me/starssh v0.0.2/go.mod h1:1gvG/GT5Y5EvOx9ZKnLFUa+wOX20HaqS1IuTnU7BOlk= +b612.me/startext v0.0.0-20220314043758-22c6d5e5b1cd h1:EsmnczYZhOV8JTxD/m0N0qBjfZN8JuLNrTJ6z3S8YqA= +b612.me/startext v0.0.0-20220314043758-22c6d5e5b1cd/go.mod h1:yKdeLQHZ3scqyjw1ZODCoL+hLmkOp2eu5riP4agraz8= +b612.me/win32api v0.0.1 h1:vLFB1xhO6pd9+zB2EyaapKB459Urv3v+C1YwgwOFEWo= +b612.me/win32api v0.0.1/go.mod h1:MHu0JBQjzxQ2yxpZPUBbn5un45o67eF5iWKa4Q9e0yE= +b612.me/wincmd v0.0.2 h1:Ub1WtelVT6a3vD4B6zDYo3UPO/t9ymnI3x1dQPJcrGw= +b612.me/wincmd v0.0.2/go.mod h1:bwpyCKfSDY8scSMo3Lrd0Qnqvpz7/CILL7oodfG0wgo= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9 h1:cC0Hbb+18DJ4i6ybqDybvj4wdIDS4vnD0QEci98PgM8= +github.com/goftp/file-driver v0.0.0-20180502053751-5d604a0fc0c9/go.mod h1:GpOj6zuVBG3Inr9qjEnuVTgBlk2lZ1S9DcoFiXWyKss= +github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42 h1:JdOp2qR5PF4O75tzHeqrwnDDv8oHDptWyTbyYS4fD8E= +github.com/goftp/server v0.0.0-20200708154336-f64f7c2d8a42/go.mod h1:k/SS6VWkxY7dHPhoMQ8IdRu8L4lQtmGbhyXGg+vCnXE= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jlaffaye/ftp v0.1.0 h1:DLGExl5nBoSFoNshAUHwXAezXwXBvFdx7/qwhucWNSE= +github.com/jlaffaye/ftp v0.1.0/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= +github.com/pkg/sftp v1.13.4 h1:Lb0RYJCmgUcBgZosfoi9Y9sbl6+LJgOIgk/2Y4YjMFg= +github.com/pkg/sftp v1.13.4/go.mod h1:LzqnAvaD5TWeNBsZpfKxSYn1MbjWwOsCIAFFJbpIsK8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220313003712-b769efc7c000/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s= +golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= +golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hash/hash.go b/hash/hash.go new file mode 100644 index 0000000..3553f20 --- /dev/null +++ b/hash/hash.go @@ -0,0 +1,75 @@ +package hash + +import ( + "b612.me/starcrypto" + "encoding/hex" + "fmt" + "os" + + "b612.me/starlog" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "hash", + Short: "多种方法哈希值校验", + Long: "进行多种方法哈希值校验", + Run: func(this *cobra.Command, args []string) { + var cumethod, method []string + var result = make(map[string]string) + var err error + cumethod = []string{"md5", "crc32", "sha512", "sha384", "sha256", "sha224", "sha1"} + if ok, _ := this.Flags().GetBool("all"); ok { + method = cumethod + } else { + if len(args) == 0 { + this.Usage() + os.Exit(1) + } + + for _, v := range cumethod { + if ok, _ := this.Flags().GetBool(v); ok { + method = append(method, v) + } + } + if len(method) == 0 { + method = append(method, "md5") + } + } + if ok, _ := this.Flags().GetBool("file"); ok { + result, err = starcrypto.FileSumAll(args[0], method, func(pect float64) { + if pect != 100.0 { + fmt.Printf("校验已完成:%f%%\r", pect) + } else { + fmt.Printf("校验已完成:%f%%\n", pect) + } + }) + } else { + var bres map[string][]byte + bres, err = starcrypto.SumAll([]byte(args[0]), method) + if err == nil { + for k, v := range bres { + result[k] = hex.EncodeToString(v) + } + } + } + if err != nil { + starlog.Criticalln("错误:" + err.Error()) + } + for _, v := range method { + fmt.Printf("%s:%s\n", v, result[v]) + } + }, +} + +func init() { + Cmd.Flags().BoolP("all", "a", false, "使用所有的校验方法") + Cmd.Flags().BoolP("file", "f", false, "对指定文件进行校验") + Cmd.Flags().BoolP("md5", "m", false, "进行MD5校验(默认)") + Cmd.Flags().BoolP("crc32", "c", false, "进行CRC32校验") + Cmd.Flags().BoolP("sha512", "s", false, "进行SHA512校验") + Cmd.Flags().Bool("sha384", false, "进行SHA384校验") + Cmd.Flags().Bool("sha256", false, "进行SHA256校验") + Cmd.Flags().Bool("sha224", false, "进行SHA224校验") + Cmd.Flags().Bool("sha1", false, "进行SHA1校验") +} diff --git a/httpreverse/cfg.ini b/httpreverse/cfg.ini new file mode 100644 index 0000000..54a0d27 --- /dev/null +++ b/httpreverse/cfg.ini @@ -0,0 +1,15 @@ +[web1] +addr=0.0.0.0 +port=9999 +key=C:\\tech\b612.me.key +cert=C:\\tech\b612.me.cer +enablessl=true +reverse=/::https://www.b612.me +replace=www.b612.me::127.0.0.1:9999 +inheader=Accept-Encoding::none +host=b612.me +authuser=b612 +authpasswd=b612 +whiteip= +blackip= +wanringpage= \ No newline at end of file diff --git a/httpreverse/cmd.go b/httpreverse/cmd.go new file mode 100644 index 0000000..15c77d0 --- /dev/null +++ b/httpreverse/cmd.go @@ -0,0 +1,92 @@ +package httpreverse + +import ( + "b612.me/starlog" + "github.com/spf13/cobra" + "net/url" + "os" + "os/signal" +) + +var remote, config string +var addr, key, cert, log string +var port int +var enablessl bool +var host string + +func init() { + Cmd.Flags().StringVarP(&host, "host", "H", "", "host字段") + Cmd.Flags().StringVarP(&remote, "remote", "r", "", "反向代理地址") + Cmd.Flags().StringVarP(&config, "config", "C", "", "配置文件地址") + Cmd.Flags().StringVarP(&addr, "addr", "a", "0.0.0.0", "监听地址") + Cmd.Flags().StringVarP(&key, "key", "k", "", "ssl key地址") + Cmd.Flags().StringVarP(&cert, "cert", "c", "", "ssl 证书地址") + Cmd.Flags().StringVarP(&log, "log", "l", "", "log日志地址") + Cmd.Flags().BoolVarP(&enablessl, "enable-ssl", "s", false, "启用ssl") + Cmd.Flags().IntVarP(&port, "port", "p", 8080, "监听端口") +} + +var Cmd = &cobra.Command{ + Use: "hproxy", + Short: "Http Reverse Proxy(Http反向代理)", + Run: func(cmd *cobra.Command, args []string) { + if log != "" { + starlog.SetLogFile(log, starlog.Std, true) + } + if config != "" { + r, err := Parse(config) + if err != nil { + starlog.Errorln(err) + os.Exit(1) + } + go func() { + sig := make(chan os.Signal) + signal.Notify(sig, os.Kill, os.Interrupt) + starlog.Noticeln("Stop Due to Recv Siganl", <-sig) + r.Close() + }() + err = r.Run() + if err != nil { + starlog.Errorln("Http Reverse Proxy Exit Error", err) + os.Exit(2) + } + starlog.Infoln("Http Reverse Proxy Exit Normally") + return + } + if remote == "" { + starlog.Errorln("please enter the reverse url") + os.Exit(4) + } + u, err := url.Parse(remote) + if err != nil { + starlog.Errorln(err) + os.Exit(3) + } + reverse := ReverseConfig{ + Name: "web", + Addr: addr, + Host: host, + ReverseURL: map[string]*url.URL{ + "/": u, + }, + Port: port, + UsingSSL: enablessl, + Key: key, + Cert: cert, + XForwardMode: 1, + } + go func() { + sig := make(chan os.Signal) + signal.Notify(sig, os.Kill, os.Interrupt) + starlog.Noticeln("Stop Due to Recv Siganl", <-sig) + reverse.Close() + }() + err = reverse.Run() + if err != nil { + starlog.Errorln("Http Reverse Proxy Exit Error", err) + os.Exit(2) + } + starlog.Infoln("Http Reverse Proxy Exit Normally") + return + }, +} diff --git a/httpreverse/iputil.go b/httpreverse/iputil.go new file mode 100644 index 0000000..40c42da --- /dev/null +++ b/httpreverse/iputil.go @@ -0,0 +1,84 @@ +package httpreverse + +import ( + "strconv" + "strings" +) + +func IPCIDR(ip string) (string, int, error) { + if !strings.Contains(ip, "/") { + return ip, 32, nil + } + tmp := strings.Split(ip, "/") + cidr, err := strconv.Atoi(tmp[1]) + if err != nil { + return "", 0, err + } + intIp, err := IP2Int(tmp[0]) + if err != nil { + return "", 0, err + } + ip, err = Int2IP(int((uint32(intIp) >> (32 - cidr)) << (32 - cidr))) + return ip, cidr, err +} + +func IPEnd(ip string, cidr int) (string, error) { + intIp, err := IP2Int(ip) + if err != nil { + return "", err + } + ip, err = Int2IP(int(uint32(intIp) | ((uint32(4294967295) << cidr) >> cidr))) + return ip, err +} + +func IPStart(ip string, cidr int) (string, error) { + intIp, err := IP2Int(ip) + if err != nil { + return "", err + } + ip, err = Int2IP(int((uint32(intIp) >> (32 - cidr)) << (32 - cidr))) + return ip, err +} + +func IPInRange(cidrip, ip string) (bool, error) { + w, c, err := IPCIDR(cidrip) + if err != nil { + return false, err + } + f, _, err := IPCIDR(ip + "/" + strconv.Itoa(c)) + if err != nil { + return false, err + } + return w == f, nil +} + +func IPinRange2(startIP string, cidr int, ip string) (bool, error) { + f, _, err := IPCIDR(ip + "/" + strconv.Itoa(cidr)) + if err != nil { + return false, err + } + return startIP == f, nil +} + +func IP2Int(ipAddr string) (int, error) { + var ipNum uint32 = 0 + ipFilter := strings.Split(ipAddr, ".") + for i := 0; i < len(ipFilter); i++ { + num, err := strconv.ParseUint(ipFilter[len(ipFilter)-1-i], 10, 32) + ipNum |= (uint32(num) << (8 * uint32(i))) + if err != nil { + return 0, err + } + } + return int(ipNum), nil +} + +func Int2IP(intIP int) (string, error) { + var result string + var ip uint32 + ip = uint32(intIP) + for i := 0; i < 4; i++ { + result += strconv.FormatUint(uint64(uint8(ip>>uint8((3-i)*8))), 10) + "." + } + return result[0 : len(result)-1], nil +} diff --git a/httpreverse/reverse.go b/httpreverse/reverse.go new file mode 100644 index 0000000..9db8dbc --- /dev/null +++ b/httpreverse/reverse.go @@ -0,0 +1,174 @@ +package httpreverse + +import ( + "b612.me/staros/sysconf" + "errors" + "io/ioutil" + "net/http" + "net/http/httputil" + "net/url" + "strings" + "sync" +) + +type ReverseConfig struct { + Name string + Addr string + ReverseURL map[string]*url.URL + Port int + UsingSSL bool + Key string + Cert string + Host string + InHeader [][2]string + OutHeader [][2]string + Cookie [][3]string //[3]string should contains path::key::value + ReplaceList [][2]string + ReplaceOnce bool + proxy map[string]*httputil.ReverseProxy + XForwardMode int //0=off 1=useremote 2=add + httpmux http.ServeMux + httpserver http.Server + + basicAuthUser string + basicAuthPwd string + protectAuthPage []string + blackip map[string]int + whiteip map[string]int + warningpage string + warnpagedata []byte +} + +type HttpReverseServer struct { + Config []*ReverseConfig +} + +func Parse(path string) (HttpReverseServer, error) { + var res HttpReverseServer + ini := sysconf.NewIni() + err := ini.ParseFromFile(path) + if err != nil { + return res, err + } + for _, v := range ini.Data { + var ins = ReverseConfig{ + Name: v.Name, + Host: v.Get("host"), + Addr: v.Get("addr"), + Port: v.Int("port"), + UsingSSL: v.Bool("enablessl"), + Key: v.Get("key"), + Cert: v.Get("cert"), + ReplaceOnce: v.Bool("replaceonce"), + XForwardMode: v.Int("xforwardmode"), + basicAuthUser: v.Get("authuser"), + basicAuthPwd: v.Get("authpasswd"), + warningpage: v.Get("warnpage"), + } + if ins.warningpage != "" { + data, err := ioutil.ReadFile(ins.warningpage) + if err != nil { + return res, err + } + ins.warnpagedata = data + } + ins.proxy = make(map[string]*httputil.ReverseProxy) + ins.ReverseURL = make(map[string]*url.URL) + for _, reverse := range v.GetAll("reverse") { + kv := strings.SplitN(reverse, "::", 2) + if len(kv) != 2 { + return res, errors.New("reverse settings not correct:" + reverse) + } + ins.ReverseURL[strings.TrimSpace(kv[0])], err = url.Parse(strings.TrimSpace(kv[1])) + if err != nil { + return res, err + } + } + for _, header := range v.GetAll("inheader") { + kv := strings.SplitN(header, "::", 2) + if len(kv) != 2 { + return res, errors.New("header settings not correct:" + header) + } + ins.InHeader = append(ins.InHeader, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])}) + } + for _, authpage := range v.GetAll("authpage") { + ins.protectAuthPage = append(ins.protectAuthPage, authpage) + } + ins.blackip = make(map[string]int) + for _, blackip := range v.GetAll("blackip") { + ip, cidr, err := IPCIDR(blackip) + if err != nil { + return res, err + } + ins.blackip[ip] = cidr + } + ins.whiteip = make(map[string]int) + for _, whiteip := range v.GetAll("whiteip") { + ip, cidr, err := IPCIDR(whiteip) + if err != nil { + return res, err + } + ins.whiteip[ip] = cidr + } + for _, header := range v.GetAll("outheader") { + kv := strings.SplitN(header, "::", 2) + if len(kv) != 2 { + return res, errors.New("header settings not correct:" + header) + } + ins.OutHeader = append(ins.OutHeader, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])}) + } + for _, cookie := range v.GetAll("cookie") { + kv := strings.SplitN(cookie, "::", 3) + if len(kv) != 3 { + return res, errors.New("cookie settings not correct:" + cookie) + } + ins.Cookie = append(ins.Cookie, [3]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]), strings.TrimSpace(kv[2])}) + } + for _, replace := range v.GetAll("replace") { + kv := strings.SplitN(replace, "::", 2) + if len(kv) != 2 { + return res, errors.New("replace settings not correct:" + replace) + } + ins.ReplaceList = append(ins.ReplaceList, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])}) + } + res.Config = append(res.Config, &ins) + } + return res, nil +} + +func (h *HttpReverseServer) Run() error { + var wg sync.WaitGroup + var mu sync.Mutex + var haveErr string + for _, v := range h.Config { + wg.Add(1) + go func(v *ReverseConfig) { + defer wg.Done() + err := v.Run() + if err != nil { + mu.Lock() + haveErr += err.Error() + "\n" + mu.Unlock() + } + }(v) + } + wg.Wait() + if haveErr != "" { + return errors.New(haveErr) + } + return nil +} + +func (h *HttpReverseServer) Close() error { + var haveErr string + for _, v := range h.Config { + err := v.Close() + if err != nil { + haveErr += err.Error() + "\n" + } + } + if haveErr != "" { + return errors.New(haveErr) + } + return nil +} diff --git a/httpreverse/reverse_test.go b/httpreverse/reverse_test.go new file mode 100644 index 0000000..a72f561 --- /dev/null +++ b/httpreverse/reverse_test.go @@ -0,0 +1,23 @@ +package httpreverse + +import ( + "fmt" + "testing" +) + +func TestReverseParse(t *testing.T) { + data, err := Parse("./cfg.ini") + if err != nil { + t.Fatal(err) + } + fmt.Printf("%+v\n", data) +} + +func TestReverse(t *testing.T) { + data, err := Parse("./cfg.ini") + if err != nil { + t.Fatal(err) + } + fmt.Printf("%+v\n", data) + data.Run() +} diff --git a/httpreverse/service.go b/httpreverse/service.go new file mode 100644 index 0000000..d81fe25 --- /dev/null +++ b/httpreverse/service.go @@ -0,0 +1,262 @@ +package httpreverse + +import ( + "b612.me/starlog" + "bytes" + "context" + "encoding/base64" + "fmt" + "io/ioutil" + "net/http" + "net/http/httputil" + "net/url" + "strconv" + "strings" + "time" +) + +var version = "2.0.1" + +func (h *ReverseConfig) Run() error { + err := h.init() + if err != nil { + return err + } + for key, proxy := range h.proxy { + h.httpmux.HandleFunc(key, func(writer http.ResponseWriter, request *http.Request) { + starlog.Infof("<%s> Req Path:%s Addr:%s UA:%s\n", h.Name, request.URL.Path, request.RemoteAddr, request.Header.Get("User-Agent")) + + if !h.BasicAuth(writer, request) { + h.SetResponseHeader(writer) + return + } + if !h.filter(writer, request) { + h.SetResponseHeader(writer) + writer.WriteHeader(403) + if len(h.warnpagedata) != 0 { + writer.Write(h.warnpagedata) + return + } + writer.Write([]byte(` + + 403 Forbidden + +

403 Forbidden

+

You Are Not Allowed to Access This Page

+

Please Contact Site Administrator For Help

+
B612 HTTP REVERSE SERVER
+ + `)) + return + } + proxy.ServeHTTP(writer, request) + }) + } + h.httpserver = http.Server{ + Addr: fmt.Sprintf("%s:%d", h.Addr, h.Port), + Handler: &h.httpmux, + } + starlog.Infoln(h.Name + " Listening on " + h.Addr + ":" + strconv.Itoa(h.Port)) + if !h.UsingSSL { + if err := h.httpserver.ListenAndServe(); err != http.ErrServerClosed { + return err + } + return nil + } + if h.httpserver.ListenAndServeTLS(h.Cert, h.Key); err != http.ErrServerClosed { + return err + } + return nil +} + +func (h *ReverseConfig) Close() error { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + return h.httpserver.Shutdown(ctx) +} + +func (h *ReverseConfig) init() error { + h.proxy = make(map[string]*httputil.ReverseProxy) + for key, val := range h.ReverseURL { + h.proxy[key] = httputil.NewSingleHostReverseProxy(val) + h.proxy[key].ModifyResponse = h.ModifyResponse() + originalDirector := h.proxy[key].Director + h.proxy[key].Director = func(req *http.Request) { + originalDirector(req) + h.ModifyRequest(req, val) + } + } + return nil +} + +func (h *ReverseConfig) SetResponseHeader(resp http.ResponseWriter) { + resp.Header().Set("X-Powered-By", "B612.ME") + resp.Header().Set("Server", "B612/"+version) + resp.Header().Set("X-Proxy", "B612 Reverse Proxy") +} + +func (h *ReverseConfig) ModifyResponse() func(*http.Response) error { + return func(resp *http.Response) error { + for _, v := range h.OutHeader { + resp.Header.Set(v[0], v[1]) + } + resp.Header.Del("X-Powered-By") + resp.Header.Del("Server") + resp.Header.Del("X-Proxy") + resp.Header.Set("X-Powered-By", "B612.ME") + resp.Header.Set("Server", "B612/"+version) + resp.Header.Set("X-Proxy", "B612 Reverse Proxy") + if len(h.ReplaceList) != 0 && resp.ContentLength <= 20*1024*1024 && strings.Contains(resp.Header.Get("Content-Type"), "text") { + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + strBody := string(body) + replaceCount := -1 + if h.ReplaceOnce { + replaceCount = 1 + } + for _, v := range h.ReplaceList { + strBody = strings.Replace(strBody, v[0], v[1], replaceCount) + } + body = []byte(strBody) + resp.Body = ioutil.NopCloser(bytes.NewReader(body)) + resp.ContentLength = int64(len(body)) + resp.Header.Set("Content-Length", strconv.Itoa(len(body))) + } + return nil + } +} + +func (h *ReverseConfig) ModifyRequest(req *http.Request, remote *url.URL) { + if h.XForwardMode == 1 { + req.Header.Set("X-Forwarded-For", strings.Split(req.RemoteAddr, ":")[0]) + } else if h.XForwardMode == 2 { + xforward := strings.Split(strings.TrimSpace(req.Header.Get("X-Forwarded-For")), ",") + xforward = append(xforward, strings.Split(req.RemoteAddr, ":")[0]) + req.Header.Set("X-Forwarded-For", strings.Join(xforward, ", ")) + } + for _, v := range h.Cookie { + req.AddCookie(&http.Cookie{ + Name: v[1], + Value: v[2], + Path: v[0], + }) + } + host := h.Host + if host == "" { + host = remote.Host + } + targetQuery := remote.RawQuery + req.URL.Scheme = remote.Scheme + req.URL.Host = remote.Host + req.Host = host + req.URL.Path, req.URL.RawPath = joinURLPath(remote, req.URL) + if targetQuery == "" || req.URL.RawQuery == "" { + req.URL.RawQuery = targetQuery + req.URL.RawQuery + } else { + req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery + } + for _, v := range h.InHeader { + req.Header.Set(v[0], v[1]) + } +} + +func (h *ReverseConfig) GiveBasicAuth(w http.ResponseWriter) { + w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`) + w.WriteHeader(401) + w.Write([]byte(` + + 401 Authorization Required + +

401 Authorization Required

+
B612 HTTP SERVER
+ + `)) +} + +func (h *ReverseConfig) BasicAuth(w http.ResponseWriter, r *http.Request) bool { + if h.basicAuthPwd != "" { + if len(h.protectAuthPage) != 0 { + for _, v := range h.protectAuthPage { + if !(strings.Index(r.URL.Path, v) == 0 || strings.Contains(r.URL.RawQuery, v)) { + return true + } else { + break + } + } + } + authHeader := strings.TrimSpace(r.Header.Get("Authorization")) + if len(authHeader) == 0 { + h.GiveBasicAuth(w) + return false + } else { + userAuth := base64.StdEncoding.EncodeToString([]byte(h.basicAuthUser + ":" + h.basicAuthPwd)) + authStr := strings.Split(authHeader, " ") + if strings.TrimSpace(authStr[1]) != userAuth || strings.ToLower(strings.TrimSpace(authStr[0])) != "basic" { + h.GiveBasicAuth(w) + return false + } + } + } + return true +} + +func (h *ReverseConfig) filter(w http.ResponseWriter, r *http.Request) bool { + if len(h.blackip) == 0 && len(h.whiteip) == 0 { + return true + } + if len(h.whiteip) != 0 { + if _, ok := h.whiteip[strings.Split(r.RemoteAddr, ":")[0]]; ok { + return true + } + for k, v := range h.whiteip { + if match, _ := IPinRange2(k, v, strings.Split(r.RemoteAddr, ":")[0]); match { + return true + } + } + return false + } + if _, ok := h.blackip[strings.Split(r.RemoteAddr, ":")[0]]; ok { + return false + } + for k, v := range h.blackip { + if match, _ := IPinRange2(k, v, strings.Split(r.RemoteAddr, ":")[0]); match { + return false + } + } + return true +} + +func joinURLPath(a, b *url.URL) (path, rawpath string) { + if a.RawPath == "" && b.RawPath == "" { + return singleJoiningSlash(a.Path, b.Path), "" + } + // Same as singleJoiningSlash, but uses EscapedPath to determine + // whether a slash should be added + apath := a.EscapedPath() + bpath := b.EscapedPath() + + aslash := strings.HasSuffix(apath, "/") + bslash := strings.HasPrefix(bpath, "/") + + switch { + case aslash && bslash: + return a.Path + b.Path[1:], apath + bpath[1:] + case !aslash && !bslash: + return a.Path + "/" + b.Path, apath + "/" + bpath + } + return a.Path + b.Path, apath + bpath +} + +func singleJoiningSlash(a, b string) string { + aslash := strings.HasSuffix(a, "/") + bslash := strings.HasPrefix(b, "/") + switch { + case aslash && bslash: + return a + b[1:] + case !aslash && !bslash: + return a + "/" + b + } + return a + b +} diff --git a/httpserver/cmd.go b/httpserver/cmd.go new file mode 100644 index 0000000..e92177f --- /dev/null +++ b/httpserver/cmd.go @@ -0,0 +1,73 @@ +package httpserver + +import ( + "b612.me/starlog" + "b612.me/staros" + "context" + "github.com/spf13/cobra" + "os" + "os/signal" + "strings" +) + +var s HttpServer + +var daemon bool + +func init() { + Cmd.Flags().StringVarP(&s.port, "port", "p", "80", "监听端口") + Cmd.Flags().StringVarP(&s.addr, "ip", "i", "0.0.0.0", "监听ip") + Cmd.Flags().StringVarP(&s.envPath, "folder", "f", "./", "本地文件地址") + Cmd.Flags().StringVarP(&s.uploadFolder, "upload", "u", "", "文件上传文件夹路径") + Cmd.Flags().BoolVarP(&daemon, "daemon", "d", false, "以后台进程运行") + Cmd.Flags().StringVarP(&s.basicAuthUser, "auth", "a", "", "HTTP BASIC AUTH认证(用户名:密码)") + Cmd.Flags().StringVarP(&s.indexFile, "index", "n", "", "Index文件名,如index.html") + Cmd.Flags().StringVarP(&s.logpath, "log", "l", "", "log地址") + Cmd.Flags().StringVarP(&s.cert, "ssl-cert", "c", "", "TLS证书路径") + Cmd.Flags().StringVarP(&s.key, "ssl-key", "k", "", "TLS密钥路径") + Cmd.Flags().BoolVarP(&s.disableMIME, "disablemime", "m", false, "停止解析MIME,全部按下载文件处理") + Cmd.Flags().StringSliceVarP(&s.protectAuthPage, "protect-page", "P", []string{}, "Basic Auth 开启白名单") + Cmd.Flags().Bool("daeapplied", false, "") + Cmd.Flags().MarkHidden("daeapplied") +} + +var Cmd = &cobra.Command{ + Use: "http", + Short: "HTTP文件服务器(HTTP File Browser Server)", + Long: `HTTP文件服务器(HTTP File Browser Server)`, + Run: func(cmd *cobra.Command, args []string) { + apply, _ := cmd.Flags().GetBool("daeapplied") + if daemon && !apply { + nArgs := append(os.Args[1:], "--daeapplied") + pid, err := staros.Daemon(os.Args[0], nArgs...) + if err != nil { + starlog.Criticalln("Daemon Error:", err) + os.Exit(1) + } + starlog.StdPrintf([]starlog.Attr{starlog.FgGreen}, "Success,PID=%v\n", pid) + return + } + sig := make(chan os.Signal) + signal.Notify(sig, os.Kill, os.Interrupt) + ctx, fn := context.WithCancel(context.Background()) + go func() { + starlog.Infoln("Recv Signal", <-sig) + fn() + }() + if s.basicAuthUser != "" { + tmp := strings.SplitN(s.basicAuthUser, ":", 2) + if len(tmp) != 2 { + starlog.Errorln("basic Auth should have user and password") + os.Exit(2) + } + s.basicAuthUser = strings.TrimSpace(tmp[0]) + s.basicAuthPwd = tmp[1] + } + err := s.Run(ctx) + if err != nil { + starlog.Errorln("Http Server Closed by Errors", err) + os.Exit(1) + } + starlog.Infoln("Http Server Closed Normally") + }, +} diff --git a/httpserver/mime.go b/httpserver/mime.go new file mode 100644 index 0000000..cda0226 --- /dev/null +++ b/httpserver/mime.go @@ -0,0 +1,2037 @@ +package httpserver + +import "path/filepath" + +func (h *HttpServer) MIME(fullpath string) string { + ext := filepath.Ext(filepath.Base(fullpath)) + if len(ext) == 0 || ext == "." { + return "" + } + ext = ext[1:] + switch ext { + case "123": + return "application/vnd.lotus-1-2-3" + case "3dml": + return "text/vnd.in3d.3dml" + case "3ds": + return "image/x-3ds" + case "3g2": + return "video/3gpp2" + case "3gp": + return "video/3gpp" + case "7z": + return "application/x-7z-compressed" + case "aab": + return "application/x-authorware-bin" + case "aac": + return "audio/x-aac" + case "aam": + return "application/x-authorware-map" + case "aas": + return "application/x-authorware-seg" + case "abs": + return "audio/x-mpeg" + case "abw": + return "application/x-abiword" + case "ac": + return "application/pkix-attr-cert" + case "acc": + return "application/vnd.americandynamics.acc" + case "ace": + return "application/x-ace-compressed" + case "acu": + return "application/vnd.acucobol" + case "acutc": + return "application/vnd.acucorp" + case "adp": + return "audio/adpcm" + case "aep": + return "application/vnd.audiograph" + case "afm": + return "application/x-font-type1" + case "afp": + return "application/vnd.ibm.modcap" + case "ahead": + return "application/vnd.ahead.space" + case "ai": + return "application/postscript" + case "aif": + return "audio/x-aiff" + case "aifc": + return "audio/x-aiff" + case "aiff": + return "audio/x-aiff" + case "aim": + return "application/x-aim" + case "air": + return "application/vnd.adobe.air-application-installer-package+zip" + case "ait": + return "application/vnd.dvb.ait" + case "ami": + return "application/vnd.amiga.ami" + case "anx": + return "application/annodex" + case "apk": + return "application/vnd.android.package-archive" + case "appcache": + return "text/cache-manifest" + case "application": + return "application/x-ms-application" + case "apr": + return "application/vnd.lotus-approach" + case "arc": + return "application/x-freearc" + case "art": + return "image/x-jg" + case "asc": + return "application/pgp-signature" + case "asf": + return "video/x-ms-asf" + case "asm": + return "text/x-asm" + case "aso": + return "application/vnd.accpac.simply.aso" + case "asx": + return "video/x-ms-asf" + case "atc": + return "application/vnd.acucorp" + case "atom": + return "application/atom+xml" + case "atomcat": + return "application/atomcat+xml" + case "atomsvc": + return "application/atomsvc+xml" + case "atx": + return "application/vnd.antix.game-component" + case "au": + return "audio/basic" + case "avi": + return "video/x-msvideo" + case "avx": + return "video/x-rad-screenplay" + case "aw": + return "application/applixware" + case "axa": + return "audio/annodex" + case "axv": + return "video/annodex" + case "azf": + return "application/vnd.airzip.filesecure.azf" + case "azs": + return "application/vnd.airzip.filesecure.azs" + case "azw": + return "application/vnd.amazon.ebook" + case "bat": + return "application/x-msdownload" + case "bcpio": + return "application/x-bcpio" + case "bdf": + return "application/x-font-bdf" + case "bdm": + return "application/vnd.syncml.dm+wbxml" + case "bed": + return "application/vnd.realvnc.bed" + case "bh2": + return "application/vnd.fujitsu.oasysprs" + case "bin": + return "application/octet-stream" + case "blb": + return "application/x-blorb" + case "blorb": + return "application/x-blorb" + case "bmi": + return "application/vnd.bmi" + case "bmp": + return "image/bmp" + case "body": + return "text/html" + case "book": + return "application/vnd.framemaker" + case "box": + return "application/vnd.previewsystems.box" + case "boz": + return "application/x-bzip2" + case "bpk": + return "application/octet-stream" + case "btif": + return "image/prs.btif" + case "bz": + return "application/x-bzip" + case "bz2": + return "application/x-bzip2" + case "c": + return "text/x-c" + case "c11amc": + return "application/vnd.cluetrust.cartomobile-config" + case "c11amz": + return "application/vnd.cluetrust.cartomobile-config-pkg" + case "c4d": + return "application/vnd.clonk.c4group" + case "c4f": + return "application/vnd.clonk.c4group" + case "c4g": + return "application/vnd.clonk.c4group" + case "c4p": + return "application/vnd.clonk.c4group" + case "c4u": + return "application/vnd.clonk.c4group" + case "cab": + return "application/vnd.ms-cab-compressed" + case "caf": + return "audio/x-caf" + case "cap": + return "application/vnd.tcpdump.pcap" + case "car": + return "application/vnd.curl.car" + case "cat": + return "application/vnd.ms-pki.seccat" + case "cb7": + return "application/x-cbr" + case "cba": + return "application/x-cbr" + case "cbr": + return "application/x-cbr" + case "cbt": + return "application/x-cbr" + case "cbz": + return "application/x-cbr" + case "cc": + return "text/x-c" + case "cct": + return "application/x-director" + case "ccxml": + return "application/ccxml+xml" + case "cdbcmsg": + return "application/vnd.contact.cmsg" + case "cdf": + return "application/x-cdf" + case "cdkey": + return "application/vnd.mediastation.cdkey" + case "cdmia": + return "application/cdmi-capability" + case "cdmic": + return "application/cdmi-container" + case "cdmid": + return "application/cdmi-domain" + case "cdmio": + return "application/cdmi-object" + case "cdmiq": + return "application/cdmi-queue" + case "cdx": + return "chemical/x-cdx" + case "cdxml": + return "application/vnd.chemdraw+xml" + case "cdy": + return "application/vnd.cinderella" + case "cer": + return "application/pkix-cert" + case "cfs": + return "application/x-cfs-compressed" + case "cgm": + return "image/cgm" + case "chat": + return "application/x-chat" + case "chm": + return "application/vnd.ms-htmlhelp" + case "chrt": + return "application/vnd.kde.kchart" + case "cif": + return "chemical/x-cif" + case "cii": + return "application/vnd.anser-web-certificate-issue-initiation" + case "cil": + return "application/vnd.ms-artgalry" + case "cla": + return "application/vnd.claymore" + case "class": + return "application/java" + case "clkk": + return "application/vnd.crick.clicker.keyboard" + case "clkp": + return "application/vnd.crick.clicker.palette" + case "clkt": + return "application/vnd.crick.clicker.template" + case "clkw": + return "application/vnd.crick.clicker.wordbank" + case "clkx": + return "application/vnd.crick.clicker" + case "clp": + return "application/x-msclip" + case "cmc": + return "application/vnd.cosmocaller" + case "cmdf": + return "chemical/x-cmdf" + case "cml": + return "chemical/x-cml" + case "cmp": + return "application/vnd.yellowriver-custom-menu" + case "cmx": + return "image/x-cmx" + case "cod": + return "application/vnd.rim.cod" + case "com": + return "application/x-msdownload" + case "conf": + return "text/plain" + case "cpio": + return "application/x-cpio" + case "cpp": + return "text/x-c" + case "cpt": + return "application/mac-compactpro" + case "crd": + return "application/x-mscardfile" + case "crl": + return "application/pkix-crl" + case "crt": + return "application/x-x509-ca-cert" + case "cryptonote": + return "application/vnd.rig.cryptonote" + case "csh": + return "application/x-csh" + case "csml": + return "chemical/x-csml" + case "csp": + return "application/vnd.commonspace" + case "css": + return "text/css" + case "cst": + return "application/x-director" + case "csv": + return "text/csv" + case "cu": + return "application/cu-seeme" + case "curl": + return "text/vnd.curl" + case "cww": + return "application/prs.cww" + case "cxt": + return "application/x-director" + case "cxx": + return "text/x-c" + case "dae": + return "model/vnd.collada+xml" + case "daf": + return "application/vnd.mobius.daf" + case "dart": + return "application/vnd.dart" + case "dataless": + return "application/vnd.fdsn.seed" + case "davmount": + return "application/davmount+xml" + case "dbk": + return "application/docbook+xml" + case "dcr": + return "application/x-director" + case "dcurl": + return "text/vnd.curl.dcurl" + case "dd2": + return "application/vnd.oma.dd2+xml" + case "ddd": + return "application/vnd.fujixerox.ddd" + case "deb": + return "application/x-debian-package" + case "def": + return "text/plain" + case "deploy": + return "application/octet-stream" + case "der": + return "application/x-x509-ca-cert" + case "dfac": + return "application/vnd.dreamfactory" + case "dgc": + return "application/x-dgc-compressed" + case "dib": + return "image/bmp" + case "dic": + return "text/x-c" + case "dir": + return "application/x-director" + case "dis": + return "application/vnd.mobius.dis" + case "dist": + return "application/octet-stream" + case "distz": + return "application/octet-stream" + case "djv": + return "image/vnd.djvu" + case "djvu": + return "image/vnd.djvu" + case "dll": + return "application/x-msdownload" + case "dmg": + return "application/x-apple-diskimage" + case "dmp": + return "application/vnd.tcpdump.pcap" + case "dms": + return "application/octet-stream" + case "dna": + return "application/vnd.dna" + case "doc": + return "application/msword" + case "docm": + return "application/vnd.ms-word.document.macroenabled.12" + case "docx": + return "application/vnd.openxmlformats-officedocument.wordprocessingml.document" + case "dot": + return "application/msword" + case "dotm": + return "application/vnd.ms-word.template.macroenabled.12" + case "dotx": + return "application/vnd.openxmlformats-officedocument.wordprocessingml.template" + case "dp": + return "application/vnd.osgi.dp" + case "dpg": + return "application/vnd.dpgraph" + case "dra": + return "audio/vnd.dra" + case "dsc": + return "text/prs.lines.tag" + case "dssc": + return "application/dssc+der" + case "dtb": + return "application/x-dtbook+xml" + case "dtd": + return "application/xml-dtd" + case "dts": + return "audio/vnd.dts" + case "dtshd": + return "audio/vnd.dts.hd" + case "dump": + return "application/octet-stream" + case "dv": + return "video/x-dv" + case "dvb": + return "video/vnd.dvb.file" + case "dvi": + return "application/x-dvi" + case "dwf": + return "model/vnd.dwf" + case "dwg": + return "image/vnd.dwg" + case "dxf": + return "image/vnd.dxf" + case "dxp": + return "application/vnd.spotfire.dxp" + case "dxr": + return "application/x-director" + case "ecelp4800": + return "audio/vnd.nuera.ecelp4800" + case "ecelp7470": + return "audio/vnd.nuera.ecelp7470" + case "ecelp9600": + return "audio/vnd.nuera.ecelp9600" + case "ecma": + return "application/ecmascript" + case "edm": + return "application/vnd.novadigm.edm" + case "edx": + return "application/vnd.novadigm.edx" + case "efif": + return "application/vnd.picsel" + case "ei6": + return "application/vnd.pg.osasli" + case "elc": + return "application/octet-stream" + case "emf": + return "application/x-msmetafile" + case "eml": + return "message/rfc822" + case "emma": + return "application/emma+xml" + case "emz": + return "application/x-msmetafile" + case "eol": + return "audio/vnd.digital-winds" + case "eot": + return "application/vnd.ms-fontobject" + case "eps": + return "application/postscript" + case "epub": + return "application/epub+zip" + case "es3": + return "application/vnd.eszigno3+xml" + case "esa": + return "application/vnd.osgi.subsystem" + case "esf": + return "application/vnd.epson.esf" + case "et3": + return "application/vnd.eszigno3+xml" + case "etx": + return "text/x-setext" + case "eva": + return "application/x-eva" + case "evy": + return "application/x-envoy" + case "exe": + return "application/octet-stream" + case "exi": + return "application/exi" + case "ext": + return "application/vnd.novadigm.ext" + case "ez": + return "application/andrew-inset" + case "ez2": + return "application/vnd.ezpix-album" + case "ez3": + return "application/vnd.ezpix-package" + case "f": + return "text/x-fortran" + case "f4v": + return "video/x-f4v" + case "f77": + return "text/x-fortran" + case "f90": + return "text/x-fortran" + case "fbs": + return "image/vnd.fastbidsheet" + case "fcdt": + return "application/vnd.adobe.formscentral.fcdt" + case "fcs": + return "application/vnd.isac.fcs" + case "fdf": + return "application/vnd.fdf" + case "fe_launch": + return "application/vnd.denovo.fcselayout-link" + case "fg5": + return "application/vnd.fujitsu.oasysgp" + case "fgd": + return "application/x-director" + case "fh": + return "image/x-freehand" + case "fh4": + return "image/x-freehand" + case "fh5": + return "image/x-freehand" + case "fh7": + return "image/x-freehand" + case "fhc": + return "image/x-freehand" + case "fig": + return "application/x-xfig" + case "flac": + return "audio/flac" + case "fli": + return "video/x-fli" + case "flo": + return "application/vnd.micrografx.flo" + case "flv": + return "video/x-flv" + case "flw": + return "application/vnd.kde.kivio" + case "flx": + return "text/vnd.fmi.flexstor" + case "fly": + return "text/vnd.fly" + case "fm": + return "application/vnd.framemaker" + case "fnc": + return "application/vnd.frogans.fnc" + case "for": + return "text/x-fortran" + case "fpx": + return "image/vnd.fpx" + case "frame": + return "application/vnd.framemaker" + case "fsc": + return "application/vnd.fsc.weblaunch" + case "fst": + return "image/vnd.fst" + case "ftc": + return "application/vnd.fluxtime.clip" + case "fti": + return "application/vnd.anser-web-funds-transfer-initiation" + case "fvt": + return "video/vnd.fvt" + case "fxp": + return "application/vnd.adobe.fxp" + case "fxpl": + return "application/vnd.adobe.fxp" + case "fzs": + return "application/vnd.fuzzysheet" + case "g2w": + return "application/vnd.geoplan" + case "g3": + return "image/g3fax" + case "g3w": + return "application/vnd.geospace" + case "gac": + return "application/vnd.groove-account" + case "gam": + return "application/x-tads" + case "gbr": + return "application/rpki-ghostbusters" + case "gca": + return "application/x-gca-compressed" + case "gdl": + return "model/vnd.gdl" + case "geo": + return "application/vnd.dynageo" + case "gex": + return "application/vnd.geometry-explorer" + case "ggb": + return "application/vnd.geogebra.file" + case "ggt": + return "application/vnd.geogebra.tool" + case "ghf": + return "application/vnd.groove-help" + case "gif": + return "image/gif" + case "gim": + return "application/vnd.groove-identity-message" + case "gml": + return "application/gml+xml" + case "gmx": + return "application/vnd.gmx" + case "gnumeric": + return "application/x-gnumeric" + case "gph": + return "application/vnd.flographit" + case "gpx": + return "application/gpx+xml" + case "gqf": + return "application/vnd.grafeq" + case "gqs": + return "application/vnd.grafeq" + case "gram": + return "application/srgs" + case "gramps": + return "application/x-gramps-xml" + case "gre": + return "application/vnd.geometry-explorer" + case "grv": + return "application/vnd.groove-injector" + case "grxml": + return "application/srgs+xml" + case "gsf": + return "application/x-font-ghostscript" + case "gtar": + return "application/x-gtar" + case "gtm": + return "application/vnd.groove-tool-message" + case "gtw": + return "model/vnd.gtw" + case "gv": + return "text/vnd.graphviz" + case "gxf": + return "application/gxf" + case "gxt": + return "application/vnd.geonext" + case "gz": + return "application/x-gzip" + case "h": + return "text/x-c" + case "h261": + return "video/h261" + case "h263": + return "video/h263" + case "h264": + return "video/h264" + case "hal": + return "application/vnd.hal+xml" + case "hbci": + return "application/vnd.hbci" + case "hdf": + return "application/x-hdf" + case "hh": + return "text/x-c" + case "hlp": + return "application/winhlp" + case "hpgl": + return "application/vnd.hp-hpgl" + case "hpid": + return "application/vnd.hp-hpid" + case "hps": + return "application/vnd.hp-hps" + case "hqx": + return "application/mac-binhex40" + case "htc": + return "text/x-component" + case "htke": + return "application/vnd.kenameaapp" + case "htm": + return "text/html" + case "html": + return "text/html" + case "hvd": + return "application/vnd.yamaha.hv-dic" + case "hvp": + return "application/vnd.yamaha.hv-voice" + case "hvs": + return "application/vnd.yamaha.hv-script" + case "i2g": + return "application/vnd.intergeo" + case "icc": + return "application/vnd.iccprofile" + case "ice": + return "x-conference/x-cooltalk" + case "icm": + return "application/vnd.iccprofile" + case "ico": + return "image/x-icon" + case "ics": + return "text/calendar" + case "ief": + return "image/ief" + case "ifb": + return "text/calendar" + case "ifm": + return "application/vnd.shana.informed.formdata" + case "iges": + return "model/iges" + case "igl": + return "application/vnd.igloader" + case "igm": + return "application/vnd.insors.igm" + case "igs": + return "model/iges" + case "igx": + return "application/vnd.micrografx.igx" + case "iif": + return "application/vnd.shana.informed.interchange" + case "imp": + return "application/vnd.accpac.simply.imp" + case "ims": + return "application/vnd.ms-ims" + case "in": + return "text/plain" + case "ink": + return "application/inkml+xml" + case "inkml": + return "application/inkml+xml" + case "install": + return "application/x-install-instructions" + case "iota": + return "application/vnd.astraea-software.iota" + case "ipfix": + return "application/ipfix" + case "ipk": + return "application/vnd.shana.informed.package" + case "irm": + return "application/vnd.ibm.rights-management" + case "irp": + return "application/vnd.irepository.package+xml" + case "iso": + return "application/x-iso9660-image" + case "itp": + return "application/vnd.shana.informed.formtemplate" + case "ivp": + return "application/vnd.immervision-ivp" + case "ivu": + return "application/vnd.immervision-ivu" + case "jad": + return "text/vnd.sun.j2me.app-descriptor" + case "jam": + return "application/vnd.jam" + case "jar": + return "application/java-archive" + case "java": + return "text/x-java-source" + case "jisp": + return "application/vnd.jisp" + case "jlt": + return "application/vnd.hp-jlyt" + case "jnlp": + return "application/x-java-jnlp-file" + case "joda": + return "application/vnd.joost.joda-archive" + case "jpe": + return "image/jpeg" + case "jpeg": + return "image/jpeg" + case "jpg": + return "image/jpeg" + case "jpgm": + return "video/jpm" + case "jpgv": + return "video/jpeg" + case "jpm": + return "video/jpm" + case "js": + return "application/javascript" + case "jsf": + return "text/plain" + case "json": + return "application/json" + case "jsonml": + return "application/jsonml+json" + case "jspf": + return "text/plain" + case "kar": + return "audio/midi" + case "karbon": + return "application/vnd.kde.karbon" + case "kfo": + return "application/vnd.kde.kformula" + case "kia": + return "application/vnd.kidspiration" + case "kml": + return "application/vnd.google-earth.kml+xml" + case "kmz": + return "application/vnd.google-earth.kmz" + case "kne": + return "application/vnd.kinar" + case "knp": + return "application/vnd.kinar" + case "kon": + return "application/vnd.kde.kontour" + case "kpr": + return "application/vnd.kde.kpresenter" + case "kpt": + return "application/vnd.kde.kpresenter" + case "kpxx": + return "application/vnd.ds-keypoint" + case "ksp": + return "application/vnd.kde.kspread" + case "ktr": + return "application/vnd.kahootz" + case "ktx": + return "image/ktx" + case "ktz": + return "application/vnd.kahootz" + case "kwd": + return "application/vnd.kde.kword" + case "kwt": + return "application/vnd.kde.kword" + case "lasxml": + return "application/vnd.las.las+xml" + case "latex": + return "application/x-latex" + case "lbd": + return "application/vnd.llamagraphics.life-balance.desktop" + case "lbe": + return "application/vnd.llamagraphics.life-balance.exchange+xml" + case "les": + return "application/vnd.hhe.lesson-player" + case "lha": + return "application/x-lzh-compressed" + case "link66": + return "application/vnd.route66.link66+xml" + case "list": + return "text/plain" + case "list3820": + return "application/vnd.ibm.modcap" + case "listafp": + return "application/vnd.ibm.modcap" + case "lnk": + return "application/x-ms-shortcut" + case "log": + return "text/plain" + case "lostxml": + return "application/lost+xml" + case "lrf": + return "application/octet-stream" + case "lrm": + return "application/vnd.ms-lrm" + case "ltf": + return "application/vnd.frogans.ltf" + case "lvp": + return "audio/vnd.lucent.voice" + case "lwp": + return "application/vnd.lotus-wordpro" + case "lzh": + return "application/x-lzh-compressed" + case "m13": + return "application/x-msmediaview" + case "m14": + return "application/x-msmediaview" + case "m1v": + return "video/mpeg" + case "m21": + return "application/mp21" + case "m2a": + return "audio/mpeg" + case "m2v": + return "video/mpeg" + case "m3a": + return "audio/mpeg" + case "m3u": + return "audio/x-mpegurl" + case "m3u8": + return "application/vnd.apple.mpegurl" + case "m4a": + return "audio/mp4" + case "m4b": + return "audio/mp4" + case "m4r": + return "audio/mp4" + case "m4u": + return "video/vnd.mpegurl" + case "m4v": + return "video/mp4" + case "ma": + return "application/mathematica" + case "mac": + return "image/x-macpaint" + case "mads": + return "application/mads+xml" + case "mag": + return "application/vnd.ecowin.chart" + case "maker": + return "application/vnd.framemaker" + case "man": + return "text/troff" + case "mar": + return "application/octet-stream" + case "mathml": + return "application/mathml+xml" + case "mb": + return "application/mathematica" + case "mbk": + return "application/vnd.mobius.mbk" + case "mbox": + return "application/mbox" + case "mc1": + return "application/vnd.medcalcdata" + case "mcd": + return "application/vnd.mcd" + case "mcurl": + return "text/vnd.curl.mcurl" + case "mdb": + return "application/x-msaccess" + case "mdi": + return "image/vnd.ms-modi" + case "me": + return "text/troff" + case "mesh": + return "model/mesh" + case "meta4": + return "application/metalink4+xml" + case "metalink": + return "application/metalink+xml" + case "mets": + return "application/mets+xml" + case "mfm": + return "application/vnd.mfmp" + case "mft": + return "application/rpki-manifest" + case "mgp": + return "application/vnd.osgeo.mapguide.package" + case "mgz": + return "application/vnd.proteus.magazine" + case "mid": + return "audio/midi" + case "midi": + return "audio/midi" + case "mie": + return "application/x-mie" + case "mif": + return "application/x-mif" + case "mime": + return "message/rfc822" + case "mj2": + return "video/mj2" + case "mjp2": + return "video/mj2" + case "mk3d": + return "video/x-matroska" + case "mka": + return "audio/x-matroska" + case "mks": + return "video/x-matroska" + case "mkv": + return "video/x-matroska" + case "mlp": + return "application/vnd.dolby.mlp" + case "mmd": + return "application/vnd.chipnuts.karaoke-mmd" + case "mmf": + return "application/vnd.smaf" + case "mmr": + return "image/vnd.fujixerox.edmics-mmr" + case "mng": + return "video/x-mng" + case "mny": + return "application/x-msmoney" + case "mobi": + return "application/x-mobipocket-ebook" + case "mods": + return "application/mods+xml" + case "mov": + return "video/quicktime" + case "movie": + return "video/x-sgi-movie" + case "mp1": + return "audio/mpeg" + case "mp2": + return "audio/mpeg" + case "mp21": + return "application/mp21" + case "mp2a": + return "audio/mpeg" + case "mp3": + return "audio/mpeg" + case "mp4": + return "video/mp4" + case "mp4a": + return "audio/mp4" + case "mp4s": + return "application/mp4" + case "mp4v": + return "video/mp4" + case "mpa": + return "audio/mpeg" + case "mpc": + return "application/vnd.mophun.certificate" + case "mpe": + return "video/mpeg" + case "mpeg": + return "video/mpeg" + case "mpega": + return "audio/x-mpeg" + case "mpg": + return "video/mpeg" + case "mpg4": + return "video/mp4" + case "mpga": + return "audio/mpeg" + case "mpkg": + return "application/vnd.apple.installer+xml" + case "mpm": + return "application/vnd.blueice.multipass" + case "mpn": + return "application/vnd.mophun.application" + case "mpp": + return "application/vnd.ms-project" + case "mpt": + return "application/vnd.ms-project" + case "mpv2": + return "video/mpeg2" + case "mpy": + return "application/vnd.ibm.minipay" + case "mqy": + return "application/vnd.mobius.mqy" + case "mrc": + return "application/marc" + case "mrcx": + return "application/marcxml+xml" + case "ms": + return "text/troff" + case "mscml": + return "application/mediaservercontrol+xml" + case "mseed": + return "application/vnd.fdsn.mseed" + case "mseq": + return "application/vnd.mseq" + case "msf": + return "application/vnd.epson.msf" + case "msh": + return "model/mesh" + case "msi": + return "application/x-msdownload" + case "msl": + return "application/vnd.mobius.msl" + case "msty": + return "application/vnd.muvee.style" + case "mts": + return "model/vnd.mts" + case "mus": + return "application/vnd.musician" + case "musicxml": + return "application/vnd.recordare.musicxml+xml" + case "mvb": + return "application/x-msmediaview" + case "mwf": + return "application/vnd.mfer" + case "mxf": + return "application/mxf" + case "mxl": + return "application/vnd.recordare.musicxml" + case "mxml": + return "application/xv+xml" + case "mxs": + return "application/vnd.triscape.mxs" + case "mxu": + return "video/vnd.mpegurl" + case "n-gage": + return "application/vnd.nokia.n-gage.symbian.install" + case "n3": + return "text/n3" + case "nb": + return "application/mathematica" + case "nbp": + return "application/vnd.wolfram.player" + case "nc": + return "application/x-netcdf" + case "ncx": + return "application/x-dtbncx+xml" + case "nfo": + return "text/x-nfo" + case "ngdat": + return "application/vnd.nokia.n-gage.data" + case "nitf": + return "application/vnd.nitf" + case "nlu": + return "application/vnd.neurolanguage.nlu" + case "nml": + return "application/vnd.enliven" + case "nnd": + return "application/vnd.noblenet-directory" + case "nns": + return "application/vnd.noblenet-sealer" + case "nnw": + return "application/vnd.noblenet-web" + case "npx": + return "image/vnd.net-fpx" + case "nsc": + return "application/x-conference" + case "nsf": + return "application/vnd.lotus-notes" + case "ntf": + return "application/vnd.nitf" + case "nzb": + return "application/x-nzb" + case "oa2": + return "application/vnd.fujitsu.oasys2" + case "oa3": + return "application/vnd.fujitsu.oasys3" + case "oas": + return "application/vnd.fujitsu.oasys" + case "obd": + return "application/x-msbinder" + case "obj": + return "application/x-tgif" + case "oda": + return "application/oda" + case "odb": + return "application/vnd.oasis.opendocument.database" + case "odc": + return "application/vnd.oasis.opendocument.chart" + case "odf": + return "application/vnd.oasis.opendocument.formula" + case "odft": + return "application/vnd.oasis.opendocument.formula-template" + case "odg": + return "application/vnd.oasis.opendocument.graphics" + case "odi": + return "application/vnd.oasis.opendocument.image" + case "odm": + return "application/vnd.oasis.opendocument.text-master" + case "odp": + return "application/vnd.oasis.opendocument.presentation" + case "ods": + return "application/vnd.oasis.opendocument.spreadsheet" + case "odt": + return "application/vnd.oasis.opendocument.text" + case "oga": + return "audio/ogg" + case "ogg": + return "audio/ogg" + case "ogv": + return "video/ogg" + case "ogx": + return "application/ogg" + case "omdoc": + return "application/omdoc+xml" + case "onepkg": + return "application/onenote" + case "onetmp": + return "application/onenote" + case "onetoc": + return "application/onenote" + case "onetoc2": + return "application/onenote" + case "opf": + return "application/oebps-package+xml" + case "opml": + return "text/x-opml" + case "oprc": + return "application/vnd.palm" + case "org": + return "application/vnd.lotus-organizer" + case "osf": + return "application/vnd.yamaha.openscoreformat" + case "osfpvg": + return "application/vnd.yamaha.openscoreformat.osfpvg+xml" + case "otc": + return "application/vnd.oasis.opendocument.chart-template" + case "otf": + return "application/x-font-otf" + case "otg": + return "application/vnd.oasis.opendocument.graphics-template" + case "oth": + return "application/vnd.oasis.opendocument.text-web" + case "oti": + return "application/vnd.oasis.opendocument.image-template" + case "otp": + return "application/vnd.oasis.opendocument.presentation-template" + case "ots": + return "application/vnd.oasis.opendocument.spreadsheet-template" + case "ott": + return "application/vnd.oasis.opendocument.text-template" + case "oxps": + return "application/oxps" + case "oxt": + return "application/vnd.openofficeorg.extension" + case "p": + return "text/x-pascal" + case "p10": + return "application/pkcs10" + case "p12": + return "application/x-pkcs12" + case "p7b": + return "application/x-pkcs7-certificates" + case "p7c": + return "application/pkcs7-mime" + case "p7m": + return "application/pkcs7-mime" + case "p7r": + return "application/x-pkcs7-certreqresp" + case "p7s": + return "application/pkcs7-signature" + case "p8": + return "application/pkcs8" + case "pas": + return "text/x-pascal" + case "paw": + return "application/vnd.pawaafile" + case "pbd": + return "application/vnd.powerbuilder6" + case "pbm": + return "image/x-portable-bitmap" + case "pcap": + return "application/vnd.tcpdump.pcap" + case "pcf": + return "application/x-font-pcf" + case "pcl": + return "application/vnd.hp-pcl" + case "pclxl": + return "application/vnd.hp-pclxl" + case "pct": + return "image/pict" + case "pcurl": + return "application/vnd.curl.pcurl" + case "pcx": + return "image/x-pcx" + case "pdb": + return "application/vnd.palm" + case "pdf": + return "application/pdf" + case "pfa": + return "application/x-font-type1" + case "pfb": + return "application/x-font-type1" + case "pfm": + return "application/x-font-type1" + case "pfr": + return "application/font-tdpfr" + case "pfx": + return "application/x-pkcs12" + case "pgm": + return "image/x-portable-graymap" + case "pgn": + return "application/x-chess-pgn" + case "pgp": + return "application/pgp-encrypted" + case "pic": + return "image/pict" + case "pict": + return "image/pict" + case "pkg": + return "application/octet-stream" + case "pki": + return "application/pkixcmp" + case "pkipath": + return "application/pkix-pkipath" + case "plb": + return "application/vnd.3gpp.pic-bw-large" + case "plc": + return "application/vnd.mobius.plc" + case "plf": + return "application/vnd.pocketlearn" + case "pls": + return "audio/x-scpls" + case "pml": + return "application/vnd.ctc-posml" + case "png": + return "image/png" + case "pnm": + return "image/x-portable-anymap" + case "pnt": + return "image/x-macpaint" + case "portpkg": + return "application/vnd.macports.portpkg" + case "pot": + return "application/vnd.ms-powerpoint" + case "potm": + return "application/vnd.ms-powerpoint.template.macroenabled.12" + case "potx": + return "application/vnd.openxmlformats-officedocument.presentationml.template" + case "ppam": + return "application/vnd.ms-powerpoint.addin.macroenabled.12" + case "ppd": + return "application/vnd.cups-ppd" + case "ppm": + return "image/x-portable-pixmap" + case "pps": + return "application/vnd.ms-powerpoint" + case "ppsm": + return "application/vnd.ms-powerpoint.slideshow.macroenabled.12" + case "ppsx": + return "application/vnd.openxmlformats-officedocument.presentationml.slideshow" + case "ppt": + return "application/vnd.ms-powerpoint" + case "pptm": + return "application/vnd.ms-powerpoint.presentation.macroenabled.12" + case "pptx": + return "application/vnd.openxmlformats-officedocument.presentationml.presentation" + case "pqa": + return "application/vnd.palm" + case "prc": + return "application/x-mobipocket-ebook" + case "pre": + return "application/vnd.lotus-freelance" + case "prf": + return "application/pics-rules" + case "ps": + return "application/postscript" + case "psb": + return "application/vnd.3gpp.pic-bw-small" + case "psd": + return "image/vnd.adobe.photoshop" + case "psf": + return "application/x-font-linux-psf" + case "pskcxml": + return "application/pskc+xml" + case "ptid": + return "application/vnd.pvi.ptid1" + case "pub": + return "application/x-mspublisher" + case "pvb": + return "application/vnd.3gpp.pic-bw-var" + case "pwn": + return "application/vnd.3m.post-it-notes" + case "pya": + return "audio/vnd.ms-playready.media.pya" + case "pyv": + return "video/vnd.ms-playready.media.pyv" + case "qam": + return "application/vnd.epson.quickanime" + case "qbo": + return "application/vnd.intu.qbo" + case "qfx": + return "application/vnd.intu.qfx" + case "qps": + return "application/vnd.publishare-delta-tree" + case "qt": + return "video/quicktime" + case "qti": + return "image/x-quicktime" + case "qtif": + return "image/x-quicktime" + case "qwd": + return "application/vnd.quark.quarkxpress" + case "qwt": + return "application/vnd.quark.quarkxpress" + case "qxb": + return "application/vnd.quark.quarkxpress" + case "qxd": + return "application/vnd.quark.quarkxpress" + case "qxl": + return "application/vnd.quark.quarkxpress" + case "qxt": + return "application/vnd.quark.quarkxpress" + case "ra": + return "audio/x-pn-realaudio" + case "ram": + return "audio/x-pn-realaudio" + case "rar": + return "application/x-rar-compressed" + case "ras": + return "image/x-cmu-raster" + case "rcprofile": + return "application/vnd.ipunplugged.rcprofile" + case "rdf": + return "application/rdf+xml" + case "rdz": + return "application/vnd.data-vision.rdz" + case "rep": + return "application/vnd.businessobjects" + case "res": + return "application/x-dtbresource+xml" + case "rgb": + return "image/x-rgb" + case "rif": + return "application/reginfo+xml" + case "rip": + return "audio/vnd.rip" + case "ris": + return "application/x-research-info-systems" + case "rl": + return "application/resource-lists+xml" + case "rlc": + return "image/vnd.fujixerox.edmics-rlc" + case "rld": + return "application/resource-lists-diff+xml" + case "rm": + return "application/vnd.rn-realmedia" + case "rmi": + return "audio/midi" + case "rmp": + return "audio/x-pn-realaudio-plugin" + case "rms": + return "application/vnd.jcp.javame.midlet-rms" + case "rmvb": + return "application/vnd.rn-realmedia-vbr" + case "rnc": + return "application/relax-ng-compact-syntax" + case "roa": + return "application/rpki-roa" + case "roff": + return "text/troff" + case "rp9": + return "application/vnd.cloanto.rp9" + case "rpss": + return "application/vnd.nokia.radio-presets" + case "rpst": + return "application/vnd.nokia.radio-preset" + case "rq": + return "application/sparql-query" + case "rs": + return "application/rls-services+xml" + case "rsd": + return "application/rsd+xml" + case "rss": + return "application/rss+xml" + case "rtf": + return "application/rtf" + case "rtx": + return "text/richtext" + case "s": + return "text/x-asm" + case "s3m": + return "audio/s3m" + case "saf": + return "application/vnd.yamaha.smaf-audio" + case "sbml": + return "application/sbml+xml" + case "sc": + return "application/vnd.ibm.secure-container" + case "scd": + return "application/x-msschedule" + case "scm": + return "application/vnd.lotus-screencam" + case "scq": + return "application/scvp-cv-request" + case "scs": + return "application/scvp-cv-response" + case "scurl": + return "text/vnd.curl.scurl" + case "sda": + return "application/vnd.stardivision.draw" + case "sdc": + return "application/vnd.stardivision.calc" + case "sdd": + return "application/vnd.stardivision.impress" + case "sdkd": + return "application/vnd.solent.sdkm+xml" + case "sdkm": + return "application/vnd.solent.sdkm+xml" + case "sdp": + return "application/sdp" + case "sdw": + return "application/vnd.stardivision.writer" + case "see": + return "application/vnd.seemail" + case "seed": + return "application/vnd.fdsn.seed" + case "sema": + return "application/vnd.sema" + case "semd": + return "application/vnd.semd" + case "semf": + return "application/vnd.semf" + case "ser": + return "application/java-serialized-object" + case "setpay": + return "application/set-payment-initiation" + case "setreg": + return "application/set-registration-initiation" + case "sfd-hdstx": + return "application/vnd.hydrostatix.sof-data" + case "sfs": + return "application/vnd.spotfire.sfs" + case "sfv": + return "text/x-sfv" + case "sgi": + return "image/sgi" + case "sgl": + return "application/vnd.stardivision.writer-global" + case "sgm": + return "text/sgml" + case "sgml": + return "text/sgml" + case "sh": + return "application/x-sh" + case "shar": + return "application/x-shar" + case "shf": + return "application/shf+xml" + case "sid": + return "image/x-mrsid-image" + case "sig": + return "application/pgp-signature" + case "sil": + return "audio/silk" + case "silo": + return "model/mesh" + case "sis": + return "application/vnd.symbian.install" + case "sisx": + return "application/vnd.symbian.install" + case "sit": + return "application/x-stuffit" + case "sitx": + return "application/x-stuffitx" + case "skd": + return "application/vnd.koan" + case "skm": + return "application/vnd.koan" + case "skp": + return "application/vnd.koan" + case "skt": + return "application/vnd.koan" + case "sldm": + return "application/vnd.ms-powerpoint.slide.macroenabled.12" + case "sldx": + return "application/vnd.openxmlformats-officedocument.presentationml.slide" + case "slt": + return "application/vnd.epson.salt" + case "sm": + return "application/vnd.stepmania.stepchart" + case "smf": + return "application/vnd.stardivision.math" + case "smi": + return "application/smil+xml" + case "smil": + return "application/smil+xml" + case "smv": + return "video/x-smv" + case "smzip": + return "application/vnd.stepmania.package" + case "snd": + return "audio/basic" + case "snf": + return "application/x-font-snf" + case "so": + return "application/octet-stream" + case "spc": + return "application/x-pkcs7-certificates" + case "spf": + return "application/vnd.yamaha.smaf-phrase" + case "spl": + return "application/x-futuresplash" + case "spot": + return "text/vnd.in3d.spot" + case "spp": + return "application/scvp-vp-response" + case "spq": + return "application/scvp-vp-request" + case "spx": + return "audio/ogg" + case "sql": + return "application/x-sql" + case "src": + return "application/x-wais-source" + case "srt": + return "application/x-subrip" + case "sru": + return "application/sru+xml" + case "srx": + return "application/sparql-results+xml" + case "ssdl": + return "application/ssdl+xml" + case "sse": + return "application/vnd.kodak-descriptor" + case "ssf": + return "application/vnd.epson.ssf" + case "ssml": + return "application/ssml+xml" + case "st": + return "application/vnd.sailingtracker.track" + case "stc": + return "application/vnd.sun.xml.calc.template" + case "std": + return "application/vnd.sun.xml.draw.template" + case "stf": + return "application/vnd.wt.stf" + case "sti": + return "application/vnd.sun.xml.impress.template" + case "stk": + return "application/hyperstudio" + case "stl": + return "application/vnd.ms-pki.stl" + case "str": + return "application/vnd.pg.format" + case "stw": + return "application/vnd.sun.xml.writer.template" + case "sub": + return "text/vnd.dvb.subtitle" + case "sus": + return "application/vnd.sus-calendar" + case "susp": + return "application/vnd.sus-calendar" + case "sv4cpio": + return "application/x-sv4cpio" + case "sv4crc": + return "application/x-sv4crc" + case "svc": + return "application/vnd.dvb.service" + case "svd": + return "application/vnd.svd" + case "svg": + return "image/svg+xml" + case "svgz": + return "image/svg+xml" + case "swa": + return "application/x-director" + case "swf": + return "application/x-shockwave-flash" + case "swi": + return "application/vnd.aristanetworks.swi" + case "sxc": + return "application/vnd.sun.xml.calc" + case "sxd": + return "application/vnd.sun.xml.draw" + case "sxg": + return "application/vnd.sun.xml.writer.global" + case "sxi": + return "application/vnd.sun.xml.impress" + case "sxm": + return "application/vnd.sun.xml.math" + case "sxw": + return "application/vnd.sun.xml.writer" + case "t": + return "text/troff" + case "t3": + return "application/x-t3vm-image" + case "taglet": + return "application/vnd.mynfc" + case "tao": + return "application/vnd.tao.intent-module-archive" + case "tar": + return "application/x-tar" + case "tcap": + return "application/vnd.3gpp2.tcap" + case "tcl": + return "application/x-tcl" + case "teacher": + return "application/vnd.smart.teacher" + case "tei": + return "application/tei+xml" + case "teicorpus": + return "application/tei+xml" + case "tex": + return "application/x-tex" + case "texi": + return "application/x-texinfo" + case "texinfo": + return "application/x-texinfo" + case "text": + return "text/plain" + case "tfi": + return "application/thraud+xml" + case "tfm": + return "application/x-tex-tfm" + case "tga": + return "image/x-tga" + case "thmx": + return "application/vnd.ms-officetheme" + case "tif": + return "image/tiff" + case "tiff": + return "image/tiff" + case "tmo": + return "application/vnd.tmobile-livetv" + case "torrent": + return "application/x-bittorrent" + case "tpl": + return "application/vnd.groove-tool-template" + case "tpt": + return "application/vnd.trid.tpt" + case "tr": + return "text/troff" + case "tra": + return "application/vnd.trueapp" + case "trm": + return "application/x-msterminal" + case "tsd": + return "application/timestamped-data" + case "tsv": + return "text/tab-separated-values" + case "ttc": + return "application/x-font-ttf" + case "ttf": + return "application/x-font-ttf" + case "ttl": + return "text/turtle" + case "twd": + return "application/vnd.simtech-mindmapper" + case "twds": + return "application/vnd.simtech-mindmapper" + case "txd": + return "application/vnd.genomatix.tuxedo" + case "txf": + return "application/vnd.mobius.txf" + case "txt": + return "text/plain" + case "u32": + return "application/x-authorware-bin" + case "udeb": + return "application/x-debian-package" + case "ufd": + return "application/vnd.ufdl" + case "ufdl": + return "application/vnd.ufdl" + case "ulw": + return "audio/basic" + case "ulx": + return "application/x-glulx" + case "umj": + return "application/vnd.umajin" + case "unityweb": + return "application/vnd.unity" + case "uoml": + return "application/vnd.uoml+xml" + case "uri": + return "text/uri-list" + case "uris": + return "text/uri-list" + case "urls": + return "text/uri-list" + case "ustar": + return "application/x-ustar" + case "utz": + return "application/vnd.uiq.theme" + case "uu": + return "text/x-uuencode" + case "uva": + return "audio/vnd.dece.audio" + case "uvd": + return "application/vnd.dece.data" + case "uvf": + return "application/vnd.dece.data" + case "uvg": + return "image/vnd.dece.graphic" + case "uvh": + return "video/vnd.dece.hd" + case "uvi": + return "image/vnd.dece.graphic" + case "uvm": + return "video/vnd.dece.mobile" + case "uvp": + return "video/vnd.dece.pd" + case "uvs": + return "video/vnd.dece.sd" + case "uvt": + return "application/vnd.dece.ttml+xml" + case "uvu": + return "video/vnd.uvvu.mp4" + case "uvv": + return "video/vnd.dece.video" + case "uvva": + return "audio/vnd.dece.audio" + case "uvvd": + return "application/vnd.dece.data" + case "uvvf": + return "application/vnd.dece.data" + case "uvvg": + return "image/vnd.dece.graphic" + case "uvvh": + return "video/vnd.dece.hd" + case "uvvi": + return "image/vnd.dece.graphic" + case "uvvm": + return "video/vnd.dece.mobile" + case "uvvp": + return "video/vnd.dece.pd" + case "uvvs": + return "video/vnd.dece.sd" + case "uvvt": + return "application/vnd.dece.ttml+xml" + case "uvvu": + return "video/vnd.uvvu.mp4" + case "uvvv": + return "video/vnd.dece.video" + case "uvvx": + return "application/vnd.dece.unspecified" + case "uvvz": + return "application/vnd.dece.zip" + case "uvx": + return "application/vnd.dece.unspecified" + case "uvz": + return "application/vnd.dece.zip" + case "vcard": + return "text/vcard" + case "vcd": + return "application/x-cdlink" + case "vcf": + return "text/x-vcard" + case "vcg": + return "application/vnd.groove-vcard" + case "vcs": + return "text/x-vcalendar" + case "vcx": + return "application/vnd.vcx" + case "vis": + return "application/vnd.visionary" + case "viv": + return "video/vnd.vivo" + case "vob": + return "video/x-ms-vob" + case "vor": + return "application/vnd.stardivision.writer" + case "vox": + return "application/x-authorware-bin" + case "vrml": + return "model/vrml" + case "vsd": + return "application/vnd.visio" + case "vsf": + return "application/vnd.vsf" + case "vss": + return "application/vnd.visio" + case "vst": + return "application/vnd.visio" + case "vsw": + return "application/vnd.visio" + case "vtu": + return "model/vnd.vtu" + case "vxml": + return "application/voicexml+xml" + case "w3d": + return "application/x-director" + case "wad": + return "application/x-doom" + case "wav": + return "audio/x-wav" + case "wax": + return "audio/x-ms-wax" + case "wbmp": + return "image/vnd.wap.wbmp" + case "wbs": + return "application/vnd.criticaltools.wbs+xml" + case "wbxml": + return "application/vnd.wap.wbxml" + case "wcm": + return "application/vnd.ms-works" + case "wdb": + return "application/vnd.ms-works" + case "wdp": + return "image/vnd.ms-photo" + case "weba": + return "audio/webm" + case "webm": + return "video/webm" + case "webp": + return "image/webp" + case "wg": + return "application/vnd.pmi.widget" + case "wgt": + return "application/widget" + case "wks": + return "application/vnd.ms-works" + case "wm": + return "video/x-ms-wm" + case "wma": + return "audio/x-ms-wma" + case "wmd": + return "application/x-ms-wmd" + case "wmf": + return "application/x-msmetafile" + case "wml": + return "text/vnd.wap.wml" + case "wmlc": + return "application/vnd.wap.wmlc" + case "wmls": + return "text/vnd.wap.wmlscript" + case "wmlsc": + return "application/vnd.wap.wmlscriptc" + case "wmv": + return "video/x-ms-wmv" + case "wmx": + return "video/x-ms-wmx" + case "wmz": + return "application/x-msmetafile" + case "woff": + return "application/x-font-woff" + case "wpd": + return "application/vnd.wordperfect" + case "wpl": + return "application/vnd.ms-wpl" + case "wps": + return "application/vnd.ms-works" + case "wqd": + return "application/vnd.wqd" + case "wri": + return "application/x-mswrite" + case "wrl": + return "model/vrml" + case "wsdl": + return "application/wsdl+xml" + case "wspolicy": + return "application/wspolicy+xml" + case "wtb": + return "application/vnd.webturbo" + case "wvx": + return "video/x-ms-wvx" + case "x32": + return "application/x-authorware-bin" + case "x3d": + return "model/x3d+xml" + case "x3db": + return "model/x3d+binary" + case "x3dbz": + return "model/x3d+binary" + case "x3dv": + return "model/x3d+vrml" + case "x3dvz": + return "model/x3d+vrml" + case "x3dz": + return "model/x3d+xml" + case "xaml": + return "application/xaml+xml" + case "xap": + return "application/x-silverlight-app" + case "xar": + return "application/vnd.xara" + case "xbap": + return "application/x-ms-xbap" + case "xbd": + return "application/vnd.fujixerox.docuworks.binder" + case "xbm": + return "image/x-xbitmap" + case "xdf": + return "application/xcap-diff+xml" + case "xdm": + return "application/vnd.syncml.dm+xml" + case "xdp": + return "application/vnd.adobe.xdp+xml" + case "xdssc": + return "application/dssc+xml" + case "xdw": + return "application/vnd.fujixerox.docuworks" + case "xenc": + return "application/xenc+xml" + case "xer": + return "application/patch-ops-error+xml" + case "xfdf": + return "application/vnd.adobe.xfdf" + case "xfdl": + return "application/vnd.xfdl" + case "xht": + return "application/xhtml+xml" + case "xhtml": + return "application/xhtml+xml" + case "xhvml": + return "application/xv+xml" + case "xif": + return "image/vnd.xiff" + case "xla": + return "application/vnd.ms-excel" + case "xlam": + return "application/vnd.ms-excel.addin.macroenabled.12" + case "xlc": + return "application/vnd.ms-excel" + case "xlf": + return "application/x-xliff+xml" + case "xlm": + return "application/vnd.ms-excel" + case "xls": + return "application/vnd.ms-excel" + case "xlsb": + return "application/vnd.ms-excel.sheet.binary.macroenabled.12" + case "xlsm": + return "application/vnd.ms-excel.sheet.macroenabled.12" + case "xlsx": + return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + case "xlt": + return "application/vnd.ms-excel" + case "xltm": + return "application/vnd.ms-excel.template.macroenabled.12" + case "xltx": + return "application/vnd.openxmlformats-officedocument.spreadsheetml.template" + case "xlw": + return "application/vnd.ms-excel" + case "xm": + return "audio/xm" + case "xml": + return "application/xml" + case "xo": + return "application/vnd.olpc-sugar" + case "xop": + return "application/xop+xml" + case "xpi": + return "application/x-xpinstall" + case "xpl": + return "application/xproc+xml" + case "xpm": + return "image/x-xpixmap" + case "xpr": + return "application/vnd.is-xpr" + case "xps": + return "application/vnd.ms-xpsdocument" + case "xpw": + return "application/vnd.intercon.formnet" + case "xpx": + return "application/vnd.intercon.formnet" + case "xsl": + return "application/xml" + case "xslt": + return "application/xslt+xml" + case "xsm": + return "application/vnd.syncml+xml" + case "xspf": + return "application/xspf+xml" + case "xul": + return "application/vnd.mozilla.xul+xml" + case "xvm": + return "application/xv+xml" + case "xvml": + return "application/xv+xml" + case "xwd": + return "image/x-xwindowdump" + case "xyz": + return "chemical/x-xyz" + case "xz": + return "application/x-xz" + case "yang": + return "application/yang" + case "yin": + return "application/yin+xml" + case "z": + return "application/x-compress" + case "Z": + return "application/x-compress" + case "z1": + return "application/x-zmachine" + case "z2": + return "application/x-zmachine" + case "z3": + return "application/x-zmachine" + case "z4": + return "application/x-zmachine" + case "z5": + return "application/x-zmachine" + case "z6": + return "application/x-zmachine" + case "z7": + return "application/x-zmachine" + case "z8": + return "application/x-zmachine" + case "zaz": + return "application/vnd.zzazz.deck+xml" + case "zip": + return "application/zip" + case "zir": + return "application/vnd.zul" + case "zirz": + return "application/vnd.zul" + case "zmm": + return "application/vnd.handheld-entertainment+xml" + + default: + return "" + } + return "" +} diff --git a/httpserver/server.go b/httpserver/server.go new file mode 100644 index 0000000..9410193 --- /dev/null +++ b/httpserver/server.go @@ -0,0 +1,467 @@ +package httpserver + +import ( + "b612.me/starcrypto" + "b612.me/starlog" + "b612.me/staros" + "context" + "encoding/base64" + "errors" + "fmt" + "io" + "io/ioutil" + "net" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "time" +) + +var version = "2.0.0" + +type HttpServerCfgs func(cfg *HttpServerCfg) + +type HttpServerCfg struct { + basicAuthUser string + basicAuthPwd string + envPath string + uploadFolder string + logpath string + indexFile string + cert string + key string + addr string + port string + protectAuthPage []string + disableMIME bool + ctx context.Context +} +type HttpServer struct { + HttpServerCfg +} + +func WithTLSCert(cert, key string) HttpServerCfgs { + return func(cfg *HttpServerCfg) { + cfg.key = key + cfg.cert = cert + } +} + +func WithUploadFolder(path string) HttpServerCfgs { + return func(cfg *HttpServerCfg) { + cfg.uploadFolder = path + } +} + +func NewHttpServer(addr, port, path string, opts ...HttpServerCfgs) *HttpServer { + var server = HttpServer{ + HttpServerCfg: HttpServerCfg{ + addr: addr, + port: port, + envPath: path, + }, + } + for _, opt := range opts { + opt(&server.HttpServerCfg) + } + return &server +} + +func (h *HttpServer) Run(ctx context.Context) error { + h.ctx = ctx + server := http.Server{ + Addr: h.addr + ":" + h.port, + Handler: h, + } + go func() { + select { + case <-h.ctx.Done(): + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + server.Shutdown(ctx) + } + }() + if h.logpath != "" { + starlog.SetLogFile(h.logpath, starlog.Std, true) + } + netcards, err := net.Interfaces() + if err == nil { + for _, v := range netcards { + if strings.Contains(v.Flags.String(), "up") { + addrs, err := v.Addrs() + if err == nil { + var ips []string + for _, ip := range addrs { + ips = append(ips, ip.String()) + } + starlog.Noticef("Name:%s IP:%s MAC:%s\n", v.Name, strings.Join(ips, ","), v.HardwareAddr) + } + } + } + } + starlog.Infoln("Listening on " + h.addr + ":" + h.port) + if h.cert == "" { + if err := server.ListenAndServe(); err != http.ErrServerClosed { + return err + } + return nil + } + if err := server.ListenAndServeTLS(h.cert, h.key); err != http.ErrServerClosed { + return err + } + return nil +} + +func (h *HttpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + h.Listen(w, r) +} + +func (h *HttpServer) GiveBasicAuth(w http.ResponseWriter) { + w.Header().Set("WWW-Authenticate", ` Basic realm="Please Enter Passwd"`) + w.WriteHeader(401) + w.Write([]byte(` + + 401 Authorization Required + +

401 Authorization Required

+
B612 HTTP SERVER
+ + `)) +} + +func (h *HttpServer) BasicAuth(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request) bool { + if h.basicAuthPwd != "" { + if len(h.protectAuthPage) != 0 { + for _, v := range h.protectAuthPage { + if !(strings.Index(r.URL.Path, v) == 0 || strings.Contains(r.URL.RawQuery, v)) { + return true + } else { + break + } + } + } + authHeader := strings.TrimSpace(r.Header.Get("Authorization")) + if len(authHeader) == 0 { + log.Noticeln("No Authed! Get Path is", r.URL.Path, r.RemoteAddr) + h.GiveBasicAuth(w) + return false + } else { + userAuth := base64.StdEncoding.EncodeToString([]byte(h.basicAuthUser + ":" + h.basicAuthPwd)) + authStr := strings.Split(authHeader, " ") + if strings.TrimSpace(authStr[1]) != userAuth || strings.ToLower(strings.TrimSpace(authStr[0])) != "basic" { + log.Noticeln("Auth Failed! Get Path is", r.URL.Path, r.RemoteAddr, "pwd enter is", authHeader) + h.GiveBasicAuth(w) + return false + } + log.Infof("Path %s Authoried by %s:%s\n", r.URL.Path, r.RemoteAddr, authHeader) + } + } + return true +} + +func (h *HttpServer) SetUpload(w http.ResponseWriter, r *http.Request, path string) bool { + if h.uploadFolder != "" { + if len(r.URL.Query()["upload"]) != 0 { + w.Write([]byte(`
+

B612 File Upload Page

+

上传文件:

+ +
+

Copyright@b612.me

`)) + return true + } + } + return false +} +func (h *HttpServer) Listen(w http.ResponseWriter, r *http.Request) { + log := starlog.Std.NewFlag() + w.Header().Set("X-Powered-By", "B612.ME") + w.Header().Set("Server", "B612/"+version) + if !h.BasicAuth(log, w, r) { + return + } + path := r.URL.Path + if h.uploadFolder != "" && path == "/recv" && len(r.URL.Query()["upload"]) != 0 { + h.uploadFile(w, r) + return + } + fullpath := filepath.Join(h.envPath, path) + if path == "/" && h.indexFile != "" { + if staros.Exists(filepath.Join(h.envPath, h.indexFile)) { + fullpath = filepath.Join(h.envPath, h.indexFile) + path = "/" + h.indexFile + } + } + log.Noticef("Start Method:%s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr) + if h.SetUpload(w, r, path) { + return + } + switch r.Method { + case "OPTIONS", "HEAD": + err := h.BuildHeader(w, r, fullpath) + if err != nil { + log.Warningf("Finished Method:%s Path:%s IP:%s Err:%v\n", r.Method, path, r.RemoteAddr, err) + } else { + log.Infof("Finished Method:%s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr) + } + case "GET": + err := h.BuildHeader(w, r, fullpath) + if err != nil { + log.Warningf("GET Header Build Failed Path:%s IP:%s Err:%v\n", path, r.RemoteAddr, err) + } + err = h.ResponseGet(log, w, r, fullpath) + if err != nil { + log.Warningf("Finished Method %s Path:%s IP:%s Err:%v\n", r.Method, path, r.RemoteAddr, err) + return + } + log.Infof("Finished Method:%s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr) + default: + log.Warningf("Invalid Method %s Path:%s IP:%s\n", r.Method, path, r.RemoteAddr) + return + } +} + +func (h *HttpServer) CalcRange(r *http.Request) (int64, int64) { + var rangeStart, rangeEnd int64 + rangeStart, rangeEnd = -1, -1 + for k, v := range r.Header { + if strings.ToLower(k) == "range" { + if strings.Contains(v[0], "bytes=") { + v[0] = strings.Replace(v[0], "bytes=", "", -1) + } + data := strings.Split(v[0], "-") + if len(data) == 0 { + break + } + rangeStart, _ = strconv.ParseInt(data[0], 10, 64) + if len(data) > 1 { + rangeEnd, _ = strconv.ParseInt(data[1], 10, 64) + } + //w.WriteHeader(206) //206 支持断点续传 + break + } + } + return rangeStart, rangeEnd +} + +func (h *HttpServer) BuildHeader(w http.ResponseWriter, r *http.Request, fullpath string) error { + if r.Method == "OPTIONS" { + w.Header().Set("Allow", "OPTIONS,GET,HEAD") + w.Header().Set("Content-Length", "0") + } + w.Header().Set("Date", strings.ReplaceAll(time.Now().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT")) + if staros.IsFolder(fullpath) { + return nil + } + mime := h.MIME(fullpath) + if h.disableMIME || mime == "" { + w.Header().Set("Content-Type", "application/download") + w.Header().Set("Content-Disposition", "attachment;filename="+filepath.Base(fullpath)) + w.Header().Set("Content-Transfer-Encoding", "binary") + } else { + w.Header().Set("Content-Type", mime) + } + if staros.Exists(fullpath) { + finfo, err := os.Stat(fullpath) + if err != nil { + w.WriteHeader(502) + w.Write([]byte("Failed to Read " + fullpath + ",reason is " + err.Error())) + return err + } + w.Header().Set("Accept-Ranges", "bytes") + w.Header().Set("ETag", starcrypto.Md5Str([]byte(finfo.ModTime().String()))) + w.Header().Set("Last-Modified", strings.ReplaceAll(finfo.ModTime().UTC().Format("Mon, 2 Jan 2006 15:04:05 MST"), "UTC", "GMT")) + if r.Method != "OPTIONS" { + w.Header().Set("Content-Length", strconv.FormatInt(finfo.Size(), 10)) + start, end := h.CalcRange(r) + if start != -1 { + if end == -1 { + w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(finfo.Size(), 10)+"/"+strconv.FormatInt(finfo.Size(), 10)) + //w.Header().Set("Content-Length", strconv.FormatInt(fpinfo.Size()-rangeStart, 10)) + } else { + w.Header().Set("Content-Range", `bytes `+strconv.FormatInt(start, 10)+"-"+strconv.FormatInt(end, 10)+"/"+strconv.FormatInt(finfo.Size(), 10)) + //w.Header().Set("Content-Length", strconv.FormatInt(1+rangeEnd-rangeStart, 10)) + } + } + } + } + return nil +} + +func (h *HttpServer) ResponseGet(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error { + if staros.IsFolder(fullpath) { + return h.getFolder(log, w, r, fullpath) + } + return h.getFile(log, w, r, fullpath) +} + +func (h *HttpServer) getFolder(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error { + dir, err := ioutil.ReadDir(fullpath) + if err != nil { + log.Errorf("Read Folder %s failed:%v\n", fullpath, err) + w.WriteHeader(403) + if r.Method == "HEAD" { + return err + } + w.Write([]byte("

Cannot Access!

")) + } + if r.Method != "GET" { + return nil + } + w.Write([]byte("\n\n

B612 Http Server - " + version + "

")) + if h.uploadFolder != "" { + w.Write([]byte("Upload Web Page Is Openned!

")) + } + w.Write([]byte("
\n"))
+	if r.URL.Path != "/" {
+		w.Write([]byte(fmt.Sprintf("

%s %s

\n", r.URL.Path+"/..", "..", "上层文件夹"))) + } + if r.URL.Path == "/" { + r.URL.Path = "" + } else if r.URL.Path[len(r.URL.Path)-1:] == "/" { + r.URL.Path = r.URL.Path[0 : len(r.URL.Path)-1] + } + + for _, v := range dir { + if v.Name() != "." || v.Name() != ".." { + if !v.IsDir() { + w.Write([]byte(fmt.Sprintf("

%s %d %s

\n", r.URL.Path+"/"+v.Name(), v.Name(), int(v.Size()), v.ModTime().Format("2006-01-02 15:04:05")))) + } else { + w.Write([]byte(fmt.Sprintf("

%s %s %s

\n", r.URL.Path+"/"+v.Name(), v.Name(), "文件夹", v.ModTime().Format("2006-01-02 15:04:05")))) + } + } + } + w.Write([]byte("
\n")) + return nil +} + +func (h *HttpServer) getFile(log *starlog.StarLogger, w http.ResponseWriter, r *http.Request, fullpath string) error { + if !staros.Exists(fullpath) { + w.WriteHeader(404) + return errors.New("File Not Found! 404 ERROR") + } + //starlog.Debugln(r.Header) + startRange, endRange := h.CalcRange(r) + fp, err := os.Open(fullpath) + if err != nil { + log.Errorf("Failed to open file %s,reason:%v\n", r.URL.Path, err) + w.WriteHeader(502) + w.Write([]byte("

502 SERVER ERROR

")) + return err + } + defer fp.Close() + var transferData int + defer func() { + if transferData != 0 { + var tani string + tani = fmt.Sprintf("%v Byte", transferData) + if f64 := float64(transferData) / 1024; f64 > 1 { + tani = fmt.Sprintf("%v KB", f64) + if f64 = float64(f64) / 1024; f64 > 1 { + tani = fmt.Sprintf("%v MB", f64) + if f64 = float64(f64) / 1024; f64 > 1 { + tani = fmt.Sprintf("%v GB", f64) + } + } + } + log.Infof("Tranfered File %s %d bytes (%s) to remote %v\n", r.URL.Path, + transferData, tani, r.RemoteAddr) + } + }() + if startRange == -1 { + w.WriteHeader(200) + for { + buf := make([]byte, 1048576) + n, err := fp.Read(buf) + if n != 0 { + ns, err := w.Write(buf[0:n]) + transferData += ns + if err != nil { + log.Errorf("Transfer File %s to Remote Failed:%v\n", fullpath, err) + return err + } + } + if err != nil { + if err == io.EOF { + break + } + log.Errorln("Read File %s Failed:%v\n", fullpath, err) + return err + } + } + return nil + } + log.Debugf("206 transfer mode for %v %v\n", r.URL.Path, r.RemoteAddr) + w.WriteHeader(206) + fp.Seek(int64(startRange), 0) + count := startRange + for { + buf := make([]byte, 1048576) + n, err := fp.Read(buf) + if err != nil { + if err == io.EOF { + break + } + log.Errorf("Read File %s Failed:%v\n", r.URL.Path, err) + return err + } + if endRange == -1 { + ns, err := w.Write(buf[0:n]) + transferData += ns + if err != nil { + log.Errorf("Transfer File %s to Remote Failed:%v\n", r.URL.Path, err) + return err + } + } else { + if count > endRange { + break + } + writeNum := n + if count+int64(n) > endRange { + writeNum = int(endRange - count + 1) + } + ns, err := w.Write(buf[0:writeNum]) + transferData += ns + if err != nil { + log.Errorln("Transfer Error:", err) + return err + } + count += int64(n) + } + } + return nil +} + +func (h *HttpServer) uploadFile(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(200) + w.Write([]byte("USE POST METHOD!")) + return + } + r.ParseMultipartForm(10485760) + file, handler, err := r.FormFile("victorique") + if err != nil { + starlog.Errorf("Parse File From Form Failed:%v\n", err) + w.WriteHeader(502) + w.Write([]byte(err.Error())) + return + } + defer file.Close() + starlog.Noticef("Uploading %s From %s\n", handler.Filename, r.RemoteAddr) + os.MkdirAll(filepath.Join(h.envPath, h.uploadFolder), 0755) + f, err := os.OpenFile(filepath.Join(h.uploadFolder, handler.Filename), os.O_WRONLY|os.O_CREATE, 0755) + if err != nil { + starlog.Errorf("Open Local File %s Form %v Failed:%v\n", handler.Filename, r.RemoteAddr, err) + return + } + defer f.Close() + _, err = io.Copy(f, file) + if err != nil { + starlog.Errorf("Write File %s Form %v Failed:%v\n", handler.Filename, r.RemoteAddr, err) + return + } + starlog.Infof("Write File %s Form %v Finished\n", handler.Filename, r.RemoteAddr) + fmt.Fprintf(w, `

%v

Return To Web Page

`, handler.Header) +} diff --git a/image/image-basic.go b/image/image-basic.go new file mode 100644 index 0000000..fc8d458 --- /dev/null +++ b/image/image-basic.go @@ -0,0 +1,193 @@ +package image + +import ( + "b612.me/staros" + "errors" + "image" + "io/ioutil" + "os" + + "github.com/golang/freetype" + + "github.com/nfnt/resize" + + "image/color" + "image/draw" + _ "image/gif" + _ "image/jpeg" + "image/png" + _ "image/png" +) + +func OpenImage(name string) (image.Image, error) { + if !staros.Exists(name) { + return nil, errors.New("File Not Exists") + } + fso, err := os.Open(name) + if err != nil { + return nil, err + } + img, _, err := image.Decode(fso) + if err != nil { + return nil, err + } + return img, nil +} + +func MergePhoto(big, small image.Image, bigsize, smallsize uint, x, y int) image.Image { + big = resize.Resize(bigsize, bigsize, big, resize.Lanczos3) + small = resize.Resize(smallsize, smallsize, small, resize.Lanczos3) + offset := image.Pt(x, y) + b := big.Bounds() + nimg := image.NewRGBA(b) + draw.Draw(nimg, b, big, image.ZP, draw.Src) + draw.Draw(nimg, small.Bounds(), small, offset, draw.Over) + return nimg +} + +func SavePhoto(path string, img image.Image) error { + imgf, err := os.Create(path) + if err != nil { + return err + } + defer imgf.Close() + return png.Encode(imgf, img) +} + +func SetAlpha(img image.Image, alpha uint8) image.Image { + size := img.Bounds() + nimg := image.NewRGBA(size) + for x := 0; x < size.Dx(); x++ { + for y := 0; y < size.Dy(); y++ { + r, g, b, a := img.At(x, y).RGBA() + r = r >> 8 + g = g >> 8 + b = b >> 8 + a = a >> 8 + nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), alpha}) + //nimg.Set(x, y, color.Alpha{alpha}) + //nimg.Set(x, y, img.At(x, y)) + } + } + return nimg +} + +func AddText(text string, img image.Image, x, y int, dpi, size float64, colors color.RGBA, ttf string) (image.Image, error) { + if !staros.Exists(ttf) { + return nil, errors.New("File Not Exists") + } + fontbyte, err := ioutil.ReadFile(ttf) + if err != nil { + return nil, err + } + font, err := freetype.ParseFont(fontbyte) + if err != nil { + return nil, err + } + nimg, ok := img.(draw.Image) + if !ok { + size := img.Bounds() + nimg = image.NewRGBA(img.Bounds()) + for x := 0; x < size.Dx(); x++ { + for y := 0; y < size.Dy(); y++ { + r, g, b, a := img.At(x, y).RGBA() + nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}) + } + } + } + f := freetype.NewContext() + f.SetDPI(dpi) + f.SetFontSize(size) + f.SetFont(font) + f.SetClip(nimg.Bounds()) + f.SetDst(nimg) + f.SetSrc(image.NewUniform(colors)) + _, err = f.DrawString(text, freetype.Pt(x, y)) + if err != nil { + return nil, err + } + return nimg, nil +} + +type TextImg struct { + Text string + X int + Y int + Dpi float64 + Size float64 + Color color.NRGBA +} + +type TextList struct { + List []TextImg + TTF []byte +} + +func AddListTests(list TextList, img image.Image) (image.Image, error) { + font, err := freetype.ParseFont(list.TTF) + if err != nil { + return nil, err + } + nimg, ok := img.(draw.Image) + if !ok { + size := img.Bounds() + nimg = image.NewRGBA(img.Bounds()) + for x := 0; x < size.Dx(); x++ { + for y := 0; y < size.Dy(); y++ { + r, g, b, a := img.At(x, y).RGBA() + nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}) + } + } + } + for _, v := range list.List { + f := freetype.NewContext() + f.SetDPI(v.Dpi) + f.SetFontSize(v.Size) + f.SetFont(font) + f.SetClip(nimg.Bounds()) + f.SetDst(nimg) + f.SetSrc(image.NewUniform(v.Color)) + _, err = f.DrawString(v.Text, freetype.Pt(v.X, v.Y)) + if err != nil { + return nil, err + } + } + return nimg, nil +} + +func AddTexts(text string, img image.Image, x, y int, dpi, size float64, colors color.NRGBA, ttf string) (image.Image, error) { + if !staros.Exists(ttf) { + return nil, errors.New("File Not Exists") + } + fontbyte, err := ioutil.ReadFile(ttf) + if err != nil { + return nil, err + } + font, err := freetype.ParseFont(fontbyte) + if err != nil { + return nil, err + } + nimg, ok := img.(draw.Image) + if !ok { + size := img.Bounds() + nimg = image.NewRGBA(img.Bounds()) + for x := 0; x < size.Dx(); x++ { + for y := 0; y < size.Dy(); y++ { + r, g, b, a := img.At(x, y).RGBA() + nimg.Set(x, y, color.NRGBA{uint8(r), uint8(g), uint8(b), uint8(a)}) + } + } + } + f := freetype.NewContext() + f.SetDPI(dpi) + f.SetFontSize(size) + f.SetFont(font) + f.SetClip(nimg.Bounds()) + f.SetDst(nimg) + f.SetSrc(image.NewUniform(colors)) + _, err = f.DrawString(text, freetype.Pt(x, y)) + if err != nil { + return nil, err + } + return nimg, nil +} diff --git a/image/image.go b/image/image.go new file mode 100644 index 0000000..ab1aca0 --- /dev/null +++ b/image/image.go @@ -0,0 +1,82 @@ +package image + +import ( + "fmt" + "image" + + "b612.me/starlog" + "github.com/spf13/cobra" +) + +func init() { + Cmd.AddCommand(imgMirrorCmd) +} + +var Cmd = &cobra.Command{ + Use: "image", + Short: "图像处理", + Long: "简单的图像处理工具", + Run: func(this *cobra.Command, args []string) { + this.Help() + }, +} + +var imgMirrorCmd = &cobra.Command{ + Use: "mirror", + Short: "图像镜像翻转", + Long: "图像镜像翻转<水平>", + Run: func(this *cobra.Command, args []string) { + if len(args) == 0 { + starlog.Errorln("请指定需要转换的图像!") + return + } + for _, v := range args { + img, err := OpenImage(v) + if err != nil { + starlog.Errorln(err, v) + continue + } + size := img.Bounds() + nimg := image.NewRGBA(size) + for x := 0; x < size.Dx(); x++ { + for y := 0; y < size.Dy(); y++ { + nimg.Set(size.Dx()-x, y, img.At(x, y)) + } + } + if err := SavePhoto(v, nimg); err != nil { + starlog.Errorln(err, v) + continue + } else { + fmt.Println(v, "转换已完成!") + } + } + fmt.Println("任务完成!") + }, +} + +var imgAlpha = &cobra.Command{ + Use: "alpha", + Short: "设置透明度", + Long: "设置alpha通道透明度", + Run: func(this *cobra.Command, args []string) { + if len(args) == 0 { + starlog.Errorln("请指定需要转换的图像!") + return + } + for _, v := range args { + img, err := OpenImage(v) + if err != nil { + starlog.Errorln(err, v) + continue + } + img = SetAlpha(img, 4) + if err := SavePhoto(v, img); err != nil { + starlog.Errorln(err, v) + continue + } else { + fmt.Println(v, "转换已完成!") + } + } + fmt.Println("任务完成!") + }, +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..d4f6f8e --- /dev/null +++ b/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "b612.me/apps/b612/attach" + "b612.me/apps/b612/base64" + "b612.me/apps/b612/base85" + "b612.me/apps/b612/base91" + "b612.me/apps/b612/detach" + "b612.me/apps/b612/df" + "b612.me/apps/b612/dfinder" + "b612.me/apps/b612/ftp" + "b612.me/apps/b612/generate" + "b612.me/apps/b612/hash" + "b612.me/apps/b612/httpreverse" + "b612.me/apps/b612/httpserver" + "b612.me/apps/b612/image" + "b612.me/apps/b612/merge" + "b612.me/apps/b612/search" + "b612.me/apps/b612/split" + "b612.me/apps/b612/tcping" + "b612.me/apps/b612/uac" + "b612.me/apps/b612/vic" + "github.com/spf13/cobra" +) + +var cmdRoot = &cobra.Command{ + Use: "b612", + Version: "2.0.1", +} + +func init() { + cmdRoot.AddCommand(tcping.Cmd, uac.Cmd, httpserver.Cmd, httpreverse.Cmd, + base64.Cmd, base85.Cmd, base91.Cmd, attach.Cmd, detach.Cmd, df.Cmd, dfinder.Cmd, + ftp.Cmd, generate.Cmd, hash.Cmd, image.Cmd, merge.Cmd, search.Cmd, split.Cmd, vic.Cmd) +} + +func main() { + cmdRoot.Execute() +} diff --git a/merge/merge.go b/merge/merge.go new file mode 100644 index 0000000..e4749b5 --- /dev/null +++ b/merge/merge.go @@ -0,0 +1,46 @@ +package merge + +import ( + "b612.me/starcrypto" + "fmt" + + "b612.me/starlog" + + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "merge", + Short: "合并文件", + Long: "按路径自动合并分割的文件", + Run: func(this *cobra.Command, args []string) { + var src, dst string + if len(args) == 2 { + src = args[0] + dst = args[1] + } else { + src, _ = this.Flags().GetString("src") + dst, _ = this.Flags().GetString("dst") + } + if src == "" || dst == "" { + this.Help() + return + } + err := starcrypto.MergeFile(src, dst, func(pect float64) { + if pect == 100 { + fmt.Println("文件已处理:100.000000%") + } else { + fmt.Printf("文件已处理:%f%%\r", pect) + } + }) + if err != nil { + starlog.Errorln(err.Error) + } + + }, +} + +func init() { + Cmd.Flags().StringP("src", "s", "", "源文件地址,用*替换文件数字") + Cmd.Flags().StringP("dst", "d", "", "目标文件地址") +} diff --git a/net/cmd.go b/net/cmd.go new file mode 100644 index 0000000..9d9f1a1 --- /dev/null +++ b/net/cmd.go @@ -0,0 +1 @@ +package net diff --git a/net/forward.go b/net/forward.go new file mode 100644 index 0000000..57ea1bd --- /dev/null +++ b/net/forward.go @@ -0,0 +1,213 @@ +package net + +import ( + "b612.me/starlog" + "context" + "errors" + "fmt" + "io" + "net" + "sync" + "sync/atomic" + "time" +) + +type NetForward struct { + LocalAddr string + LocalPort int + RemoteURI string + EnableTCP bool + EnableUDP bool + DialTimeout time.Duration + UDPTimeout time.Duration + stopCtx context.Context + stopFn context.CancelFunc + running int32 +} + +func (n *NetForward) Close() { + n.stopFn() +} +func (n *NetForward) Run() error { + if !atomic.CompareAndSwapInt32(&n.running, 0, 1) { + return errors.New("already running") + } + n.stopCtx, n.stopFn = context.WithCancel(context.Background()) + if n.DialTimeout == 0 { + n.DialTimeout = time.Second * 10 + } + var wg sync.WaitGroup + if n.EnableTCP { + wg.Add(1) + go func() { + defer wg.Done() + n.runTCP() + }() + } + + if n.EnableUDP { + wg.Add(1) + go func() { + defer wg.Done() + n.runUDP() + }() + } + wg.Wait() + return nil +} + +func (n *NetForward) runTCP() error { + listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort)) + if err != nil { + starlog.Errorln("Listening On Tcp Failed:", err) + return err + } + go func() { + <-n.stopCtx.Done() + listen.Close() + }() + starlog.Infof("Listening TCP on %v\n", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort)) + for { + conn, err := listen.Accept() + if err != nil { + continue + } + log := starlog.Std.NewFlag() + log.Infof("Accept New TCP Conn from %v\n", conn.RemoteAddr().String()) + go func(conn net.Conn) { + rmt, err := net.DialTimeout("tcp", n.RemoteURI, n.DialTimeout) + if err != nil { + log.Errorf("Dial Remote %s Failed:%v\n", n.RemoteURI, err) + conn.Close() + return + } + log.Infof("Connect %s <==> %s\n", conn.RemoteAddr().String(), n.RemoteURI) + Copy(rmt, conn) + log.Noticef("Connection Closed %s <==> %s", conn.RemoteAddr().String(), n.RemoteURI) + }(conn) + } +} + +type UDPConn struct { + net.Conn + listen *net.UDPConn + remoteAddr *net.UDPAddr + lastbeat int64 +} + +func (u UDPConn) Write(p []byte) (n int, err error) { + u.lastbeat = time.Now().Unix() + return u.Conn.Write(p) +} + +func (u UDPConn) Read(p []byte) (n int, err error) { + u.lastbeat = time.Now().Unix() + return u.Conn.Read(p) +} + +func (u UDPConn) Work() { + buf := make([]byte, 8192) + for { + count, err := u.Read(buf) + if err != nil { + u.Close() + u.lastbeat = 0 + return + } + _, err = u.listen.Write(buf[0:count]) + if err != nil { + u.lastbeat = 0 + return + } + } +} + +func (n *NetForward) runUDP() error { + var mu sync.RWMutex + udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%v", n.LocalAddr, n.LocalPort)) + if err != nil { + return err + } + listen, err := net.ListenUDP("udp", udpAddr) + if err != nil { + return err + } + starlog.Infof("Listening UDP on %v\n", fmt.Sprintf("%s:%d", n.LocalAddr, n.LocalPort)) + go func() { + <-n.stopCtx.Done() + listen.Close() + }() + udpMap := make(map[string]UDPConn) + go func() { + for { + select { + case <-n.stopCtx.Done(): + return + case <-time.After(time.Second * 60): + mu.Lock() + for k, v := range udpMap { + if time.Now().Unix() > int64(n.UDPTimeout.Seconds())+v.lastbeat { + delete(udpMap, k) + starlog.Noticef("Connection Closed %s <==> %s", v.remoteAddr.String(), n.RemoteURI) + } + } + mu.Unlock() + } + } + }() + buf := make([]byte, 8192) + for { + count, rmt, err := listen.ReadFromUDP(buf) + if err != nil || rmt.String() == n.RemoteURI { + continue + } + go func(data []byte, rmt *net.UDPAddr) { + log := starlog.Std.NewFlag() + mu.Lock() + addr, ok := udpMap[rmt.String()] + if !ok { + log.Infof("Accept New UDP Conn from %v\n", rmt.String()) + conn, err := net.Dial("udp", n.RemoteURI) + if err != nil { + log.Errorf("Dial Remote %s Failed:%v\n", n.RemoteURI, err) + mu.Unlock() + return + } + addr = UDPConn{ + Conn: conn, + remoteAddr: rmt, + listen: listen, + lastbeat: time.Now().Unix(), + } + udpMap[rmt.String()] = addr + go addr.Work() + log.Infof("Connect %s <==> %s\n", rmt.String(), n.RemoteURI) + } + mu.Unlock() + _, err := addr.Write(data) + if err != nil { + mu.Lock() + addr.Close() + delete(udpMap, addr.remoteAddr.String()) + mu.Unlock() + log.Noticef("Connection Closed %s <==> %s", rmt.String(), n.RemoteURI) + } + }(buf[0:count], rmt) + } +} + +func Copy(dst, src net.Conn) { + var wg sync.WaitGroup + wg.Add(2) + go func() { + defer wg.Done() + io.Copy(dst, src) + }() + go func() { + defer wg.Done() + io.Copy(src, dst) + }() + wg.Wait() + dst.Close() + src.Close() +} diff --git a/net/forward_test.go b/net/forward_test.go new file mode 100644 index 0000000..fd00d2d --- /dev/null +++ b/net/forward_test.go @@ -0,0 +1,16 @@ +package net + +import "testing" + +func TestForward(t *testing.T) { + var f = NetForward{ + LocalAddr: "127.0.0.1", + LocalPort: 22232, + RemoteURI: "127.0.0.1:1127", + EnableTCP: true, + EnableUDP: true, + DialTimeout: 0, + UDPTimeout: 0, + } + f.Run() +} diff --git a/net/natclient.go b/net/natclient.go new file mode 100644 index 0000000..2416d8b --- /dev/null +++ b/net/natclient.go @@ -0,0 +1,27 @@ +package net + +import ( + "net" + "sync" +) + +type SimpleNatClient struct { + mu sync.RWMutex + cmdTCPConn net.Conn + cmdUDPConn *net.UDPAddr + ServiceTarget string + CmdTarget string + tcpAlived bool +} + +func (s *SimpleNatClient) tcpCmdConn() net.Conn { + s.mu.RLock() + defer s.mu.RUnlock() + return s.cmdTCPConn +} + +func (s *SimpleNatClient) tcpCmdConnAlived() bool { + s.mu.RLock() + defer s.mu.RUnlock() + return s.tcpAlived +} diff --git a/net/natserver.go b/net/natserver.go new file mode 100644 index 0000000..26fd41f --- /dev/null +++ b/net/natserver.go @@ -0,0 +1,138 @@ +package net + +import ( + "b612.me/starlog" + "bytes" + "errors" + "fmt" + "io" + "net" + "strings" + "sync" + "time" +) + +var MSG_CMD_HELLO = []byte{11, 27, 19, 96, 182, 18, 25, 150, 17, 39} +var MSG_NEW_CONN = []byte{0, 0, 0, 0, 255, 255, 255, 255, 11, 27} +var MSG_NEW_CONN_REQ = []byte{0, 0, 0, 0, 255, 255, 255, 255, 19, 96} +var MSG_CLOSE = []byte{255, 255, 0, 0, 255, 0, 0, 255, 255, 27} +var MSG_HEARTBEAT = []byte{6, 66, 66, 6, 6, 66, 6, 66, 11, 27} + +type SimpleNatServer struct { + mu sync.RWMutex + cmdTCPConn net.Conn + cmdUDPConn *net.UDPAddr + listenTcp net.Listener + listenUDP *net.UDPConn + Addr string + Port int + lastTCPHeart int64 + lastUDPHeart int64 + Passwd string + DialTimeout int64 + UDPTimeout int64 + running int32 + + tcpConnPool chan net.Conn + tcpAlived bool +} + +func (s *SimpleNatServer) getConnfromTCPPool() (net.Conn, error) { + select { + case conn := <-s.tcpConnPool: + return conn, nil + case <-time.After(time.Second * 10): + return nil, errors.New("no connection got") + } +} + +func (s *SimpleNatServer) tcpCmdConn() net.Conn { + s.mu.RLock() + defer s.mu.RUnlock() + return s.cmdTCPConn +} + +func (s *SimpleNatServer) tcpCmdConnAlived() bool { + s.mu.RLock() + defer s.mu.RUnlock() + return s.tcpAlived +} + +func (s *SimpleNatServer) listenTCP() error { + var err error + s.tcpConnPool = make(chan net.Conn, 10) + s.listenTcp, err = net.Listen("tcp", fmt.Sprintf("%s:d", s.Addr, s.Port)) + if err != nil { + starlog.Errorln("failed to listen tcp", err) + return err + } + for { + conn, err := s.listenTcp.Accept() + if err != nil { + continue + } + if s.tcpCmdConnAlived() { + go s.tcpClientServe(conn.(*net.TCPConn)) + continue + } + go s.waitingForTCPCmd(conn.(*net.TCPConn)) + } + return nil +} + +func (s *SimpleNatServer) tcpClientServe(conn *net.TCPConn) { + if !s.tcpCmdConnAlived() { + conn.Close() + return + } + + if strings.Split(conn.RemoteAddr().String(), ":")[0] == strings.Split(s.tcpCmdConn().RemoteAddr().String(), ":")[0] { + conn.SetReadDeadline(time.Now().Add(5 * time.Second)) + cmdBuf := make([]byte, 10) + if _, err := io.ReadFull(conn, cmdBuf); err == nil { + conn.SetReadDeadline(time.Time{}) + if bytes.Equal(cmdBuf, MSG_NEW_CONN) { + starlog.Noticef("Nat Server Recv New Client Conn From %v\n", conn.RemoteAddr().String()) + s.tcpConnPool <- conn + return + } + } + conn.SetReadDeadline(time.Time{}) + } + starlog.Noticef("Nat Server Recv New Side Conn From %v\n", conn.RemoteAddr().String()) + _, err := s.tcpCmdConn().Write(MSG_NEW_CONN_REQ) + if err != nil { + s.mu.Lock() + s.cmdTCPConn.Close() + s.tcpAlived = false + s.mu.Unlock() + starlog.Errorf("Failed to Write CMD To Client:%v\n", err) + return + } + reverse, err := s.getConnfromTCPPool() + if err != nil { + starlog.Errorf("Nat Server Conn to %v Closed %v\n", conn.RemoteAddr(), err) + conn.Close() + return + } + starlog.Infof("Nat Server Conn %v<==>%v Connected\n", conn.RemoteAddr(), reverse.RemoteAddr()) + Copy(reverse, conn) + starlog.Warningf("Nat Server Conn %v<==>%v Closed\n", conn.RemoteAddr(), reverse.RemoteAddr()) +} + +func (s *SimpleNatServer) waitingForTCPCmd(conn *net.TCPConn) { + conn.SetReadDeadline(time.Now().Add(time.Duration(s.DialTimeout) * time.Second)) + cmdBuf := make([]byte, 10) + if _, err := io.ReadFull(conn, cmdBuf); err != nil { + conn.Close() + return + } + if bytes.Equal(cmdBuf, MSG_CMD_HELLO) { + s.mu.Lock() + s.cmdTCPConn = conn + s.tcpAlived = true + conn.SetKeepAlive(true) + conn.SetKeepAlivePeriod(time.Second * 20) + s.mu.Unlock() + } +} diff --git a/rmt/remoteCmdClient.go b/rmt/remoteCmdClient.go new file mode 100644 index 0000000..e34b823 --- /dev/null +++ b/rmt/remoteCmdClient.go @@ -0,0 +1,69 @@ +package rmt + +import ( + "b612.me/notify" + "b612.me/notify/starnotify" + "b612.me/starlog" + "fmt" + "github.com/spf13/cobra" + "os" + "strings" + "time" +) + +var ( + rmtRmt string + rmtCmd RmtCmd +) + +func init() { + Cmdc.Flags().StringVarP(&rmtRmt, "remote", "r", "", "Remote Address") + Cmdc.Flags().StringVarP(&rmtPol, "protocol", "o", "tcp", "Remote protocol") + Cmdc.Flags().StringVarP(&rmtCmd.Cmd, "cmd", "c", "", "command") + Cmdc.Flags().StringVarP(&rmtCmd.WorkDir, "workdir", "w", "", "workdir") + Cmdc.Flags().StringSliceVarP(&rmtCmd.Env, "env", "e", []string{}, "env") + Cmdc.Flags().BoolVarP(&rmtCmd.Daemon, "daemon", "d", false, "daemon") + Cmdc.Flags().BoolVarP(&rmtCmd.Stream, "stream", "s", false, "stream") + Cmdc.Flags().StringVarP(&rmtListenPort, "port", "p", "5780", "Remote Port") +} + +var Cmdc = &cobra.Command{ + Use: "rmtc", + Short: "simple remote shell client", + Run: func(cmd *cobra.Command, args []string) { + if rmtRmt == "" { + starlog.Errorln("Please Enter Remote Path") + os.Exit(1) + } + err := starnotify.NewClient("c").Connect(rmtPol, fmt.Sprintf("%s:%s", rmtRmt, rmtListenPort)) + if err != nil { + starlog.Errorln("Create Remote Failed", err) + os.Exit(2) + } + starnotify.C("c").SetLink("stream", RmtSteam) + defer starnotify.C("c").Stop() + cdata, err := starnotify.C("c").SendWaitObj("cmd", rmtCmd, time.Second*3600) + if err != nil { + starlog.Errorln("Got Answer Failed:", err) + os.Exit(3) + } + data, err := cdata.Value.ToInterface() + if err != nil { + starlog.Errorln("Decode FAILED:", err) + os.Exit(4) + } + rtnData, ok := data.(RmtCmdBack) + if !ok { + starlog.Errorln("Decode FAILED2:", err) + os.Exit(5) + } + fmt.Println("Return Pid:", rtnData.RetCode) + if !rmtCmd.Stream { + fmt.Println("Return OutPut\n", rtnData.OutPut) + } + }, +} + +func RmtSteam(msg *notify.Message) { + fmt.Println(strings.TrimSpace(msg.Value.MustToString())) +} diff --git a/rmt/remoteCmdServer.go b/rmt/remoteCmdServer.go new file mode 100644 index 0000000..28e1263 --- /dev/null +++ b/rmt/remoteCmdServer.go @@ -0,0 +1,166 @@ +package rmt + +import ( + "b612.me/notify" + "b612.me/notify/starnotify" + "b612.me/starlog" + "b612.me/staros" + "fmt" + "github.com/spf13/cobra" + "os" + "os/signal" + "runtime" + "strings" + "time" +) + +type RmtCmd struct { + Env []string + WorkDir string + Cmd string + Daemon bool + Stream bool +} + +type RmtCmdBack struct { + RetCode int + OutPut string +} + +var ( + rmtListenPort string + rmtPol string +) + +func init() { + notify.RegisterName("remoteback", RmtCmdBack{}) + notify.RegisterName("remotecmd", RmtCmd{}) + Cmds.Flags().StringVarP(&rmtListenPort, "port", "p", "5780", "Listen Port") + Cmds.Flags().StringVarP(&rmtPol, "protocol", "o", "tcp", "Remote protocol") +} + +var Cmds = &cobra.Command{ + Use: "rmts", + Short: "simple remote shell server", + Run: func(cmd *cobra.Command, args []string) { + if rmtListenPort == "" { + starlog.Errorln("Please Enter Port") + os.Exit(1) + } + err := starnotify.NewServer("s").Listen(rmtPol, "0.0.0.0:"+rmtListenPort) + if err != nil { + starlog.Errorln("Create Listener Failed", err) + os.Exit(2) + } + starnotify.S("s").SetLink("cmd", ListenAndServe) + starlog.Infoln("Service Running") + stopSig := make(chan os.Signal) + signal.Notify(stopSig, os.Kill, os.Interrupt) + <-stopSig + starlog.Noticeln("Recv Stop Sig") + starnotify.S("s").Stop() + }, +} + +func ListenAndServe(msg *notify.Message) { + data, err := msg.Value.ToInterface() + if err != nil { + return + } + cmd, ok := data.(RmtCmd) + if !ok { + return + } + var sysName, sysArg string = "bash", "-c" + if runtime.GOOS == "windows" { + sysName = "cmd.exe" + sysArg = "/c" + } + if cmd.Cmd == "" { + msg.ReplyObj(RmtCmdBack{ + RetCode: 255, + OutPut: "Please enter the command", + }) + return + } + starlog.Noticef("Recv Command:%+v\n", cmd) + var myCmd *staros.StarCmd + myCmd, err = staros.Command(sysName, sysArg, cmd.Cmd) + if err != nil { + msg.ReplyObj(RmtCmdBack{ + RetCode: 255, + OutPut: err.Error(), + }) + return + } + if cmd.WorkDir != "" { + myCmd.CMD.Dir = cmd.WorkDir + } + if len(cmd.Env) > 0 { + myCmd.CMD.Env = cmd.Env + } + if cmd.Daemon { + err := myCmd.Release() + if err != nil { + msg.ReplyObj(RmtCmdBack{ + RetCode: 254, + OutPut: err.Error(), + }) + return + } + pid := myCmd.CMD.Process.Pid + msg.ReplyObj(RmtCmdBack{ + RetCode: 0, + OutPut: fmt.Sprintf("Runned,PID is %d", pid), + }) + return + } + err = myCmd.Start() + if err != nil { + msg.ReplyObj(RmtCmdBack{ + RetCode: 254, + OutPut: err.Error(), + }) + return + } + for myCmd.IsRunning() { + time.Sleep(time.Millisecond * 10) + if cmd.Stream { + std, err := myCmd.NowAllOutput() + if err != nil { + std += "\n" + err.Error() + } + std = strings.TrimSpace(std) + if len(std) == 0 { + continue + } + msg.ClientConn.Server().SendObj(msg.ClientConn, "stream", std) + } + } + time.Sleep(time.Millisecond * 50) + if !cmd.Stream { + std := myCmd.AllStdOut() + if myCmd.AllStdErr() != nil { + std += "\n" + myCmd.AllStdErr().Error() + } + msg.ReplyObj(RmtCmdBack{ + RetCode: myCmd.ExitCode(), + OutPut: std, + }) + } else { + std, err := myCmd.NowAllOutput() + if err != nil { + std += "\n" + err.Error() + } + msg.ClientConn.Server().SendObj(msg.ClientConn, "stream", std) + time.Sleep(time.Millisecond * 1000) + err = msg.ReplyObj(RmtCmdBack{ + RetCode: myCmd.ExitCode(), + OutPut: "", + }) + if err != nil { + starlog.Warningln("Reply failed:", err) + } + } + return +} diff --git a/search/search.go b/search/search.go new file mode 100644 index 0000000..26a847e --- /dev/null +++ b/search/search.go @@ -0,0 +1,98 @@ +package search + +import ( + "b612.me/stario" + "b612.me/starlog" + "b612.me/startext" + "bufio" + "fmt" + "github.com/spf13/cobra" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +var stFolder string +var stNum, stMax, stMin int +var stautoGBK bool + +func init() { + Cmd.Flags().StringVarP(&stFolder, "folder", "f", "./", "搜索的文件夹") + Cmd.Flags().IntVarP(&stNum, "thread-num", "n", 5, "并发搜寻协程数") + Cmd.Flags().BoolVarP(&stautoGBK, "autogbk", "g", true, "自动GBK识别") + Cmd.Flags().IntVar(&stMax, "max", 0, "行最大字数") + Cmd.Flags().IntVar(&stMin, "min", 0, "行最小字数") +} + +var Cmd = &cobra.Command{ + Use: "st", + Short: "搜索文件中特定字符串", + Long: "搜索文件中特定字符串", + Run: func(this *cobra.Command, args []string) { + if len(args) != 2 { + starlog.Errorln("应当传入两个参数,搜寻文件后缀和搜寻文本") + os.Exit(1) + } + err := searchText(stFolder, args[0], args[1], stNum, stautoGBK, stMax, stMin) + if err != nil { + os.Exit(2) + } + return + }, +} + +func searchText(folder string, filematch string, text string, thread int, autoGBK bool, max, min int) error { + data, err := ioutil.ReadDir(folder) + if err != nil { + starlog.Errorln("read folder failed", folder, err) + return err + } + wg := stario.NewWaitGroup(thread) + searchFn := func(filepath string, text string) { + //starlog.Debugln("searching", filepath, text) + defer wg.Done() + fp, err := os.Open(filepath) + if err != nil { + starlog.Errorln("open file failed", filepath, err) + return + } + defer fp.Close() + reader := bufio.NewReader(fp) + count := 0 + for { + origin, err := reader.ReadString('\n') + count++ + if stautoGBK && startext.IsGBK([]byte(origin)) { + originByte, _ := startext.GBK2UTF8([]byte(origin)) + origin = string(originByte) + } + origin = strings.TrimSpace(origin) + if max != 0 && len(origin) > max { + continue + } + if min != 0 && len(origin) < min { + continue + } + if strings.Contains(origin, text) { + fmt.Printf("file:%s line:%d matched:%s\n", filepath, count, origin) + } + if err != nil { + break + } + } + } + for _, v := range data { + if v.IsDir() { + searchText(filepath.Join(folder, v.Name()), filematch, text, thread, autoGBK, stMax, stMin) + } + filepath := filepath.Join(folder, v.Name()) + if matched, _ := regexp.MatchString(filematch, filepath); matched { + wg.Add(1) + go searchFn(filepath, text) + } + } + wg.Wait() + return nil +} diff --git a/sftp/sftp.go b/sftp/sftp.go new file mode 100644 index 0000000..60e37b2 --- /dev/null +++ b/sftp/sftp.go @@ -0,0 +1,26 @@ +package sftp + +import ( + "b612.me/starssh" + "os" + "path" + "path/filepath" +) + +func TransferFile(s *starssh.StarSSH, local, remote string, isPull, fullname bool) error { + if !fullname && !isPull { + s.ShellOne("mkdir -p " + remote) + remote = remote + "/" + filepath.Base(local) + } + if !fullname && isPull { + os.MkdirAll(local, 0755) + local = filepath.Join(local, path.Base(remote)) + } + if fullname && isPull { + os.MkdirAll(filepath.Dir(local), 0755) + } + if fullname && !isPull { + os.MkdirAll(path.Dir(remote), 0755) + } + return nil +} diff --git a/split/split.go b/split/split.go new file mode 100644 index 0000000..0f040dd --- /dev/null +++ b/split/split.go @@ -0,0 +1,60 @@ +package split + +import ( + "b612.me/starcrypto" + "fmt" + "strconv" + + "b612.me/starlog" + "b612.me/staros" + + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "split", + Short: "分割文件", + Long: "按字节或文件数分割文件", + Run: func(this *cobra.Command, args []string) { + var src, dst string + var num int + if len(args) == 3 { + src = args[0] + dst = args[1] + num, _ = strconv.Atoi(args[2]) + } else { + src, _ = this.Flags().GetString("src") + dst, _ = this.Flags().GetString("dst") + num, _ = this.Flags().GetInt("num") + } + if !staros.Exists(src) { + starlog.Errorln("源文件不存在") + this.Help() + return + } + if num == 0 { + starlog.Errorln("参数num不合法", "red") + this.Help() + return + } + ok, _ := this.Flags().GetBool("byte") + err := starcrypto.SplitFile(src, dst, num, !ok, func(pect float64) { + if pect == 100 { + fmt.Println("文件已处理:100.000000%") + } else { + fmt.Printf("文件已处理:%f%%\r", pect) + } + }) + if err != nil { + starlog.Errorln(err.Error) + } + + }, +} + +func init() { + Cmd.Flags().StringP("src", "s", "", "源文件地址") + Cmd.Flags().StringP("dst", "d", "./split*.vicque", "目标文件地址,用*替换文件数字") + Cmd.Flags().BoolP("byte", "b", false, "按byte分割") + Cmd.Flags().IntP("num", "n", 0, "分割数/byte数") +} diff --git a/tcping/cmd.go b/tcping/cmd.go new file mode 100644 index 0000000..ae687e1 --- /dev/null +++ b/tcping/cmd.go @@ -0,0 +1,174 @@ +package tcping + +import ( + "fmt" + "github.com/spf13/cobra" + "os" + "os/signal" + "strconv" + "syscall" + "time" +) + +var ( + counter int + timeout string + interval string + sigs chan os.Signal + + httpMode bool + httpHead bool + httpPost bool + httpUA string + + permanent bool + + dnsServer []string +) + +var Cmd = &cobra.Command{ + Use: "tcping", + Short: "tcp/http ping", + Long: "使用进行Tcp或Http协议进行ping探测", + Example: ` + 1. ping over tcp + > tcping google.com + 2. ping over tcp with custom port + > tcping google.com 443 + 3. ping over http + > tcping -H google.com + 4. ping with URI schema + > tcping http://hui.lu + `, + Run: func(cmd *cobra.Command, args []string) { + sigs = make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + if permanent && counter != 4 { + fmt.Println("不能同时指定-t与-c,请检查您的输入") + return + } + if permanent { + counter = 0 + } + if len(args) != 2 && len(args) != 1 { + cmd.Usage() + return + } + host := args[0] + + var ( + err error + port int + schema string + ) + if len(args) == 2 { + port, err = strconv.Atoi(args[1]) + if err != nil { + fmt.Println("端口应当为Int类型") + cmd.Usage() + return + } + schema = TCP.String() + } else { + var matched bool + schema, host, port, matched = CheckURI(host) + if !matched { + fmt.Println("不是一个合法的URI") + cmd.Usage() + return + } + } + var timeoutDuration time.Duration + if res, err := strconv.Atoi(timeout); err == nil { + timeoutDuration = time.Duration(res) * time.Millisecond + } else { + timeoutDuration, err = time.ParseDuration(timeout) + if err != nil { + fmt.Println("parse timeout failed", err) + cmd.Usage() + return + } + } + + var intervalDuration time.Duration + if res, err := strconv.Atoi(interval); err == nil { + intervalDuration = time.Duration(res) * time.Millisecond + } else { + intervalDuration, err = time.ParseDuration(interval) + if err != nil { + fmt.Println("parse interval failed", err) + cmd.Usage() + return + } + } + var protocol Protocol + if httpMode { + protocol = HTTP + } else { + protocol, err = NewProtocol(schema) + if err != nil { + fmt.Println(err) + cmd.Usage() + return + } + } + if len(dnsServer) != 0 { + UseCustomeDNS(dnsServer) + } + + parseHost := FormatIP(host) + target := Target{ + Timeout: timeoutDuration, + Interval: intervalDuration, + Host: parseHost, + Port: port, + Counter: counter, + Protocol: protocol, + } + var pinger Pinger + switch protocol { + case TCP: + pinger = NewTCPing() + case HTTP, HTTPS: + var httpMethod string + switch { + case httpHead: + httpMethod = "HEAD" + case httpPost: + httpMethod = "POST" + default: + httpMethod = "GET" + } + pinger = NewHTTPing(httpMethod) + default: + fmt.Printf("schema: %s not support\n", schema) + cmd.Usage() + return + } + pinger.SetTarget(&target) + pingerDone := pinger.Start() + select { + case <-pingerDone: + break + case <-sigs: + break + } + + fmt.Println(pinger.Result()) + }, +} + +func init() { + Cmd.Flags().IntVarP(&counter, "counter", "c", 4, "ping的次数") + Cmd.Flags().BoolVarP(&permanent, "permanent", "t", false, "一直ping下去") + Cmd.Flags().StringVarP(&timeout, "timeout", "T", "1s", `超时时间, 单位为 "ns", "us" (or "µs"), "ms", "s", "m", "h"`) + Cmd.Flags().StringVarP(&interval, "interval", "I", "1s", `ping间隔时间, 单位为 "ns", "us" (or "µs"), "ms", "s", "m", "h"`) + + Cmd.Flags().BoolVarP(&httpMode, "http", "H", false, `Use "HTTP" mode. will ignore URI Schema, force to http`) + Cmd.Flags().BoolVar(&httpHead, "head", false, `使用http head模式`) + Cmd.Flags().BoolVar(&httpPost, "post", false, `使用http post模式`) + Cmd.Flags().StringVar(&httpUA, "user-agent", "victorique/tcping", `自定义UA`) + + Cmd.Flags().StringArrayVarP(&dnsServer, "dns-server", "D", nil, `使用自定义DNS服务器`) + +} diff --git a/tcping/fqdn.go b/tcping/fqdn.go new file mode 100644 index 0000000..6031f6e --- /dev/null +++ b/tcping/fqdn.go @@ -0,0 +1,17 @@ +package tcping + +import "net" + +// GetIP ... +func GetIP(hostname string) string { + addrs, err := net.LookupIP(hostname) + if err != nil { + return "" + } + for _, addr := range addrs { + if ipv4 := addr.To4(); ipv4 != nil { + return ipv4.String() + } + } + return "" +} diff --git a/tcping/http.go b/tcping/http.go new file mode 100644 index 0000000..bc69d9b --- /dev/null +++ b/tcping/http.go @@ -0,0 +1,120 @@ +package tcping + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/http/httptrace" + "time" +) + +// HTTPing ... +type HTTPing struct { + target *Target + done chan struct{} + result *Result + Method string +} + +var _ Pinger = (*HTTPing)(nil) + +// NewHTTPing return new HTTPing +func NewHTTPing(method string) *HTTPing { + return &HTTPing{ + done: make(chan struct{}), + Method: method, + } +} + +// SetTarget ... +func (ping *HTTPing) SetTarget(target *Target) { + ping.target = target + if ping.result == nil { + ping.result = &Result{Target: target} + } +} + +// Start ping +func (ping *HTTPing) Start() <-chan struct{} { + go func() { + t := time.NewTicker(ping.target.Interval) + defer t.Stop() + for { + select { + case <-t.C: + if ping.result.Counter >= ping.target.Counter && ping.target.Counter != 0 { + ping.Stop() + return + } + duration, resp, remoteAddr, err := ping.ping() + ping.result.Counter++ + + if err != nil { + fmt.Printf("Ping %s - failed: %s\n", ping.target, err) + } else { + defer resp.Body.Close() + length, _ := io.Copy(ioutil.Discard, resp.Body) + fmt.Printf("Ping %s(%s) - %s is open - time=%s method=%s status=%d bytes=%d\n", ping.target, remoteAddr, ping.target.Protocol, duration, ping.Method, resp.StatusCode, length) + if ping.result.MinDuration == 0 { + ping.result.MinDuration = duration + } + if ping.result.MaxDuration == 0 { + ping.result.MaxDuration = duration + } + ping.result.SuccessCounter++ + if duration > ping.result.MaxDuration { + ping.result.MaxDuration = duration + } else if duration < ping.result.MinDuration { + ping.result.MinDuration = duration + } + ping.result.TotalDuration += duration + } + case <-ping.done: + return + } + } + }() + return ping.done +} + +// Result return ping result +func (ping *HTTPing) Result() *Result { + return ping.result +} + +// Stop the tcping +func (ping *HTTPing) Stop() { + ping.done <- struct{}{} +} + +func (ping HTTPing) ping() (time.Duration, *http.Response, string, error) { + var resp *http.Response + var body io.Reader + if ping.Method == "POST" { + body = bytes.NewBufferString("{}") + } + req, err := http.NewRequest(ping.Method, ping.target.String(), body) + req.Header.Set(http.CanonicalHeaderKey("User-Agent"), "tcping") + if err != nil { + return 0, nil, "", err + } + var remoteAddr string + trace := &httptrace.ClientTrace{ + ConnectStart: func(network, addr string) { + remoteAddr = addr + }, + } + req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace)) + duration, errIfce := timeIt(func() interface{} { + client := http.Client{Timeout: ping.target.Timeout} + resp, err = client.Do(req) + return err + }) + if errIfce != nil { + err := errIfce.(error) + return 0, nil, "", err + } + return time.Duration(duration), resp, remoteAddr, nil +} diff --git a/tcping/ping.go b/tcping/ping.go new file mode 100644 index 0000000..6545763 --- /dev/null +++ b/tcping/ping.go @@ -0,0 +1,149 @@ +package tcping + +import ( + "bytes" + "fmt" + "html/template" + "regexp" + "strconv" + "strings" + "time" +) + +// Protocol ... +type Protocol int + +func (protocol Protocol) String() string { + switch protocol { + case TCP: + return "tcp" + case HTTP: + return "http" + case HTTPS: + return "https" + } + return "unkown" +} + +const ( + // TCP is tcp protocol + TCP Protocol = iota + // HTTP is http protocol + HTTP + // HTTPS is https protocol + HTTPS +) + +// NewProtocol convert protocol stirng to Protocol +func NewProtocol(protocol string) (Protocol, error) { + switch strings.ToLower(protocol) { + case TCP.String(): + return TCP, nil + case HTTP.String(): + return HTTP, nil + case HTTPS.String(): + return HTTPS, nil + } + return 0, fmt.Errorf("protocol %s not support", protocol) +} + +// Target is a ping +type Target struct { + Protocol Protocol + Host string + Port int + + Counter int + Interval time.Duration + Timeout time.Duration +} + +func (target Target) String() string { + return fmt.Sprintf("%s://%s:%d", target.Protocol, target.Host, target.Port) +} + +// Pinger is a ping interface +type Pinger interface { + Start() <-chan struct{} + Stop() + Result() *Result + SetTarget(target *Target) +} + +// Ping is a ping interface +type Ping interface { + Start() <-chan struct{} + + Host() string + Port() int + Protocol() Protocol + Counter() int + + Stop() + + Result() Result +} + +// Result ... +type Result struct { + Counter int + SuccessCounter int + Target *Target + + MinDuration time.Duration + MaxDuration time.Duration + TotalDuration time.Duration +} + +// Avg return the average time of ping +func (result Result) Avg() time.Duration { + if result.SuccessCounter == 0 { + return 0 + } + return result.TotalDuration / time.Duration(result.SuccessCounter) +} + +// Failed return failed counter +func (result Result) Failed() int { + return result.Counter - result.SuccessCounter +} + +func (result Result) String() string { + const resultTpl = ` +Ping statistics {{.Target}} + {{.Counter}} probes sent. + {{.SuccessCounter}} successful, {{.Failed}} failed. +Approximate trip times: + Minimum = {{.MinDuration}}, Maximum = {{.MaxDuration}}, Average = {{.Avg}}` + t := template.Must(template.New("result").Parse(resultTpl)) + res := bytes.NewBufferString("") + t.Execute(res, result) + return res.String() +} + +// CheckURI check uri +func CheckURI(uri string) (schema, host string, port int, matched bool) { + const reExp = `^((?P((ht|f)tp(s?))|tcp)\://)?((([a-zA-Z0-9_\-]+\.)+[a-zA-Z]{2,})|((?:(?:25[0-5]|2[0-4]\d|[01]\d\d|\d?\d)((\.?\d)\.)){4})|(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9]))(:([0-9]+))?(/[a-zA-Z0-9\-\._\?\,\'/\\\+&%\$#\=~]*)?$` + pattern := regexp.MustCompile(reExp) + res := pattern.FindStringSubmatch(uri) + if len(res) == 0 { + return + } + matched = true + schema = res[2] + if schema == "" { + schema = "tcp" + } + host = res[6] + if res[17] == "" { + if schema == HTTPS.String() { + port = 443 + } else { + port = 80 + } + } else { + port, _ = strconv.Atoi(res[17]) + } + + return +} diff --git a/tcping/tcp.go b/tcping/tcp.go new file mode 100644 index 0000000..820b290 --- /dev/null +++ b/tcping/tcp.go @@ -0,0 +1,102 @@ +package tcping + +import ( + "fmt" + "net" + "time" +) + +// TCPing ... +type TCPing struct { + target *Target + done chan struct{} + result *Result +} + +var _ Pinger = (*TCPing)(nil) + +// NewTCPing return a new TCPing +func NewTCPing() *TCPing { + tcping := TCPing{ + done: make(chan struct{}), + } + return &tcping +} + +// SetTarget set target for TCPing +func (tcping *TCPing) SetTarget(target *Target) { + tcping.target = target + if tcping.result == nil { + tcping.result = &Result{Target: target} + } +} + +// Result return the result +func (tcping TCPing) Result() *Result { + return tcping.result +} + +// Start a tcping +func (tcping TCPing) Start() <-chan struct{} { + go func() { + t := time.NewTicker(tcping.target.Interval) + defer t.Stop() + for { + select { + case <-t.C: + if tcping.result.Counter >= tcping.target.Counter && tcping.target.Counter != 0 { + tcping.Stop() + return + } + duration, remoteAddr, err := tcping.ping() + tcping.result.Counter++ + + if err != nil { + fmt.Printf("Ping %s - failed: %s\n", tcping.target, err) + } else { + fmt.Printf("Ping %s(%s) - Connected - time=%s\n", tcping.target, remoteAddr, duration) + + if tcping.result.MinDuration == 0 { + tcping.result.MinDuration = duration + } + if tcping.result.MaxDuration == 0 { + tcping.result.MaxDuration = duration + } + tcping.result.SuccessCounter++ + if duration > tcping.result.MaxDuration { + tcping.result.MaxDuration = duration + } else if duration < tcping.result.MinDuration { + tcping.result.MinDuration = duration + } + tcping.result.TotalDuration += duration + } + case <-tcping.done: + return + } + } + }() + return tcping.done +} + +// Stop the tcping +func (tcping *TCPing) Stop() { + tcping.done <- struct{}{} +} + +func (tcping TCPing) ping() (time.Duration, net.Addr, error) { + var remoteAddr net.Addr + duration, errIfce := timeIt(func() interface{} { + conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", tcping.target.Host, tcping.target.Port), tcping.target.Timeout) + if err != nil { + return err + } + remoteAddr = conn.RemoteAddr() + conn.Close() + return nil + }) + if errIfce != nil { + err := errIfce.(error) + return 0, remoteAddr, err + } + return time.Duration(duration), remoteAddr, nil +} diff --git a/tcping/utils.go b/tcping/utils.go new file mode 100644 index 0000000..c152586 --- /dev/null +++ b/tcping/utils.go @@ -0,0 +1,51 @@ +package tcping + +import ( + "context" + "fmt" + "net" + "strings" + "time" +) + +func timeIt(f func() interface{}) (int64, interface{}) { + startAt := time.Now() + res := f() + endAt := time.Now() + return endAt.UnixNano() - startAt.UnixNano(), res +} + +// UseCustomeDNS will set the dns to default DNS resolver for global +func UseCustomeDNS(dns []string) { + resolver := net.Resolver{ + PreferGo: true, + Dial: func(ctx context.Context, network, address string) (conn net.Conn, err error) { + for _, addr := range dns { + if conn, err = net.Dial("udp", addr+":53"); err != nil { + continue + } else { + return conn, nil + } + } + return + }, + } + net.DefaultResolver = &resolver +} + +// FormatIP - trim spaces and format IP +// +// IP - the provided IP +// +// string - return "" if the input is neither valid IPv4 nor valid IPv6 +// return IPv4 in format like "192.168.9.1" +// return IPv6 in format like "[2002:ac1f:91c5:1::bd59]" +func FormatIP(IP string) string { + + host := strings.Trim(IP, "[ ]") + if parseIP := net.ParseIP(host); parseIP != nil && parseIP.To4() == nil { + host = fmt.Sprintf("[%s]", host) + } + + return host +} diff --git a/uac/cmd.go b/uac/cmd.go new file mode 100644 index 0000000..fdab72a --- /dev/null +++ b/uac/cmd.go @@ -0,0 +1,54 @@ +//go:build windows +// +build windows + +package uac + +import ( + "b612.me/starlog" + "github.com/spf13/cobra" + "os" + "path/filepath" +) + +var fpath string +var fargs string +var workdir string +var showWindow bool + +func init() { + Cmd.Flags().StringVarP(&fpath, "path", "p", "", "filepath/文件路径") + Cmd.Flags().StringVarP(&fargs, "args", "a", "", "args use space to split/参数,空格分隔") + Cmd.Flags().StringVarP(&workdir, "workdir", "d", "./", "workdir path") + Cmd.Flags().BoolVarP(&showWindow, "hide-window", "w", false, "hide the window show") +} + +var Cmd = &cobra.Command{ + Use: "uac", + Short: "run process with administrator permission", + Example: "vtqe uac 'c:\\program.exe arg1 arg2'", + Version: "2.0.0", + Run: func(cmd *cobra.Command, args []string) { + showWindow = !showWindow + workdir, _ = filepath.Abs(workdir) + if fpath == "" && len(args) == 0 { + starlog.Errorln("Please enter a filepath") + os.Exit(1) + } + if fpath != "" { + err := RunAsUacSimple(fpath, fargs, workdir, showWindow) + if err != nil { + starlog.Errorln("StartAsUac Failed", err) + os.Exit(2) + } + return + } + if len(args) > 0 { + err := RunAsUac(args[0], workdir, showWindow) + if err != nil { + starlog.Errorln("StartAsUac Failed", err) + os.Exit(2) + } + return + } + }, +} diff --git a/uac/uac.go b/uac/uac.go new file mode 100644 index 0000000..bb64f73 --- /dev/null +++ b/uac/uac.go @@ -0,0 +1,75 @@ +//go:build windows + +package uac + +import ( + "b612.me/wincmd" + "strings" +) + +func RunAsUacSimple(fpath string, cmdArgs string, workDir string, showWindow bool) error { + intShow := 0 + if showWindow { + intShow = 1 + } + return wincmd.StartProcess(fpath, cmdArgs, workDir, true, intShow) +} + +func RunAsUac(cmdLine, workDir string, showWindow bool) error { + intShow := 0 + if showWindow { + intShow = 1 + } + fpath, args := getPathArgsFromString(cmdLine) + return wincmd.StartProcess(fpath, strings.Join(args, " "), workDir, true, intShow) +} + +func getPathArgsFromString(cmdLine string) (string, []string) { + var fpath string + var cmdArgs []string + var markStart rune = -1 + + var tmp []rune + var lastRune rune + cmdLine = strings.TrimSpace(cmdLine) + for k, v := range cmdLine { + if v == ' ' || v == '"' || v == '\'' { + if k == 0 { + markStart = v + continue + } + if markStart == v && v == lastRune { + continue + } + if lastRune != '\\' { + lastRune = v + if 0 == markStart { + markStart = v + continue + } else if markStart == v || markStart == -1 { + markStart = 0 + if v == ' ' { + markStart = v + } + if fpath == "" { + fpath = string(tmp) + } else { + cmdArgs = append(cmdArgs, string(tmp)) + } + tmp = []rune{} + continue + } + } + } + lastRune = v + tmp = append(tmp, v) + } + if len(tmp) != 0 { + if fpath == "" { + fpath = string(tmp) + } else { + cmdArgs = append(cmdArgs, string(tmp)) + } + } + return fpath, cmdArgs +} diff --git a/uac/uac_nowindows.go b/uac/uac_nowindows.go new file mode 100644 index 0000000..bbe4609 --- /dev/null +++ b/uac/uac_nowindows.go @@ -0,0 +1,12 @@ +//go:build !windows + +package uac + +import "github.com/spf13/cobra" + +var Cmd = &cobra.Command{ + Use: "uac", + Short: "run process with administrator permission", + Example: "vtqe uac 'c:\\program.exe arg1 arg2'", + Hidden: true, +} diff --git a/vic/vic.go b/vic/vic.go new file mode 100644 index 0000000..956b619 --- /dev/null +++ b/vic/vic.go @@ -0,0 +1,93 @@ +package vic + +import ( + "b612.me/starcrypto" + "fmt" + "os" + "regexp" + + "b612.me/starlog" + "github.com/spf13/cobra" +) + +var Cmd = &cobra.Command{ + Use: "vicque", + Short: "嵐を乗り越えて", + Long: "あの子の未来を照らし出せ", + Run: func(this *cobra.Command, args []string) { + var err error + ok, _ := this.Flags().GetBool("file") + de, _ := this.Flags().GetBool("decode") + pwd, _ := this.Flags().GetString("key") + rep, _ := this.Flags().GetBool("replace") + ext, _ := this.Flags().GetBool("extension") + if len(args) != 2 || args[1] != "sakura" { + starlog.Errorln("ヴィクトリカだけが使えるよ") + return + } + shell := func(pect float64) { + if pect == 100 { + fmt.Println("已处理:100.000000%") + } else { + fmt.Printf("已处理:%f%%\r", pect) + } + } + if ok { + path, _ := this.Flags().GetString("path") + if !de { + err = starcrypto.VicqueEncodeV1File(args[0], path, pwd, shell) + if err == nil { + if rep { + os.Remove(args[0]) + os.Rename(path, args[0]) + path = args[0] + } + if ext { + os.Rename(path, path+".victorique") + } + } + } else { + err = starcrypto.VicqueDecodeV1File(args[0], path, pwd, shell) + if err == nil { + if rep { + os.Remove(args[0]) + os.Rename(path, args[0]) + path = args[0] + } + if ext { + reg := regexp.MustCompile(`(.*?)\.victorique$`) + if reg.MatchString(path) { + paths := reg.FindStringSubmatch(path) + os.Rename(path, paths[1]) + } + } + } + } + } else { + if !de { + data := starcrypto.VicqueEncodeV1([]byte(args[0]), pwd) + fmt.Println(data) + fmt.Println(starcrypto.Base64Encode(data)) + } else { + var data []byte + src, _ := starcrypto.Base64Decode(args[0]) + data = starcrypto.VicqueDecodeV1(src, pwd) + fmt.Println(string(data)) + } + } + if err != nil { + starlog.Errorln(err) + return + } + }, +} + +func init() { + Cmd.Flags().BoolP("file", "f", false, "VICQUE处理文件") + Cmd.Flags().StringP("path", "p", "./v64.encode", "指定处理地址,默认为./v64.encode") + Cmd.Flags().BoolP("decode", "d", false, "VICQUE解码") + Cmd.Flags().StringP("key", "k", "", "密钥") + Cmd.Flags().BoolP("replace", "r", false, "覆盖原文件") + Cmd.Flags().BoolP("extension", "e", false, "添加/取消.victorique后缀") + Cmd.MarkFlagRequired("key") +}