File size: 3,892 Bytes
81205f1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package cmd

import (
	"fmt"

	"github.com/BioHazard786/Warpdrop/cli/internal/config"
	"github.com/BioHazard786/Warpdrop/cli/internal/files"
	"github.com/BioHazard786/Warpdrop/cli/internal/signaling"
	"github.com/BioHazard786/Warpdrop/cli/internal/transfer"
	"github.com/BioHazard786/Warpdrop/cli/internal/ui"
	"github.com/spf13/cobra"
)

var (
	flagDomain   string
	flagSTUN     string
	flagTURN     string
	flagTURNUser string
	flagTURNPass string
	flagRelay    bool
)

var sendCmd = &cobra.Command{
	Use:     "send",
	Aliases: []string{"s"},
	Short:   "Send files to a receiver",
	Long: `Send files directly to a receiver using WebRTC technology.

Examples:
  warpdrop send file1.txt file2.pdf
  warpdrop send --domain custom.example.com file.txt
  warpdrop send --relay file.txt`,
	RunE: func(cmd *cobra.Command, args []string) error {
		if len(args) == 0 {
			return fmt.Errorf("no files specified")
		}
		return sendFiles(args)
	},
}

func sendFiles(filePaths []string) error {
	stopSpinner := ui.RunSpinner("Validating files...")
	defer stopSpinner()
	fileInfos, err := files.ValidateFiles(filePaths)
	if err != nil {
		return err
	}
	stopSpinner()

	displayFileTable(fileInfos)

	cfg, err := LoadConfig(config.Options{
		Domain:     flagDomain,
		STUNServer: flagSTUN,
		TURNServer: flagTURN,
		TURNUser:   flagTURNUser,
		TURNPass:   flagTURNPass,
		ForceRelay: flagRelay,
	})
	if err != nil {
		return err
	}

	fmt.Println()
	stopSpinner = ui.RunConnectionSpinner("Connecting to server...")
	defer stopSpinner()
	ctx, err := NewConnectionContext(cfg)
	if err != nil {
		return err
	}
	defer ctx.Close()
	stopSpinner()

	roomID, err := createRoom(ctx)
	if err != nil {
		return err
	}

	displayRoomInfo(roomID, cfg)

	peerInfo, err := waitForPeer(ctx)
	if err != nil {
		return err
	}
	ctx.PeerInfo = peerInfo

	fileInfoPtrs := prepareFileData(fileInfos)

	session, err := CreateSenderSession(ctx, fileInfoPtrs)
	if err != nil {
		return transfer.NewError("create session", err)
	}

	return RunSenderSession(session, nil)
}

func displayFileTable(fileInfos []files.FileInfo) {
	items := make([]ui.FileTableItem, len(fileInfos))
	for i, f := range fileInfos {
		items[i] = ui.FileTableItem{Index: i + 1, Name: f.Name, Size: f.Size, Type: f.Type}
	}
	fmt.Println()
	ui.RenderFileTable(items)
}

func displayRoomInfo(roomID string, cfg *config.Config) {
	ui.RenderRoomInfo(roomID, cfg.GetRoomLink(roomID))
}

func createRoom(ctx *ConnectionContext) (string, error) {
	ctx.Client.SendMessage(&signaling.Message{
		Type:       signaling.MessageTypeCreateRoom,
		ClientType: "cli",
	})

	select {
	case roomID := <-ctx.Handler.RoomCreated:
		return roomID, nil
	case errMsg := <-ctx.Handler.Error:
		return "", transfer.WrapError("create room", transfer.ErrSignalingError, errMsg)
	}
}

func waitForPeer(ctx *ConnectionContext) (*signaling.PeerInfo, error) {
	fmt.Println()
	stopSpinner := ui.RunWaitingSpinner("Waiting for receiver to join...")
	defer stopSpinner()

	select {
	case peerInfo := <-ctx.Handler.PeerJoined:
		return peerInfo, nil
	case errMsg := <-ctx.Handler.Error:
		return nil, transfer.WrapError("wait for peer", transfer.ErrSignalingError, errMsg)
	}
}

func prepareFileData(fileInfos []files.FileInfo) []*files.FileInfo {
	ptrs := make([]*files.FileInfo, len(fileInfos))

	for i := range fileInfos {
		ptrs[i] = &fileInfos[i]
	}

	return ptrs
}

func init() {
	rootCmd.AddCommand(sendCmd)

	sendCmd.Flags().StringVarP(&flagDomain, "domain", "d", "", "Custom domain")
	sendCmd.Flags().StringVarP(&flagSTUN, "stun", "s", "", "Custom STUN server")
	sendCmd.Flags().StringVarP(&flagTURN, "turn", "t", "", "Custom TURN server")
	sendCmd.Flags().StringVarP(&flagTURNUser, "turn-user", "u", "", "TURN username")
	sendCmd.Flags().StringVarP(&flagTURNPass, "turn-pass", "p", "", "TURN password")
	sendCmd.Flags().BoolVarP(&flagRelay, "relay", "r", false, "Force relay mode")
}