diff --git a/main.go b/main.go index 0fbc03c..b55bb36 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,8 @@ package main -import "github.com/schollz/croc/src/cli" +import ( + "github.com/schollz/croc/src/cli" +) var Version string diff --git a/src/cli/cli.go b/src/cli/cli.go index 8781bce..3e1428c 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -61,7 +61,7 @@ func Run() { }, } app.Flags = []cli.Flag{ - cli.StringFlag{Name: "addr", Value: "198.199.67.130", Usage: "address of the public relay"}, + cli.StringFlag{Name: "addr", Value: "croc4.schollz.com", 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,8158,8159,8160,8161", Usage: "tcp ports of the public relay server to connect"}, cli.BoolFlag{Name: "no-local", Usage: "disable local mode"}, diff --git a/src/croc/croc.go b/src/croc/croc.go index 4bdace4..50c4e4a 100644 --- a/src/croc/croc.go +++ b/src/croc/croc.go @@ -5,10 +5,10 @@ import ( "time" "github.com/schollz/croc/src/logger" - "github.com/schollz/croc/src/recipient" + "github.com/schollz/croc/src/models" "github.com/schollz/croc/src/relay" - "github.com/schollz/croc/src/sender" "github.com/schollz/croc/src/zipper" + "github.com/schollz/progressbar" ) func init() { @@ -52,6 +52,11 @@ type Croc struct { // is using local relay isLocal bool normalFinish bool + + // state variables + StateString string + Bar *progressbar.ProgressBar + FileInfo models.FileStats } // Init will initiate with the default parameters @@ -63,7 +68,7 @@ func Init(debug bool) (c *Croc) { c.RelayWebsocketPort = "8153" c.RelayTCPPorts = []string{"8154", "8155", "8156", "8157", "8158", "8159", "8160", "8161"} c.CurveType = "siec" - c.Address = "198.199.67.130" + c.Address = "croc4.schollz.com" c.AddressWebsocketPort = "8153" c.AddressTCPPorts = []string{"8154", "8155", "8156", "8157", "8158", "8159", "8160", "8161"} c.NoRecipientPrompt = true @@ -78,8 +83,6 @@ func Init(debug bool) (c *Croc) { func SetDebugLevel(debugLevel string) { logger.SetLogLevel(debugLevel) - sender.DebugLevel = debugLevel - recipient.DebugLevel = debugLevel relay.DebugLevel = debugLevel zipper.DebugLevel = debugLevel } diff --git a/src/recipient/recipient.go b/src/croc/recipient.go similarity index 78% rename from src/recipient/recipient.go rename to src/croc/recipient.go index ed0a642..e3c0d08 100644 --- a/src/recipient/recipient.go +++ b/src/croc/recipient.go @@ -1,4 +1,4 @@ -package recipient +package croc import ( "bufio" @@ -7,16 +7,14 @@ import ( "errors" "fmt" "io/ioutil" - "net" "os" "strconv" "strings" "sync" "time" - humanize "github.com/dustin/go-humanize" - log "github.com/cihub/seelog" + humanize "github.com/dustin/go-humanize" "github.com/gorilla/websocket" "github.com/schollz/croc/src/comm" "github.com/schollz/croc/src/compress" @@ -34,9 +32,9 @@ import ( var DebugLevel string // Receive is the async operation to receive a file -func Receive(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, done chan struct{}, c *websocket.Conn, codephrase string, noPrompt bool, useStdout bool) { +func (cr *Croc) startRecipient(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, done chan struct{}, c *websocket.Conn, codephrase string, noPrompt bool, useStdout bool) { logger.SetLogLevel(DebugLevel) - err := receive(forceSend, serverAddress, tcpPorts, isLocal, c, codephrase, noPrompt, useStdout) + err := cr.receive(forceSend, serverAddress, tcpPorts, isLocal, c, codephrase, noPrompt, useStdout) if err != nil { if !strings.HasPrefix(err.Error(), "websocket: close 100") { fmt.Fprintf(os.Stderr, "\n"+err.Error()) @@ -45,8 +43,7 @@ func Receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo done <- struct{}{} } -func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, c *websocket.Conn, codephrase string, noPrompt bool, useStdout bool) (err error) { - var fstats models.FileStats +func (cr *Croc) receive(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, c *websocket.Conn, codephrase string, noPrompt bool, useStdout bool) (err error) { var sessionKey []byte var transferTime time.Duration var hash256 []byte @@ -75,6 +72,7 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo spin.Writer = os.Stderr spin.Suffix = " performing PAKE..." spin.Start() + defer spin.Stop() // pick an elliptic curve curve := siec.SIEC255() @@ -137,10 +135,14 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo tcpConnections = make([]comm.Comm, len(tcpPorts)) for i, tcpPort := range tcpPorts { log.Debugf("connecting to %d", i) - tcpConnections[i], err = connectToTCPServer(utils.SHA256(fmt.Sprintf("%d%x", i, sessionKey)), serverAddress+":"+tcpPort) + var message string + tcpConnections[i], message, err = connectToTCPServer(utils.SHA256(fmt.Sprintf("%d%x", i, sessionKey)), serverAddress+":"+tcpPort) if err != nil { log.Error(err) } + if message != "recipient" { + log.Errorf("got wrong message: %s", message) + } } log.Debugf("fully connected") } @@ -169,16 +171,16 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo if err != nil { return err } - err = json.Unmarshal(decryptedFileData, &fstats) + err = json.Unmarshal(decryptedFileData, &cr.FileInfo) if err != nil { return err } - log.Debugf("got file stats: %+v", fstats) + log.Debugf("got file stats: %+v", cr.FileInfo) // determine if the file is resuming or not - progressFile = fmt.Sprintf("%s.progress", fstats.SentName) + progressFile = fmt.Sprintf("%s.progress", cr.FileInfo.SentName) overwritingOrReceiving := "Receiving" - if utils.Exists(fstats.Name) || utils.Exists(fstats.SentName) { + if utils.Exists(cr.FileInfo.Name) || utils.Exists(cr.FileInfo.SentName) { overwritingOrReceiving = "Overwriting" if utils.Exists(progressFile) { overwritingOrReceiving = "Resume receiving" @@ -202,14 +204,14 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo // prompt user about the file fileOrFolder := "file" - if fstats.IsDir { + if cr.FileInfo.IsDir { fileOrFolder = "folder" } fmt.Fprintf(os.Stderr, "\r%s %s (%s) into: %s\n", overwritingOrReceiving, fileOrFolder, - humanize.Bytes(uint64(fstats.Size)), - fstats.Name, + humanize.Bytes(uint64(cr.FileInfo.Size)), + cr.FileInfo.Name, ) if !noPrompt { if "y" != utils.GetInput("ok? (y/N): ") { @@ -222,27 +224,27 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo // await file // erase file if overwriting if overwritingOrReceiving == "Overwriting" { - os.Remove(fstats.SentName) + os.Remove(cr.FileInfo.SentName) } var f *os.File - if utils.Exists(fstats.SentName) && resumeFile { + if utils.Exists(cr.FileInfo.SentName) && resumeFile { if !useWebsockets { - f, err = os.OpenFile(fstats.SentName, os.O_WRONLY, 0644) + f, err = os.OpenFile(cr.FileInfo.SentName, os.O_WRONLY, 0644) } else { - f, err = os.OpenFile(fstats.SentName, os.O_APPEND|os.O_WRONLY, 0644) + f, err = os.OpenFile(cr.FileInfo.SentName, os.O_APPEND|os.O_WRONLY, 0644) } if err != nil { log.Error(err) return err } } else { - f, err = os.Create(fstats.SentName) + f, err = os.Create(cr.FileInfo.SentName) if err != nil { log.Error(err) return err } if !useWebsockets { - if err = f.Truncate(fstats.Size); err != nil { + if err = f.Truncate(cr.FileInfo.Size); err != nil { log.Error(err) return err } @@ -259,13 +261,13 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo // start the ui for pgoress bytesWritten := 0 fmt.Fprintf(os.Stderr, "\nReceiving (<-%s)...\n", otherIP) - bar := progressbar.NewOptions( - int(fstats.Size), + cr.Bar = progressbar.NewOptions( + int(cr.FileInfo.Size), progressbar.OptionSetRenderBlankState(true), - progressbar.OptionSetBytes(int(fstats.Size)), + progressbar.OptionSetBytes(int(cr.FileInfo.Size)), progressbar.OptionSetWriter(os.Stderr), ) - bar.Add((len(blocks) * blockSize)) + cr.Bar.Add((len(blocks) * blockSize)) finished := make(chan bool) go func(finished chan bool, dataChan chan []byte) (err error) { @@ -285,7 +287,7 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo defer fProgress.Close() blocksWritten := 0.0 - blocksToWrite := float64(fstats.Size) + blocksToWrite := float64(cr.FileInfo.Size) if useWebsockets { blocksToWrite = blocksToWrite/float64(models.WEBSOCKET_BUFFER_SIZE/8) - float64(len(blocks)) } else { @@ -301,7 +303,7 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo log.Error(err) return err } - decrypted, err := enc.Decrypt(sessionKey, !fstats.IsEncrypted) + decrypted, err := enc.Decrypt(sessionKey, !cr.FileInfo.IsEncrypted) if err != nil { log.Error(err) return err @@ -316,7 +318,7 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo } // do decompression - if fstats.IsCompressed && !fstats.IsDir { + if cr.FileInfo.IsCompressed && !cr.FileInfo.IsDir { decrypted = compress.Decompress(decrypted) } @@ -344,9 +346,9 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo bytesWritten += n blocksWritten += 1.0 // update the progress bar - bar.Add(n) - if int64(bytesWritten) == fstats.Size || blocksWritten >= blocksToWrite { - log.Debug("finished", int64(bytesWritten), fstats.Size, blocksWritten, blocksToWrite) + cr.Bar.Add(n) + if int64(bytesWritten) == cr.FileInfo.Size || blocksWritten >= blocksToWrite { + log.Debug("finished", int64(bytesWritten), cr.FileInfo.Size, blocksWritten, blocksToWrite) break } } @@ -429,10 +431,10 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo } // finish bar - bar.Finish() + cr.Bar.Finish() // check hash - hash256, err = utils.HashFile(fstats.SentName) + hash256, err = utils.HashFile(cr.FileInfo.SentName) if err != nil { log.Error(err) return err @@ -444,45 +446,45 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo log.Debugf("got hash: %x", message) if bytes.Equal(hash256, message) { // open directory - if fstats.IsDir { - err = zipper.UnzipFile(fstats.SentName, ".") + if cr.FileInfo.IsDir { + err = zipper.UnzipFile(cr.FileInfo.SentName, ".") if DebugLevel != "debug" { - os.Remove(fstats.SentName) + os.Remove(cr.FileInfo.SentName) } } else { err = nil } if err == nil { - if useStdout && !fstats.IsDir { + if useStdout && !cr.FileInfo.IsDir { var bFile []byte - bFile, err = ioutil.ReadFile(fstats.SentName) + bFile, err = ioutil.ReadFile(cr.FileInfo.SentName) if err != nil { return err } os.Stdout.Write(bFile) - os.Remove(fstats.SentName) + os.Remove(cr.FileInfo.SentName) } - transferRate := float64(fstats.Size) / 1000000.0 / transferTime.Seconds() + transferRate := float64(cr.FileInfo.Size) / 1000000.0 / transferTime.Seconds() transferType := "MB/s" if transferRate < 1 { - transferRate = float64(fstats.Size) / 1000.0 / transferTime.Seconds() + transferRate = float64(cr.FileInfo.Size) / 1000.0 / transferTime.Seconds() transferType = "kB/s" } folderOrFile := "file" - if fstats.IsDir { + if cr.FileInfo.IsDir { folderOrFile = "folder" } if useStdout { - fstats.Name = "stdout" + cr.FileInfo.Name = "stdout" } - fmt.Fprintf(os.Stderr, "\nReceived %s written to %s (%2.1f %s)\n", folderOrFile, fstats.Name, transferRate, transferType) + fmt.Fprintf(os.Stderr, "\nReceived %s written to %s (%2.1f %s)\n", folderOrFile, cr.FileInfo.Name, transferRate, transferType) os.Remove(progressFile) } return err } else { if DebugLevel != "debug" { log.Debug("removing corrupted file") - os.Remove(fstats.SentName) + os.Remove(cr.FileInfo.SentName) } return errors.New("file corrupted") } @@ -492,36 +494,3 @@ func receive(forceSend int, serverAddress string, tcpPorts []string, isLocal boo step++ } } - -func connectToTCPServer(room string, address string) (com comm.Comm, err error) { - log.Debugf("recipient connecting to %s", address) - connection, err := net.Dial("tcp", address) - if err != nil { - return - } - connection.SetReadDeadline(time.Now().Add(3 * time.Hour)) - connection.SetDeadline(time.Now().Add(3 * time.Hour)) - connection.SetWriteDeadline(time.Now().Add(3 * time.Hour)) - - com = comm.New(connection) - log.Debug("waiting for server contact") - ok, err := com.Receive() - if err != nil { - return - } - log.Debugf("[%s] server says: %s", address, ok) - - err = com.Send(room) - if err != nil { - return - } - ok, err = com.Receive() - log.Debugf("[%s] server says: %s", address, ok) - if err != nil { - return - } - if ok != "recipient" { - err = errors.New(ok) - } - return -} diff --git a/src/sender/sender.go b/src/croc/sender.go similarity index 85% rename from src/sender/sender.go rename to src/croc/sender.go index 7e10af8..11291d3 100644 --- a/src/sender/sender.go +++ b/src/croc/sender.go @@ -1,4 +1,4 @@ -package sender +package croc import ( "bytes" @@ -24,31 +24,31 @@ import ( "github.com/schollz/croc/src/utils" "github.com/schollz/croc/src/zipper" "github.com/schollz/pake" - "github.com/schollz/progressbar/v2" + "github.com/schollz/progressbar" "github.com/schollz/spinner" "github.com/tscholl2/siec" ) -var DebugLevel string - // Send is the async call to send data -func Send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, done chan struct{}, c *websocket.Conn, fname string, codephrase string, useCompression bool, useEncryption bool) { +func (cr *Croc) startSender(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, done chan struct{}, c *websocket.Conn, fname string, codephrase string, useCompression bool, useEncryption bool) { logger.SetLogLevel(DebugLevel) log.Debugf("sending %s", fname) - err := send(forceSend, serverAddress, tcpPorts, isLocal, c, fname, codephrase, useCompression, useEncryption) + err := cr.send(forceSend, serverAddress, tcpPorts, isLocal, c, fname, codephrase, useCompression, useEncryption) if err != nil { if !strings.HasPrefix(err.Error(), "websocket: close 100") { fmt.Fprintf(os.Stderr, "\n"+err.Error()) } + cr.StateString = err.Error() + } else { + cr.StateString = "File transfer completed." } done <- struct{}{} } -func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, c *websocket.Conn, fname string, codephrase string, useCompression bool, useEncryption bool) (err error) { +func (cr *Croc) send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, c *websocket.Conn, fname string, codephrase string, useCompression bool, useEncryption bool) (err error) { var f *os.File defer f.Close() // ignore the error if it wasn't opened :( - var fstats models.FileStats var fileHash []byte var otherIP string var startTransfer time.Time @@ -92,6 +92,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, // start a spinner spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond) spin.Writer = os.Stderr + defer spin.Stop() // pick an elliptic curve curve := siec.SIEC255() @@ -139,7 +140,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, fileReady <- err return } - fstats = models.FileStats{ + cr.FileInfo = models.FileStats{ Name: filename, Size: fstat.Size(), ModTime: fstat.ModTime(), @@ -148,15 +149,15 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, IsCompressed: useCompression, IsEncrypted: useEncryption, } - if fstats.IsDir { + if cr.FileInfo.IsDir { // zip the directory - fstats.SentName, err = zipper.ZipFile(fname, true) + cr.FileInfo.SentName, err = zipper.ZipFile(fname, true) if err != nil { log.Error(err) fileReady <- err return } - fname = fstats.SentName + fname = cr.FileInfo.SentName fstat, err := os.Stat(fname) if err != nil { @@ -164,7 +165,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, return } // get new size - fstats.Size = fstat.Size() + cr.FileInfo.Size = fstat.Size() } // open the file @@ -182,6 +183,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, c.WriteMessage(websocket.BinaryMessage, P.Bytes()) // start PAKE spinnner spin.Suffix = " performing PAKE..." + cr.StateString = "Performing PAKE..." spin.Start() case 2: // P recieves H(k),v from Q @@ -197,6 +199,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, // wait for readiness spin.Stop() spin.Suffix = " waiting for recipient ok..." + cr.StateString = "Waiting for recipient ok...." spin.Start() case 3: log.Debugf("[%d] recipient declares readiness for file info", step) @@ -211,10 +214,14 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, log.Debugf("connecting to server") for i, tcpPort := range tcpPorts { log.Debugf("connecting to %s on connection %d", tcpPort, i) - tcpConnections[i], err = connectToTCPServer(utils.SHA256(fmt.Sprintf("%d%x", i, sessionKey)), serverAddress+":"+tcpPort) + var message string + tcpConnections[i], message, err = connectToTCPServer(utils.SHA256(fmt.Sprintf("%d%x", i, sessionKey)), serverAddress+":"+tcpPort) if err != nil { log.Error(err) } + if message != "sender" { + log.Errorf("got wrong message: %s", message) + } } } isConnectedIfUsingTCP <- true @@ -224,7 +231,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, if err != nil { return err } - fstatsBytes, err := json.Marshal(fstats) + fstatsBytes, err := json.Marshal(cr.FileInfo) if err != nil { return err } @@ -263,9 +270,9 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, // start loading the file into memory // start streaming encryption/compression - if fstats.IsDir { + if cr.FileInfo.IsDir { // remove file if zipped - defer os.Remove(fstats.SentName) + defer os.Remove(cr.FileInfo.SentName) } go func(dataChan chan DataChan) { var buffer []byte @@ -287,7 +294,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, // do compression var compressedBytes []byte - if useCompression && !fstats.IsDir { + if useCompression && !cr.FileInfo.IsDir { compressedBytes = compress.Compress(buffer[:bytesread]) } else { compressedBytes = buffer[:bytesread] @@ -351,7 +358,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, if !bytes.HasPrefix(message, []byte("ready")) { return errors.New("recipient refused file") } - + cr.StateString = "Transfer in progress..." fmt.Fprintf(os.Stderr, "\rSending (->%s)...\n", otherIP) // send file, compure hash simultaneously startTransfer = time.Now() @@ -362,13 +369,13 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, } else { blockSize = models.TCP_BUFFER_SIZE / 2 } - bar := progressbar.NewOptions( - int(fstats.Size), + cr.Bar = progressbar.NewOptions( + int(cr.FileInfo.Size), progressbar.OptionSetRenderBlankState(true), - progressbar.OptionSetBytes(int(fstats.Size)), + progressbar.OptionSetBytes(int(cr.FileInfo.Size)), progressbar.OptionSetWriter(os.Stderr), ) - bar.Add(blockSize * len(blocksToSkip)) + cr.Bar.Add(blockSize * len(blocksToSkip)) if useWebsockets { for { @@ -376,7 +383,8 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, if data.err != nil { return data.err } - bar.Add(data.bytesRead) + cr.Bar.Add(data.bytesRead) + // write data to websockets err = c.WriteMessage(websocket.BinaryMessage, data.b) if err != nil { @@ -404,7 +412,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, log.Error(data.err) return } - bar.Add(data.bytesRead) + cr.Bar.Add(data.bytesRead) // write data to tcp connection _, err = tcpConnections[i].Write(data.b) if err != nil { @@ -422,7 +430,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, wg.Wait() } - bar.Finish() + cr.Bar.Finish() log.Debug("send hash to finish file") fileHash, err = utils.HashFile(fname) if err != nil { @@ -440,10 +448,10 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, log.Debugf("[%d] determing whether it went ok", step) if bytes.Equal(message, fileHash) { log.Debug("file transfered successfully") - transferRate := float64(fstats.Size) / 1000000.0 / transferTime.Seconds() + transferRate := float64(cr.FileInfo.Size) / 1000000.0 / transferTime.Seconds() transferType := "MB/s" if transferRate < 1 { - transferRate = float64(fstats.Size) / 1000.0 / transferTime.Seconds() + transferRate = float64(cr.FileInfo.Size) / 1000.0 / transferTime.Seconds() transferType = "kB/s" } fmt.Fprintf(os.Stderr, "\nTransfer complete (%2.1f %s)", transferRate, transferType) @@ -459,7 +467,7 @@ func send(forceSend int, serverAddress string, tcpPorts []string, isLocal bool, } } -func connectToTCPServer(room string, address string) (com comm.Comm, err error) { +func connectToTCPServer(room string, address string) (com comm.Comm, message string, err error) { connection, err := net.DialTimeout("tcp", address, 3*time.Hour) if err != nil { return @@ -479,13 +487,7 @@ func connectToTCPServer(room string, address string) (com comm.Comm, err error) if err != nil { return } - ok, err = com.Receive() - log.Debugf("server says: %s", ok) - if err != nil { - return - } - if ok != "sender" { - err = errors.New(ok) - } + message, err = com.Receive() + log.Debugf("server says: %s", message) return } diff --git a/src/croc/sending.go b/src/croc/sending.go index 98f9632..74d972e 100644 --- a/src/croc/sending.go +++ b/src/croc/sending.go @@ -11,15 +11,14 @@ import ( log "github.com/cihub/seelog" "github.com/gorilla/websocket" - "github.com/schollz/croc/src/recipient" "github.com/schollz/croc/src/relay" - "github.com/schollz/croc/src/sender" "github.com/schollz/peerdiscovery" "github.com/schollz/utils" ) // Send the file func (c *Croc) Send(fname, codephrase string) (err error) { + defer log.Flush() log.Debugf("sending %s", fname) errChan := make(chan error) @@ -38,6 +37,10 @@ func (c *Croc) Send(fname, codephrase string) (err error) { // use local relay if !c.NoLocal { + defer func() { + log.Debug("sending relay stop signal") + relay.Stop() + }() go func() { // start own relay and connect to it go relay.Run(c.RelayWebsocketPort, c.RelayTCPPorts) @@ -161,9 +164,9 @@ func (c *Croc) sendReceive(address, websocketPort string, tcpPorts []string, fna } if isSender { - go sender.Send(c.ForceSend, address, tcpPorts, isLocal, done, sock, fname, codephrase, c.UseCompression, c.UseEncryption) + go c.startSender(c.ForceSend, address, tcpPorts, isLocal, done, sock, fname, codephrase, c.UseCompression, c.UseEncryption) } else { - go recipient.Receive(c.ForceSend, address, tcpPorts, isLocal, done, sock, codephrase, c.NoRecipientPrompt, c.Stdout) + go c.startRecipient(c.ForceSend, address, tcpPorts, isLocal, done, sock, codephrase, c.NoRecipientPrompt, c.Stdout) } for { diff --git a/src/relay/hub.go b/src/relay/hub.go index 86d2aca..bbb11b3 100644 --- a/src/relay/hub.go +++ b/src/relay/hub.go @@ -47,6 +47,10 @@ var h = hub{ func (h *hub) run() { for { + if stop { + log.Debug("stopping hub") + return + } select { case s := <-h.register: log.Debugf("adding connection to %s", s.room) diff --git a/src/relay/relay.go b/src/relay/relay.go index e713e44..951a1ea 100644 --- a/src/relay/relay.go +++ b/src/relay/relay.go @@ -1,8 +1,10 @@ package relay import ( + "context" "fmt" "net/http" + "time" log "github.com/cihub/seelog" "github.com/schollz/croc/src/logger" @@ -10,6 +12,12 @@ import ( ) var DebugLevel string +var stop bool + +func Stop() { + log.Debug("got stop signal") + stop = true +} // Run is the async operation for running a server func Run(port string, tcpPorts []string) (err error) { @@ -23,12 +31,25 @@ func Run(port string, tcpPorts []string) (err error) { go h.run() log.Debug("running relay on " + port) - http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { + m := http.NewServeMux() + s := http.Server{Addr: ":" + port, Handler: m} + m.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { serveWs(w, r) }) - http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + m.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "ok") }) - err = http.ListenAndServe(":"+port, nil) + go func() { + for { + if stop { + s.Shutdown(context.Background()) + log.Debug("stopping http server") + return + } + time.Sleep(10 * time.Millisecond) + } + }() + s.ListenAndServe() + log.Debug("finished") return } diff --git a/src/win/main.go b/src/win/main.go index 390b1b9..43e65cf 100644 --- a/src/win/main.go +++ b/src/win/main.go @@ -5,9 +5,12 @@ package main import ( "fmt" "os" + "path/filepath" "time" "github.com/schollz/croc/src/cli" + "github.com/schollz/croc/src/croc" + "github.com/schollz/croc/src/utils" "github.com/therecipe/qt/core" "github.com/therecipe/qt/widgets" ) @@ -28,8 +31,8 @@ func main() { app := widgets.NewQApplication(len(os.Args), os.Args) window := widgets.NewQMainWindow(nil, 0) - window.SetFixedSize2(250, 200) - window.SetWindowTitle("croc - secure data transfer") + window.SetFixedSize2(400, 150) + window.SetWindowTitle("🐊📦 croc") widget := widgets.NewQWidget(nil, 0) widget.SetLayout(widgets.NewQVBoxLayout()) @@ -42,15 +45,13 @@ func main() { widget.Layout().AddWidget(label) labels[i] = label } - labels[0].SetText("Click 'Send' or 'Receive' to start") + labels[0].SetText("secure data transfer") + labels[1].SetText("Click 'Send' or 'Receive' to start") button := widgets.NewQPushButton2("Send file", nil) button.ConnectClicked(func(bool) { if isWorking { - var info = widgets.NewQMessageBox(nil) - info.SetWindowTitle("Info") - info.SetText(fmt.Sprintf("Can only do one send or recieve at a time")) - info.Exec() + dialog("Can only do one send or receive at a time") return } isWorking = true @@ -59,35 +60,80 @@ func main() { fileDialog.SetAcceptMode(widgets.QFileDialog__AcceptOpen) fileDialog.SetFileMode(widgets.QFileDialog__AnyFile) if fileDialog.Exec() != int(widgets.QDialog__Accepted) { + isWorking = false return } var fn = fileDialog.SelectedFiles()[0] - fmt.Println(fn) - for i, label := range labels { - go func(i int, label *CustomLabel) { - var tick int - for range time.NewTicker(time.Duration((i+1)*25) * time.Millisecond).C { - tick++ - label.SetText(fmt.Sprintf("%v %v", tick, time.Now().UTC().Format("15:04:05.0000"))) - } - }(i, label) + if len(fn) == 0 { + dialog(fmt.Sprintf("No file selected")) + isWorking = false + return } + + go func() { + cr := croc.Init(false) + done := make(chan bool) + codePhrase := utils.GetRandomName() + _, fname := filepath.Split(fn) + labels[0].UpdateTextFromGoroutine(fmt.Sprintf("Sending '%s'", fname)) + labels[1].UpdateTextFromGoroutine(fmt.Sprintf("Code phrase: %s", codePhrase)) + + go func(done chan bool) { + for { + fmt.Println(cr.FileInfo, cr.Bar) + if cr.FileInfo.SentName != "" { + labels[0].UpdateTextFromGoroutine(fmt.Sprintf("Sending %s", cr.FileInfo.SentName)) + } + if cr.Bar != nil { + barState := cr.Bar.State() + labels[1].UpdateTextFromGoroutine(fmt.Sprintf("%2.1f%% [%2.0f:%2.0f]", barState.CurrentPercent*100, barState.SecondsSince, barState.SecondsLeft)) + } + labels[2].UpdateTextFromGoroutine(cr.StateString) + time.Sleep(100 * time.Millisecond) + select { + case _ = <-done: + labels[2].UpdateTextFromGoroutine(cr.StateString) + return + default: + continue + } + } + }(done) + + cr.Send(fn, codePhrase) + done <- true + isWorking = false + }() + + // for i, label := range labels { + // go func(i int, label *CustomLabel) { + // var tick int + // for range time.NewTicker(time.Duration((i+1)*25) * time.Millisecond).C { + // tick++ + // label.SetText(fmt.Sprintf("%v %v", tick, time.Now().UTC().Format("15:04:05.0000"))) + // } + // }(i, label) + // } }) widget.Layout().AddWidget(button) receiveButton := widgets.NewQPushButton2("Receive", nil) receiveButton.ConnectClicked(func(bool) { if isWorking { - var info = widgets.NewQMessageBox(nil) - info.SetWindowTitle("Info") - info.SetText(fmt.Sprintf("Can only do one send or recieve at a time")) - info.Exec() + dialog("Can only do one send or receive at a time") return } isWorking = true + defer func() { + isWorking = false + }() + var codePhrase = widgets.QInputDialog_GetText(nil, "Enter code phrase", "", widgets.QLineEdit__Normal, "", true, core.Qt__Dialog, core.Qt__ImhNone) - fmt.Println(codePhrase) + if len(codePhrase) < 3 { + dialog(fmt.Sprintf("Invalid codephrase: '%s'", codePhrase)) + return + } var folderDialog = widgets.NewQFileDialog2(nil, "Open folder to receive file...", "", "") folderDialog.SetAcceptMode(widgets.QFileDialog__AcceptOpen) folderDialog.SetFileMode(widgets.QFileDialog__DirectoryOnly) @@ -95,19 +141,56 @@ func main() { return } var fn = folderDialog.SelectedFiles()[0] - fmt.Println(fn) - for i, label := range labels { - go func(i int, label *CustomLabel) { - var tick int - for range time.NewTicker(time.Duration((i+1)*25) * time.Millisecond).C { - tick++ - label.SetText(fmt.Sprintf("%v %v", tick, time.Now().UTC().Format("15:04:05.0000"))) - } - }(i, label) + if len(fn) == 0 { + dialog(fmt.Sprintf("No folder selected")) + return } + cwd, _ := os.Getwd() + os.Chdir(fn) + defer os.Chdir(cwd) + + cr := croc.Init(true) + done := make(chan bool) + go func() { + cr.Receive(codePhrase) + done <- true + }() + + for { + select { + case _ = <-done: + break + } + labels[0].SetText(cr.StateString) + if cr.FileInfo.SentName != "" { + labels[0].SetText(fmt.Sprintf("%s", cr.FileInfo.SentName)) + } + if cr.Bar != nil { + barState := cr.Bar.State() + labels[1].SetText(fmt.Sprintf("%2.1f", barState.CurrentPercent)) + } + time.Sleep(100 * time.Millisecond) + } + + // for i, label := range labels { + // go func(i int, label *CustomLabel) { + // var tick int + // for range time.NewTicker(time.Duration((i+1)*25) * time.Millisecond).C { + // tick++ + // label.SetText(fmt.Sprintf("%v %v", tick, time.Now().UTC().Format("15:04:05.0000"))) + // } + // }(i, label) + // } }) widget.Layout().AddWidget(receiveButton) window.Show() app.Exec() } + +func dialog(s string) { + var info = widgets.NewQMessageBox(nil) + info.SetWindowTitle("Info") + info.SetText(s) + info.Exec() +}