commit 1ea024e28f42d77ff697dd773fe1f130560d3c08
Author: ren yuze
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(`
+ 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\nB612 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
`, 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")
+}