Skip to content
Merged

Dev #10

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 34 additions & 57 deletions cmd/receive.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package cmd

import (
"encoding/binary"
"context"
"fmt"
"io"
"net"
"os"
"path/filepath"
"time"

"github.com/neozmmv/blindspot/internal/transfer"
bstun "github.com/neozmmv/blindspot/internal/tun"
"github.com/neozmmv/blindspot/internal/utils"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -36,77 +34,56 @@ var ReceiveCmd = &cobra.Command{
return
}
} else {
home, err := os.UserHomeDir()
destDir, err = transfer.DownloadsDir()
if err != nil {
fmt.Println("Error finding home directory:", err)
return
}
destDir = filepath.Join(home, "Downloads")
if err := os.MkdirAll(destDir, 0755); err != nil {
fmt.Println("Error creating Downloads directory:", err)
fmt.Println(err)
return
}
}

myIP := bstun.VirtualIPv4(publicKey)
ln, err := net.Listen("tcp", myIP+transferPort)
recv, err := transfer.Listen(context.Background(), myIP)
if err != nil {
fmt.Printf("Could not listen on %s: %v\n", myIP+transferPort, err)
fmt.Println(err)
return
}
defer ln.Close()
defer recv.Close()

fmt.Printf("Waiting for file on %s...\n", myIP+transferPort)
fmt.Printf("Waiting for file on %s...\n", recv.Addr())

conn, err := ln.Accept()
if err != nil {
fmt.Println("Error accepting connection:", err)
return
}
defer conn.Close()

var nameLen uint16
if err := binary.Read(conn, binary.BigEndian, &nameLen); err != nil {
fmt.Println("Error reading filename length:", err)
return
}
nameBuf := make([]byte, nameLen)
if _, err := io.ReadFull(conn, nameBuf); err != nil {
fmt.Println("Error reading filename:", err)
return
}

var fileSize uint64
if err := binary.Read(conn, binary.BigEndian, &fileSize); err != nil {
fmt.Println("Error reading file size:", err)
return
}
// Render progress off a goroutine: the first snapshot prints the "Receiving…"
// header, later snapshots overwrite a single live line via carriage return.
prog := make(chan transfer.Progress, 1)
renderDone := make(chan struct{})
go func() {
var lr transfer.LineRenderer
first := true
for p := range prog {
if first {
first = false
lr.Seed(p)
fmt.Printf("Receiving %s (%d MB) from %s...\n", p.Name, p.Total/(1024*1024), p.PeerAddr)
continue
}
fmt.Print("\r" + lr.Line(p))
}
close(renderDone)
}()

filename := string(nameBuf)
destPath := filepath.Join(destDir, filename)
fmt.Printf("Receiving %s (%d MB) from %s...\n", filename, fileSize/(1024*1024), conn.RemoteAddr())
res, err := recv.Accept(context.Background(), destDir, prog)
close(prog)
<-renderDone

f, err := os.Create(destPath)
if err != nil {
fmt.Println("Error creating file:", err)
return
if enteredBody(err) {
fmt.Println()
}
defer f.Close()

pw := &progressWriter{w: f}
stop := startProgress(int64(fileSize), pw)
start := time.Now()
n, err := io.CopyN(pw, conn, int64(fileSize))
stop()
fmt.Println()
if err != nil {
fmt.Printf("Error receiving file (got %d/%d bytes): %v\n", n, fileSize, err)
fmt.Println(err)
return
}
elapsed := time.Since(start)
fmt.Printf("Saved to %s — %s in %s (avg %s/s)\n",
destPath, formatBytes(float64(n)), elapsed.Round(time.Millisecond),
formatBytes(float64(n)/elapsed.Seconds()))
res.Path, transfer.FormatBytes(float64(res.Bytes)), res.Elapsed.Round(time.Millisecond),
transfer.FormatBytes(float64(res.Bytes)/res.Elapsed.Seconds()))
},
}

Expand Down
93 changes: 44 additions & 49 deletions cmd/send.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package cmd

import (
"encoding/binary"
"context"
"errors"
"fmt"
"io"
"net"
"os"
"path/filepath"
"time"

"github.com/neozmmv/blindspot/internal/transfer"
"github.com/spf13/cobra"
)

const transferPort = ":28125"

var SendCmd = &cobra.Command{
Use: "send <peer-ip> <file>",
Short: "Send a file to a peer",
Expand All @@ -22,54 +18,53 @@ var SendCmd = &cobra.Command{
peerIP := args[0]
filePath := args[1]

f, err := os.Open(filePath)
if err != nil {
fmt.Println("Error opening file:", err)
return
}
defer f.Close()
// Render progress off a goroutine: the first snapshot prints the "Sending…"
// header, later snapshots overwrite a single live line via carriage return.
prog := make(chan transfer.Progress, 1)
renderDone := make(chan struct{})
go func() {
var lr transfer.LineRenderer
first := true
for p := range prog {
if first {
first = false
lr.Seed(p)
fmt.Printf("Sending %s (%s) to %s...\n", p.Name, transfer.FormatBytes(float64(p.Total)), peerIP)
continue
}
fmt.Print("\r" + lr.Line(p))
}
close(renderDone)
}()

info, err := f.Stat()
if err != nil {
fmt.Println("Error reading file info:", err)
return
}
res, err := transfer.Send(context.Background(), peerIP, filePath, prog)
close(prog)
<-renderDone

conn, err := net.DialTimeout("tcp", peerIP+transferPort, 5*time.Second)
if err != nil {
fmt.Printf("Peer %s is not receiving. Ask them to run 'blindspot receive'.\n", peerIP)
return
if enteredBody(err) {
fmt.Println()
}
defer conn.Close()

name := []byte(filepath.Base(filePath))
if err := binary.Write(conn, binary.BigEndian, uint16(len(name))); err != nil {
fmt.Println("Error sending filename length:", err)
return
}
if _, err := conn.Write(name); err != nil {
fmt.Println("Error sending filename:", err)
return
}
if err := binary.Write(conn, binary.BigEndian, uint64(info.Size())); err != nil {
fmt.Println("Error sending file size:", err)
return
}

fmt.Printf("Sending %s (%s) to %s...\n", info.Name(), formatBytes(float64(info.Size())), peerIP)
pw := &progressWriter{w: conn}
stop := startProgress(info.Size(), pw)
start := time.Now()
n, err := io.Copy(pw, f)
stop()
fmt.Println()
if err != nil {
fmt.Println("Error sending file:", err)
fmt.Println(err)
return
}
elapsed := time.Since(start)
fmt.Printf("Done — %s in %s (avg %s/s)\n",
formatBytes(float64(n)), elapsed.Round(time.Millisecond),
formatBytes(float64(n)/elapsed.Seconds()))
transfer.FormatBytes(float64(res.Bytes)), res.Elapsed.Round(time.Millisecond),
transfer.FormatBytes(float64(res.Bytes)/res.Elapsed.Seconds()))
},
}

// enteredBody reports whether a transfer got as far as streaming the file body — i.e. it
// succeeded, or failed during the copy. It gates the blank line that terminates the live
// progress display, so a failure before the body (dial, header, file create) prints no
// stray blank line, exactly as the pre-refactor CLI did.
func enteredBody(err error) bool {
if err == nil {
return true
}
var te *transfer.Error
if errors.As(err, &te) {
return te.Op == transfer.OpSendBody || te.Op == transfer.OpRecvBody
}
return false
}
67 changes: 0 additions & 67 deletions cmd/transfer.go

This file was deleted.

8 changes: 8 additions & 0 deletions cmd/update_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,14 @@ var updateWindowsCmd = &cobra.Command{
Use: "update",
Short: "Update blindspot (CLI and tray)",
Run: func(cmd *cobra.Command, args []string) {
// A running session keeps blindspot.exe open, so Windows can't overwrite it
// during the update (the move fails with the file in use). Tell the user to
// disconnect first, in red, and bail out before attempting anything.
if isSessionRunning() {
fmt.Printf("\033[31mYou are connected to a blindspot network. Disconnect before updating (run: blindspot disconnect).\033[0m\n")
os.Exit(1)
}

exe, err := os.Executable()
if err != nil {
fmt.Fprintf(os.Stderr, "Could not locate the blindspot executable: %v\n", err)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
)

require (
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 // indirect
github.com/adrg/xdg v0.5.3 // indirect
github.com/coder/websocket v1.8.14 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3 h1:N3IGoHHp9pb6mj1cbXbuaSXV/UMKwmbKLf53nQmtqMA=
git.sr.ht/~jackmordaunt/go-toast/v2 v2.0.3/go.mod h1:QtOLZGz8olr4qH2vWK0QH0w0O4T9fEIjMuWpKUsH7nc=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export {
};

export {
IncomingRequest,
Peer,
Status
} from "./models.js";
Loading
Loading