package main import ( "errors" "fmt" "io" "io/ioutil" "log" "os" "path/filepath" "strings" "time" humanize "github.com/dustin/go-humanize" "github.com/schollz/croc/src/croc" "github.com/schollz/croc/src/utils" "github.com/urfave/cli" ) var version string var codePhrase string var cr *croc.Croc func main() { app := cli.NewApp() app.Name = "croc" if version == "" { version = "dev" } app.Version = version app.Compiled = time.Now() app.Usage = "easily and securely transfer stuff from one computer to another" app.UsageText = "croc allows any two computers to directly and securely transfer files" // app.ArgsUsage = "[args and such]" app.Commands = []cli.Command{ { Name: "send", Usage: "send a file", 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", Action: func(c *cli.Context) error { 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) }, }, } app.Flags = []cli.Flag{ cli.StringFlag{Name: "addr", Value: "198.199.67.130", Usage: "address of the public relay"}, cli.StringFlag{Name: "addr-ws", Value: "8153", Usage: "port of the public relay websocket server to connect"}, cli.StringFlag{Name: "addr-tcp", Value: "8154,8155,8156,8157", Usage: "tcp ports of the public relay serer to connect"}, cli.BoolFlag{Name: "no-local", Usage: "disable local mode"}, cli.BoolFlag{Name: "local", Usage: "use only local mode"}, 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.BoolFlag{Name: "force-tcp", Usage: "force TCP"}, cli.BoolFlag{Name: "force-web", Usage: "force websockets"}, cli.StringFlag{Name: "port", Value: "8153", Usage: "port that the websocket listens on"}, cli.StringFlag{Name: "tcp-port", Value: "8154,8155,8156,8157", Usage: "ports that the tcp server listens on"}, cli.StringFlag{Name: "curve", Value: "siec", Usage: "specify elliptic curve to use (p224, p256, p384, p521, siec)"}, } app.EnableBashCompletion = true app.HideHelp = false app.HideVersion = false app.BashComplete = func(c *cli.Context) { fmt.Fprintf(c.App.Writer, "send\nreceive\relay") } app.Action = func(c *cli.Context) error { return receive(c) } app.Before = func(c *cli.Context) error { cr = croc.Init(c.GlobalBool("debug")) 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 } err := app.Run(os.Args) if err != nil { fmt.Printf("\nerror: %s", err.Error()) } } func send(c *cli.Context) error { stat, _ := os.Stdin.Stat() var fname string if (stat.Mode() & os.ModeCharDevice) == 0 { f, err := ioutil.TempFile(".", "croc-stdin-") if err != nil { return err } _, err = io.Copy(f, os.Stdin) if err != nil { return err } err = f.Close() if err != nil { return err } fname = f.Name() defer func() { err = os.Remove(fname) if err != nil { log.Println(err) } }() } else { fname = c.Args().First() } if fname == "" { 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") != "" { codePhrase = c.String("code") } if len(codePhrase) == 0 { // generate code phrase codePhrase = 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) if err != nil { return err } } 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, codePhrase, codePhrase, ) return cr.Send(fname, codePhrase) } func receive(c *cli.Context) error { if c.GlobalString("code") != "" { codePhrase = c.GlobalString("code") } if c.Args().First() != "" { codePhrase = c.Args().First() } if codePhrase == "" { codePhrase = utils.GetInput("Enter receive code: ") } return cr.Receive(codePhrase) } 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 }