diff --git a/go.mod b/go.mod index a684fabc..7ea3e3ff 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/schollz/spinner v0.0.0-20180925172146-6bbc5f7804f9 github.com/sirupsen/logrus v1.4.1 github.com/stretchr/testify v1.3.0 + github.com/urfave/cli v1.20.0 golang.org/x/crypto v0.0.0-20190404164418-38d8ce5564a5 golang.org/x/net v0.0.0-20181220203305-927f97764cc3 // indirect gopkg.in/urfave/cli.v1 v1.20.0 diff --git a/go.sum b/go.sum index 573aedb2..8102c554 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,7 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/schollz/croc v3.0.6+incompatible h1:rCfc8MGgcGjNW2/qSoulPh8CRGH+Ej4i3RWYOwhX9pE= github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU= github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY= github.com/schollz/pake v1.1.0 h1:+tYqsPVkuirFpmeRePjYTUhIHHKLufdmd7QfuspaXCk= @@ -121,6 +122,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/tscholl2/siec v0.0.0-20180721101609-21667da05937 h1:lhssCpSe3TjKcbvUoPzFMuv9oUyZDgI3Cmgolfw2C90= github.com/tscholl2/siec v0.0.0-20180721101609-21667da05937/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw= +github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190123085648-057139ce5d2b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= diff --git a/main.go b/main.go index d283215c..d756c3df 100644 --- a/main.go +++ b/main.go @@ -1,10 +1,9 @@ package main import ( - "flag" "fmt" - "github.com/schollz/croc/v5/src/croc" + "github.com/schollz/croc/v5/src/cli" ) func main() { @@ -24,26 +23,29 @@ func main() { // } // log.Debugln(bytes.Equal(buffer[:bytesread], emptyBuffer[:bytesread])) // } - var sender bool - flag.BoolVar(&sender, "sender", false, "sender") - flag.Parse() - c, err := croc.New(sender, "foo") - if err != nil { - panic(err) - } - if sender { - err = c.Send(croc.TransferOptions{ - // PathToFile: "../wskeystore/README.md", - // PathToFile: "./src/croc/croc.go", - // PathToFiles: []string{"C:\\Users\\zacks\\go\\src\\github.com\\schollz\\croc\\src\\croc\\croc.go", "croc.exe"}, - PathToFiles: []string{"croc2.exe", "croc3.exe"}, //,"croc2.exe", "croc3.exe"}, - //PathToFiles: []string{"README.md"}, //,"croc2.exe", "croc3.exe"}, - KeepPathInRemote: false, - }) - } else { - err = c.Receive() - } - if err != nil { + // var sender bool + // flag.BoolVar(&sender, "sender", false, "sender") + // flag.Parse() + // c, err := croc.New(sender, "foo") + // if err != nil { + // panic(err) + // } + // if sender { + // err = c.Send(croc.TransferOptions{ + // // PathToFile: "../wskeystore/README.md", + // // PathToFile: "./src/croc/croc.go", + // // PathToFiles: []string{"C:\\Users\\zacks\\go\\src\\github.com\\schollz\\croc\\src\\croc\\croc.go", "croc.exe"}, + // PathToFiles: []string{"croc2.exe", "croc3.exe"}, //,"croc2.exe", "croc3.exe"}, + // //PathToFiles: []string{"README.md"}, //,"croc2.exe", "croc3.exe"}, + // KeepPathInRemote: false, + // }) + // } else { + // err = c.Receive() + // } + // if err != nil { + // fmt.Println(err) + // } + if err := cli.Run(); err != nil { fmt.Println(err) } } diff --git a/src/cli/cli.go b/src/cli/cli.go index 47d51969..46b8b048 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "io" "io/ioutil" @@ -11,16 +12,16 @@ import ( "strings" "time" - humanize "github.com/dustin/go-humanize" - "github.com/pkg/errors" + "github.com/schollz/croc/v5/src/croc" "github.com/schollz/croc/v5/src/utils" - "github.com/skratchdot/open-golang/open" "github.com/urfave/cli" ) var Version string +var cr *croc.Client + +func Run() (err error) { -func Run() { runtime.GOMAXPROCS(runtime.NumCPU()) app := cli.NewApp() app.Name = "croc" @@ -40,8 +41,6 @@ func Run() { Description: "send a file over the relay", ArgsUsage: "[filename]", Flags: []cli.Flag{ - cli.BoolFlag{Name: "no-compress, o", Usage: "disable compression"}, - cli.BoolFlag{Name: "no-encrypt, e", Usage: "disable encryption"}, cli.StringFlag{Name: "code, c", Usage: "codephrase used to connect to relay"}, }, HelpName: "croc send", @@ -49,32 +48,12 @@ func Run() { return send(c) }, }, - { - Name: "relay", - Usage: "start a croc relay", - Description: "the croc relay will handle websocket and TCP connections", - Flags: []cli.Flag{}, - HelpName: "croc relay", - Action: func(c *cli.Context) error { - return relay(c) - }, - }, - { - Name: "config", - Usage: "generates a config file", - Description: "the croc config can be used to set static parameters", - Flags: []cli.Flag{}, - HelpName: "croc config", - Action: func(c *cli.Context) error { - return saveDefaultConfig(c) - }, - }, } app.Flags = []cli.Flag{ cli.BoolFlag{Name: "debug", Usage: "increase verbosity (a lot)"}, cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"}, cli.BoolFlag{Name: "stdout", Usage: "redirect file to stdout"}, - cli.StringFlag{Name: "port", Value: "8153", Usage: "port that the websocket listens on"}, + cli.StringFlag{Name: "relay", Value: "198.199.67.130:6372", Usage: "address of the relay"}, cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"}, } app.EnableBashCompletion = true @@ -95,43 +74,26 @@ func Run() { return receive(c) } app.Before = func(c *cli.Context) error { - cr = croc.Init(c.GlobalBool("debug")) - cr.Version = Version - cr.AllowLocalDiscovery = true - cr.Address = c.GlobalString("addr") - cr.AddressTCPPorts = strings.Split(c.GlobalString("addr-tcp"), ",") - cr.AddressWebsocketPort = c.GlobalString("addr-ws") - cr.NoRecipientPrompt = c.GlobalBool("yes") - cr.Stdout = c.GlobalBool("stdout") - cr.LocalOnly = c.GlobalBool("local") - cr.NoLocal = c.GlobalBool("no-local") - cr.ShowText = true - cr.RelayWebsocketPort = c.String("port") - cr.RelayTCPPorts = strings.Split(c.String("tcp-port"), ",") - cr.CurveType = c.String("curve") - if c.GlobalBool("force-tcp") { - cr.ForceSend = 2 - } - if c.GlobalBool("force-web") { - cr.ForceSend = 1 - } - return nil + var err error + cr, err = croc.New(croc.Options{ + Debug: c.GlobalBool("debug"), + NoPrompt: c.GlobalBool("yes"), + AddressRelay: c.GlobalString("relay"), + Stdout: c.GlobalBool("stdout"), + }) + return err } - err := app.Run(os.Args) - if err != nil { - fmt.Fprintf(os.Stderr, "\r\n%s", err.Error()) - } - fmt.Fprintf(os.Stderr, "\r\n") + return app.Run(os.Args) } -func saveDefaultConfig(c *cli.Context) error { - return croc.SaveDefaultConfig() -} +// func saveDefaultConfig(c *cli.Context) error { +// return croc.SaveDefaultConfig() +// } -func send(c *cli.Context) error { +func send(c *cli.Context) (err error) { + var fnames []string stat, _ := os.Stdin.Stat() - var fname string if (stat.Mode() & os.ModeCharDevice) == 0 { f, err := ioutil.TempFile(".", "croc-stdin-") if err != nil { @@ -145,102 +107,91 @@ func send(c *cli.Context) error { if err != nil { return err } - fname = f.Name() + fnames = []string{f.Name()} defer func() { - err = os.Remove(fname) + err = os.Remove(fnames[0]) if err != nil { log.Println(err) } }() } else { - fname = c.Args().First() + fnames = append([]string{c.Args().First()}, c.Args().Tail()...) } - if fname == "" { + if len(fnames) == 0 { return errors.New("must specify file: croc send [filename]") } - cr.UseCompression = !c.Bool("no-compress") - cr.UseEncryption = !c.Bool("no-encrypt") + if c.String("code") != "" { - cr.Codephrase = c.String("code") + cr.Options.SharedSecret = c.String("code") } - cr.LoadConfig() - if len(cr.Codephrase) == 0 { + // cr.LoadConfig() + if len(cr.Options.SharedSecret) == 0 { // generate code phrase - cr.Codephrase = utils.GetRandomName() + cr.Options.SharedSecret = utils.GetRandomName() } - // print the text - finfo, err := os.Stat(fname) - if err != nil { - return err - } - fname, _ = filepath.Abs(fname) - fname = filepath.Clean(fname) - _, filename := filepath.Split(fname) - fileOrFolder := "file" - fsize := finfo.Size() - if finfo.IsDir() { - fileOrFolder = "folder" - fsize, err = dirSize(fname) + paths := []string{} + for _, fname := range fnames { + stat, err := os.Stat(fname) if err != nil { return err } + if stat.IsDir() { + err = filepath.Walk(fname, + func(pathName string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() { + paths = append(paths, filepath.ToSlash(pathName)) + } + return nil + }) + if err != nil { + return err + } + } else { + paths = append(paths, filepath.ToSlash(fname)) + } } - fmt.Fprintf(os.Stderr, - "Sending %s %s named '%s'\nCode is: %s\nOn the other computer, please run:\n\ncroc %s\n\n", - humanize.Bytes(uint64(fsize)), - fileOrFolder, - filename, - cr.Codephrase, - cr.Codephrase, - ) - if cr.Debug { - croc.SetDebugLevel("debug") - } - return cr.Send(fname, cr.Codephrase) + + err = cr.Send(croc.TransferOptions{ + PathToFiles: paths, + KeepPathInRemote: false, + }) + + return } -func receive(c *cli.Context) error { +func receive(c *cli.Context) (err error) { if c.GlobalString("code") != "" { - cr.Codephrase = c.GlobalString("code") + cr.Options.SharedSecret = c.GlobalString("code") } if c.Args().First() != "" { - cr.Codephrase = c.Args().First() + cr.Options.SharedSecret = c.Args().First() + } + if cr.Options.SharedSecret == "" { + cr.Options.SharedSecret = utils.GetInput("Enter receive code: ") } if c.GlobalString("out") != "" { os.Chdir(c.GlobalString("out")) } - cr.LoadConfig() - openFolder := false - if len(os.Args) == 1 { - // open folder since they didn't give any arguments - openFolder = true - } - if cr.Codephrase == "" { - cr.Codephrase = utils.GetInput("Enter receive code: ") - } - if cr.Debug { - croc.SetDebugLevel("debug") - } - err := cr.Receive(cr.Codephrase) - if err == nil && openFolder { - cwd, _ := os.Getwd() - open.Run(cwd) - } - return err + + err = cr.Receive() + return } -func relay(c *cli.Context) error { - return cr.Relay() -} +// func relay(c *cli.Context) error { +// return cr.Relay() +// } -func dirSize(path string) (int64, error) { - var size int64 - err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { - if !info.IsDir() { - size += info.Size() - } - return err - }) - return size, err -} +// func dirSize(path string) (int64, error) { +// var size int64 +// err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { +// if !info.IsDir() { +// size += info.Size() +// } +// return err +// }) +// return size, err +// } diff --git a/src/croc/croc.go b/src/croc/croc.go index 164032e5..a11dbfc8 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -54,11 +54,9 @@ func Debug(debug bool) { type Client struct { Options Options // basic setup - redisdb *redis.Client - log *logrus.Entry - IsSender bool - SharedSecret string - Pake *pake.Pake + redisdb *redis.Client + log *logrus.Entry + Pake *pake.Pake // steps involved in forming relationship Step1ChannelSecured bool @@ -147,20 +145,22 @@ func New(ops Options) (c *Client, err error) { c = new(Client) // setup basic info - c.IsSender = ops.Sender - c.SharedSecret = ops.SharedSecret - c.SharedSecret = ops.SharedSecret - if sender { - c.nameOutChannel = c.SharedSecret + "2" - c.nameInChannel = c.SharedSecret + "1" + c.Options = ops + + // set channels + if c.Options.IsSender { + c.nameOutChannel = c.Options.SharedSecret + "2" + c.nameInChannel = c.Options.SharedSecret + "1" } else { - c.nameOutChannel = c.SharedSecret + "1" - c.nameInChannel = c.SharedSecret + "2" + c.nameOutChannel = c.Options.SharedSecret + "1" + c.nameInChannel = c.Options.SharedSecret + "2" } + Debug(c.Options.Debug) + // initialize redis for communication in establishing channel c.redisdb = redis.NewClient(&redis.Options{ - Addr: "198.199.67.130:6372", + Addr: c.Options.AddressRelay, Password: "", DB: 4, WriteTimeout: 1 * time.Hour, @@ -180,10 +180,10 @@ func New(ops Options) (c *Client, err error) { c.incomingMessageChannel = pubsub.Channel() // initialize pake - if c.IsSender { - c.Pake, err = pake.Init([]byte{1, 2, 3}, 1, elliptic.P521(), 1*time.Microsecond) + if c.Options.IsSender { + c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 1, elliptic.P521(), 1*time.Microsecond) } else { - c.Pake, err = pake.Init([]byte{1, 2, 3}, 0, elliptic.P521(), 1*time.Microsecond) + c.Pake, err = pake.Init([]byte(c.Options.SharedSecret), 0, elliptic.P521(), 1*time.Microsecond) } if err != nil { return @@ -193,7 +193,7 @@ func New(ops Options) (c *Client, err error) { c.log = log.WithFields(logrus.Fields{ "is": "sender", }) - if !c.IsSender { + if !c.Options.IsSender { c.log = log.WithFields(logrus.Fields{ "is": "recipient", }) @@ -223,7 +223,7 @@ func (c *Client) Receive() (err error) { } func (c *Client) transfer(options TransferOptions) (err error) { - if c.IsSender { + if c.Options.IsSender { c.FilesToTransfer = make([]FileInfo, len(options.PathToFiles)) totalFilesSize := int64(0) for i, pathToFile := range options.PathToFiles { @@ -299,7 +299,7 @@ func (c *Client) transfer(options TransferOptions) (err error) { // if recipient, initialize with sending pake information c.log.Debug("ready") - if !c.IsSender && !c.Step1ChannelSecured { + if !c.Options.IsSender && !c.Step1ChannelSecured { err = c.redisdb.Publish(c.nameOutChannel, Message{ Type: "pake", Bytes: c.Pake.Bytes(), @@ -392,7 +392,7 @@ func (c *Client) processMessage(m Message) (err error) { if err != nil { return } - if (notVerified && c.Pake.IsVerified() && !c.IsSender) || !c.Pake.IsVerified() { + if (notVerified && c.Pake.IsVerified() && !c.Options.IsSender) || !c.Pake.IsVerified() { err = c.redisdb.Publish(c.nameOutChannel, Message{ Type: "pake", Bytes: c.Pake.Bytes(), @@ -516,7 +516,7 @@ func (c *Client) processMessage(m Message) (err error) { } func (c *Client) updateState() (err error) { - if c.IsSender && c.Step1ChannelSecured && !c.Step2FileInfoTransfered { + if c.Options.IsSender && c.Step1ChannelSecured && !c.Step2FileInfoTransfered { var b []byte b, err = json.Marshal(SenderInfo{ MachineID: c.machineID, @@ -536,7 +536,7 @@ func (c *Client) updateState() (err error) { } c.Step2FileInfoTransfered = true } - if !c.IsSender && c.Step2FileInfoTransfered && !c.Step3RecipientRequestFile { + if !c.Options.IsSender && c.Step2FileInfoTransfered && !c.Step3RecipientRequestFile { // find the next file to transfer and send that number // if the files are the same size, then look for missing chunks finished := true @@ -585,7 +585,7 @@ func (c *Client) updateState() (err error) { c.Step3RecipientRequestFile = true err = c.dataChannelReceive() } - if c.IsSender && c.Step3RecipientRequestFile && !c.Step4FileTransfer { + if c.Options.IsSender && c.Step3RecipientRequestFile && !c.Step4FileTransfer { c.log.Debug("start sending data!") err = c.dataChannelSend() c.Step4FileTransfer = true