mirror of
https://github.com/schollz/croc.git
synced 2025-10-11 13:21:00 +02:00
broken, but add cli to fix
This commit is contained in:
parent
93a3ec70e7
commit
724de7e3c2
2 changed files with 260 additions and 48 deletions
246
src/cli/cli.go
Normal file
246
src/cli/cli.go
Normal file
|
@ -0,0 +1,246 @@
|
||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
humanize "github.com/dustin/go-humanize"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/schollz/croc/v5/src/utils"
|
||||||
|
"github.com/skratchdot/open-golang/open"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Version string
|
||||||
|
|
||||||
|
func Run() {
|
||||||
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: "out", Value: ".", Usage: "specify an output folder to receive the file"},
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
// if trying to send but forgot send, let the user know
|
||||||
|
if c.Args().First() != "" && utils.Exists(c.Args().First()) {
|
||||||
|
_, fname := filepath.Split(c.Args().First())
|
||||||
|
yn := utils.GetInput(fmt.Sprintf("Did you mean to send '%s'? (y/n) ", fname))
|
||||||
|
if strings.ToLower(yn) == "y" {
|
||||||
|
return send(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run(os.Args)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "\r\n%s", err.Error())
|
||||||
|
}
|
||||||
|
fmt.Fprintf(os.Stderr, "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveDefaultConfig(c *cli.Context) error {
|
||||||
|
return croc.SaveDefaultConfig()
|
||||||
|
}
|
||||||
|
|
||||||
|
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") != "" {
|
||||||
|
cr.Codephrase = c.String("code")
|
||||||
|
}
|
||||||
|
cr.LoadConfig()
|
||||||
|
if len(cr.Codephrase) == 0 {
|
||||||
|
// generate code phrase
|
||||||
|
cr.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,
|
||||||
|
cr.Codephrase,
|
||||||
|
cr.Codephrase,
|
||||||
|
)
|
||||||
|
if cr.Debug {
|
||||||
|
croc.SetDebugLevel("debug")
|
||||||
|
}
|
||||||
|
return cr.Send(fname, cr.Codephrase)
|
||||||
|
}
|
||||||
|
|
||||||
|
func receive(c *cli.Context) error {
|
||||||
|
if c.GlobalString("code") != "" {
|
||||||
|
cr.Codephrase = c.GlobalString("code")
|
||||||
|
}
|
||||||
|
if c.Args().First() != "" {
|
||||||
|
cr.Codephrase = c.Args().First()
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -52,6 +52,7 @@ func Debug(debug bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
Options Options
|
||||||
// basic setup
|
// basic setup
|
||||||
redisdb *redis.Client
|
redisdb *redis.Client
|
||||||
log *logrus.Entry
|
log *logrus.Entry
|
||||||
|
@ -132,14 +133,23 @@ func (m Message) String() string {
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
IsSender bool
|
||||||
|
SharedSecret string
|
||||||
|
Debug bool
|
||||||
|
AddressRelay string
|
||||||
|
Stdout bool
|
||||||
|
NoPrompt bool
|
||||||
|
}
|
||||||
|
|
||||||
// New establishes a new connection for transfering files between two instances.
|
// New establishes a new connection for transfering files between two instances.
|
||||||
func New(sender bool, sharedSecret string) (c *Client, err error) {
|
func New(ops Options) (c *Client, err error) {
|
||||||
c = new(Client)
|
c = new(Client)
|
||||||
|
|
||||||
// setup basic info
|
// setup basic info
|
||||||
c.IsSender = sender
|
c.IsSender = ops.Sender
|
||||||
c.SharedSecret = sharedSecret
|
c.SharedSecret = ops.SharedSecret
|
||||||
c.SharedSecret = sharedSecret
|
c.SharedSecret = ops.SharedSecret
|
||||||
if sender {
|
if sender {
|
||||||
c.nameOutChannel = c.SharedSecret + "2"
|
c.nameOutChannel = c.SharedSecret + "2"
|
||||||
c.nameInChannel = c.SharedSecret + "1"
|
c.nameInChannel = c.SharedSecret + "1"
|
||||||
|
@ -448,14 +458,6 @@ func (c *Client) processMessage(m Message) (err error) {
|
||||||
c.FilesToTransferCurrentNum = remoteFile.FilesToTransferCurrentNum
|
c.FilesToTransferCurrentNum = remoteFile.FilesToTransferCurrentNum
|
||||||
c.CurrentFileChunks = remoteFile.CurrentFileChunks
|
c.CurrentFileChunks = remoteFile.CurrentFileChunks
|
||||||
c.Step3RecipientRequestFile = true
|
c.Step3RecipientRequestFile = true
|
||||||
case "chunk":
|
|
||||||
var chunk Chunk
|
|
||||||
err = json.Unmarshal(m.Bytes, &chunk)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, err = c.CurrentFile.WriteAt(chunk.Bytes, chunk.Location)
|
|
||||||
c.log.Debug("writing chunk", chunk.Location)
|
|
||||||
case "datachannel-offer":
|
case "datachannel-offer":
|
||||||
err = c.dataChannelReceive()
|
err = c.dataChannelReceive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -567,42 +569,6 @@ func (c *Client) updateState() (err error) {
|
||||||
// start initiating the process to receive a new file
|
// start initiating the process to receive a new file
|
||||||
log.Debugf("working on file %d", c.FilesToTransferCurrentNum)
|
log.Debugf("working on file %d", c.FilesToTransferCurrentNum)
|
||||||
|
|
||||||
// // setup folder for new file
|
|
||||||
// if c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderRemote != "." {
|
|
||||||
// err = os.MkdirAll(c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderRemote, os.ModeDir)
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// pathToFile := path.Join(c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderRemote, c.FilesToTransfer[c.FilesToTransferCurrentNum].Name)
|
|
||||||
// check if file should be overwritten, or simply fixed with missing chunks
|
|
||||||
// overwrite := true
|
|
||||||
// fstats, errStats := os.Stat(pathToFile)
|
|
||||||
// if errStats == nil {
|
|
||||||
// if fstats.Size() == c.FilesToTransfer[c.FilesToTransferCurrentNum].Size {
|
|
||||||
// // just request missing chunks
|
|
||||||
// c.CurrentFileChunks = MissingChunks(pathToFile, fstats.Size(), BufferSize)
|
|
||||||
// log.Debugf("found %d missing chunks", len(c.CurrentFileChunks))
|
|
||||||
// overwrite = false
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// c.CurrentFileChunks = []int64{}
|
|
||||||
// }
|
|
||||||
// if overwrite {
|
|
||||||
// os.Remove(pathToFile)
|
|
||||||
// c.CurrentFile, err = os.Create(pathToFile)
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// err = c.CurrentFile.Truncate(c.FilesToTransfer[c.FilesToTransferCurrentNum].Size)
|
|
||||||
// } else {
|
|
||||||
// c.CurrentFile, err = os.OpenFile(pathToFile, os.O_RDWR|os.O_CREATE, 0755)
|
|
||||||
// }
|
|
||||||
// if err != nil {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
// recipient requests the file and chunks (if empty, then should receive all chunks)
|
// recipient requests the file and chunks (if empty, then should receive all chunks)
|
||||||
bRequest, _ := json.Marshal(RemoteFileRequest{
|
bRequest, _ := json.Marshal(RemoteFileRequest{
|
||||||
CurrentFileChunks: c.CurrentFileChunks,
|
CurrentFileChunks: c.CurrentFileChunks,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue