croc/src/recipient/recipient.go

263 lines
6.2 KiB
Go
Raw Normal View History

2018-09-22 05:17:57 +02:00
package recipient
import (
"bytes"
"encoding/json"
"errors"
"fmt"
2018-09-22 18:54:04 +02:00
"io/ioutil"
2018-09-22 05:17:57 +02:00
"os"
2018-09-22 05:51:43 +02:00
"strings"
2018-09-22 05:17:57 +02:00
"time"
2018-09-22 07:38:02 +02:00
"github.com/briandowns/spinner"
humanize "github.com/dustin/go-humanize"
2018-09-22 06:59:46 +02:00
2018-09-22 05:17:57 +02:00
log "github.com/cihub/seelog"
"github.com/gorilla/websocket"
"github.com/schollz/croc/src/compress"
"github.com/schollz/croc/src/crypt"
"github.com/schollz/croc/src/logger"
"github.com/schollz/croc/src/models"
"github.com/schollz/croc/src/utils"
2018-09-22 16:23:10 +02:00
"github.com/schollz/croc/src/zipper"
2018-09-22 05:17:57 +02:00
"github.com/schollz/pake"
2018-09-22 05:51:43 +02:00
"github.com/schollz/progressbar/v2"
2018-09-22 05:17:57 +02:00
"github.com/tscholl2/siec"
)
var DebugLevel string
// Receive is the async operation to receive a file
2018-09-22 18:54:04 +02:00
func Receive(done chan struct{}, c *websocket.Conn, codephrase string, noPrompt bool, useStdout bool) {
2018-09-22 05:17:57 +02:00
logger.SetLogLevel(DebugLevel)
2018-09-22 18:54:04 +02:00
err := receive(c, codephrase, noPrompt, useStdout)
2018-09-22 05:17:57 +02:00
if err != nil {
2018-09-22 06:40:39 +02:00
if strings.HasPrefix(err.Error(), "websocket: close 100") {
2018-09-22 05:51:43 +02:00
return
}
2018-09-22 05:17:57 +02:00
log.Error(err)
}
done <- struct{}{}
}
2018-09-22 18:54:04 +02:00
func receive(c *websocket.Conn, codephrase string, noPrompt bool, useStdout bool) (err error) {
2018-09-22 05:17:57 +02:00
var fstats models.FileStats
var sessionKey []byte
2018-09-22 15:20:25 +02:00
var transferTime time.Duration
2018-09-22 16:23:10 +02:00
var hash256 []byte
2018-09-22 05:17:57 +02:00
2018-09-22 07:38:02 +02:00
// start a spinner
spin := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
2018-09-22 18:57:22 +02:00
spin.Writer = os.Stderr
2018-09-22 07:38:02 +02:00
spin.Suffix = " performing PAKE..."
spin.Start()
2018-09-22 05:17:57 +02:00
// pick an elliptic curve
curve := siec.SIEC255()
// both parties should have a weak key
2018-09-22 05:51:43 +02:00
pw := []byte(codephrase)
2018-09-22 05:17:57 +02:00
// initialize recipient Q ("1" indicates recipient)
Q, err := pake.Init(pw, 1, curve, 100*time.Millisecond)
if err != nil {
return
}
step := 0
for {
messageType, message, err := c.ReadMessage()
if err != nil {
return err
}
if messageType == websocket.PongMessage || messageType == websocket.PingMessage {
continue
}
log.Debugf("got %d: %s", messageType, message)
switch step {
case 0:
// Q receives u
log.Debugf("[%d] Q computes k, sends H(k), v back to P", step)
if err := Q.Update(message); err != nil {
return err
}
c.WriteMessage(websocket.BinaryMessage, Q.Bytes())
case 1:
log.Debugf("[%d] Q recieves H(k) from P", step)
if err := Q.Update(message); err != nil {
return err
}
sessionKey, err = Q.SessionKey()
if err != nil {
return err
}
log.Debugf("%x\n", sessionKey)
c.WriteMessage(websocket.BinaryMessage, []byte("ready"))
case 2:
2018-09-22 07:38:02 +02:00
spin.Stop()
// unmarshal the file info
2018-09-22 05:17:57 +02:00
log.Debugf("[%d] recieve file info", step)
err = json.Unmarshal(message, &fstats)
if err != nil {
return err
}
// prompt user if its okay to receive file
overwritingOrReceiving := "Receiving"
if utils.Exists(fstats.Name) {
overwritingOrReceiving = "Overwriting"
}
fileOrFolder := "file"
if fstats.IsDir {
fileOrFolder = "folder"
}
2018-09-22 15:32:44 +02:00
fmt.Fprintf(os.Stderr, "%s %s (%s) into: %s\n",
overwritingOrReceiving,
fileOrFolder,
humanize.Bytes(uint64(fstats.Size)),
fstats.Name,
2018-09-22 15:32:44 +02:00
)
if !noPrompt {
if "y" != utils.GetInput("ok? (y/N): ") {
fmt.Fprintf(os.Stderr, "cancelling request")
c.WriteMessage(websocket.BinaryMessage, []byte("no"))
return nil
}
}
2018-09-22 05:17:57 +02:00
// await file
2018-09-22 06:55:07 +02:00
f, err := os.Create(fstats.SentName)
2018-09-22 05:17:57 +02:00
if err != nil {
2018-09-22 06:13:46 +02:00
log.Error(err)
2018-09-22 05:17:57 +02:00
return err
}
bytesWritten := 0
2018-09-22 15:20:25 +02:00
fmt.Fprintf(os.Stderr, "\nReceiving...\n")
2018-09-22 05:17:57 +02:00
bar := progressbar.NewOptions(
int(fstats.Size),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionSetBytes(int(fstats.Size)),
2018-09-22 07:25:01 +02:00
progressbar.OptionSetWriter(os.Stderr),
2018-09-22 05:17:57 +02:00
)
c.WriteMessage(websocket.BinaryMessage, []byte("ready"))
2018-09-22 15:20:25 +02:00
startTime := time.Now()
2018-09-22 05:17:57 +02:00
for {
messageType, message, err := c.ReadMessage()
if err != nil {
return err
}
2018-09-22 16:23:10 +02:00
if messageType != websocket.BinaryMessage {
2018-09-22 05:17:57 +02:00
continue
}
2018-09-22 16:23:10 +02:00
// // tell the sender that we recieved this packet
// c.WriteMessage(websocket.BinaryMessage, []byte("ok"))
2018-09-22 05:17:57 +02:00
2018-09-22 16:23:10 +02:00
// do decryption
var enc crypt.Encryption
err = json.Unmarshal(message, &enc)
if err != nil {
return err
}
decrypted, err := enc.Decrypt(sessionKey, true)
if err != nil {
return err
}
2018-09-22 05:17:57 +02:00
2018-09-22 16:23:10 +02:00
// do decompression
decompressed := compress.Decompress(decrypted)
// decompressed := decrypted
2018-09-22 05:17:57 +02:00
2018-09-22 16:23:10 +02:00
// write to file
n, err := f.Write(decompressed)
if err != nil {
return err
}
// update the bytes written
bytesWritten += n
// update the progress bar
bar.Add(n)
2018-09-22 05:17:57 +02:00
2018-09-22 16:23:10 +02:00
if int64(bytesWritten) == fstats.Size {
break
}
}
c.WriteMessage(websocket.BinaryMessage, []byte("done"))
// we are finished
transferTime = time.Since(startTime)
2018-09-22 05:17:57 +02:00
2018-09-22 16:23:10 +02:00
// close file
err = f.Close()
if err != nil {
return err
}
// finish bar
bar.Finish()
// check hash
hash256, err = utils.HashFile(fstats.SentName)
if err != nil {
log.Error(err)
return err
}
// tell the sender the hash so they can quit
c.WriteMessage(websocket.BinaryMessage, append([]byte("hash:"), hash256...))
case 3:
// receive the hash from the sender so we can check it and quit
log.Debugf("got hash: %x", message)
if bytes.Equal(hash256, message) {
// open directory
if fstats.IsDir {
err = zipper.UnzipFile(fstats.SentName, ".")
if DebugLevel != "debug" {
os.Remove(fstats.SentName)
}
} else {
err = nil
}
if err == nil {
2018-09-22 18:54:04 +02:00
if useStdout && !fstats.IsDir {
var bFile []byte
bFile, err = ioutil.ReadFile(fstats.SentName)
if err != nil {
return err
}
os.Stdout.Write(bFile)
os.Remove(fstats.SentName)
}
2018-09-22 16:23:10 +02:00
transferRate := float64(fstats.Size) / 1000000.0 / transferTime.Seconds()
transferType := "MB/s"
if transferRate < 1 {
transferRate = float64(fstats.Size) / 1000.0 / transferTime.Seconds()
transferType = "kB/s"
}
folderOrFile := "file"
if fstats.IsDir {
folderOrFile = "folder"
2018-09-22 05:17:57 +02:00
}
2018-09-22 18:54:04 +02:00
if useStdout {
fstats.Name = "stdout"
}
2018-09-22 16:23:10 +02:00
fmt.Fprintf(os.Stderr, "\nReceived %s written to %s (%2.1f %s)\n", folderOrFile, fstats.Name, transferRate, transferType)
2018-09-22 05:17:57 +02:00
}
2018-09-22 16:23:10 +02:00
return err
} else {
2018-09-22 18:54:04 +02:00
if DebugLevel != "debug" {
log.Debug("removing corrupted file")
os.Remove(fstats.SentName)
}
2018-09-22 16:23:10 +02:00
return errors.New("file corrupted")
2018-09-22 05:17:57 +02:00
}
2018-09-22 16:23:10 +02:00
2018-09-22 05:17:57 +02:00
default:
return fmt.Errorf("unknown step")
}
step++
}
}