-
Notifications
You must be signed in to change notification settings - Fork 0
/
upload.go
146 lines (123 loc) · 3.71 KB
/
upload.go
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
package pkg
import (
"bytes"
"errors"
"fmt"
"github.com/ProtonMail/gopenpgp/v2/crypto"
"io"
mime2 "mime"
"mime/multipart"
"net/http"
"strings"
"time"
)
// UploadFile uploads a file to the cloud.
// If encryption is enabled, it will encrypt the file before uploading.
// The file will be encrypted using the public key provided in the CryptoInfo struct.
// You need public key to encrypt the file.
// If you don't have the public key, you can get it from the server using the GetCryptoInfo function.
func UploadFile(token string, name string, rewriteMime string, disk string, folder string, cryptoInfo *CryptoInfo, reader io.Reader) (fileId string, err error) {
currentLogger("Uploading file %s", name)
var cryptoVal string
var publicRing *crypto.KeyRing
encrypt := cryptoInfo != nil
if !encrypt {
cryptoVal = "0"
confirm := ScanOrDefault("You are uploading a file without encryption. Continue? (y/n): ", "y")
if confirm != "y" {
return "", errors.New("upload aborted by user")
}
currentLogger("Uploading without encryption")
} else {
cryptoVal = "1"
currentLogger("Encrypting")
// If the crypto info is not ready, we need to get it. Nil check is not necessary because we have already checked it
if !cryptoInfo.IsCryptoReady() {
if err := cryptoInfo.TryGetReady(token, disk); err != nil {
return "", fmt.Errorf("failed to encrypt file: %w", err)
}
}
publicRing, _, err = GetKeyRings(cryptoInfo.PublicKey, cryptoInfo.RawCryptoKey, []byte(cryptoInfo.Password))
if err != nil {
return "", err
}
if !publicRing.CanEncrypt() {
return "", errors.New("public key cannot encrypt")
}
}
client := &http.Client{}
body := &bytes.Buffer{}
writerMultipart := multipart.NewWriter(body)
_ = writerMultipart.WriteField("token", token)
_ = writerMultipart.WriteField("disk", strings.TrimSpace(disk))
_ = writerMultipart.WriteField("folder", strings.TrimSpace(folder))
_ = writerMultipart.WriteField("crypto", strings.TrimSpace(cryptoVal))
part, err := writerMultipart.CreateFormFile("file", name)
if err != nil {
return "", err
}
if encrypt {
messageMeta := crypto.NewPlainMessageMetadata(true, name, time.Now().Unix())
plainWriter, err := publicRing.EncryptStreamWithCompression(part, messageMeta, nil)
if err != nil {
return "", err
}
_, err = io.Copy(plainWriter, reader)
_ = plainWriter.Close()
} else {
_, err = io.Copy(part, reader)
}
if err != nil {
return "", err
}
err = writerMultipart.Close()
if err != nil {
return "", err
}
req, err := http.NewRequest("POST", uploadUrl, body)
if err != nil {
return "", err
}
var mime string
if rewriteMime != "" {
mime = rewriteMime
} else {
mime = mime2.TypeByExtension(name)
if mime == "" {
mime = writerMultipart.FormDataContentType()
}
}
req.Header.Set("Content-Type", mime)
currentLogger("Uploading file to server")
responseInfo, err := client.Do(req)
if err != nil {
return "", err
}
defer responseInfo.Body.Close()
rawResponse, err := readerToMap(responseInfo.Body)
if err != nil {
return "", err
}
response, err := MapToStruct[ApiResponse](rawResponse)
if err != nil {
return "", err
}
if response.Error.Code != 0 {
return "", fmt.Errorf("%s: %s (code %d)", responseInfo.Status, response.Error.Message, response.Error.Code)
} else if responseInfo.StatusCode != http.StatusOK {
return "", errors.New(responseInfo.Status)
}
result, err := MapToStruct[UploadResult](response.Result)
if err != nil {
return "", err
}
if result.Ok {
fileId = result.FileID
if fileId == "" {
return "", errors.New("response file_id is empty")
}
currentLogger("File uploaded successfully. File ID: %s", fileId)
return fileId, nil
}
return "", errors.New("upload failed (unknown reason)")
}