Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/aliyundrive/util.go
1987 views
1
package aliyundrive
2
3
import (
4
"crypto/sha256"
5
"encoding/hex"
6
"errors"
7
"fmt"
8
"net/http"
9
10
"github.com/alist-org/alist/v3/drivers/base"
11
"github.com/alist-org/alist/v3/internal/op"
12
"github.com/alist-org/alist/v3/pkg/utils"
13
"github.com/dustinxie/ecc"
14
"github.com/go-resty/resty/v2"
15
"github.com/google/uuid"
16
)
17
18
func (d *AliDrive) createSession() error {
19
state, ok := global.Load(d.UserID)
20
if !ok {
21
return fmt.Errorf("can't load user state, user_id: %s", d.UserID)
22
}
23
d.sign()
24
state.retry++
25
if state.retry > 3 {
26
state.retry = 0
27
return fmt.Errorf("createSession failed after three retries")
28
}
29
_, err, _ := d.request("https://api.alipan.com/users/v1/users/device/create_session", http.MethodPost, func(req *resty.Request) {
30
req.SetBody(base.Json{
31
"deviceName": "samsung",
32
"modelName": "SM-G9810",
33
"nonce": 0,
34
"pubKey": PublicKeyToHex(&state.privateKey.PublicKey),
35
"refreshToken": d.RefreshToken,
36
})
37
}, nil)
38
if err == nil{
39
state.retry = 0
40
}
41
return err
42
}
43
44
// func (d *AliDrive) renewSession() error {
45
// _, err, _ := d.request("https://api.alipan.com/users/v1/users/device/renew_session", http.MethodPost, nil, nil)
46
// return err
47
// }
48
49
func (d *AliDrive) sign() {
50
state, _ := global.Load(d.UserID)
51
secpAppID := "5dde4e1bdf9e4966b387ba58f4b3fdc3"
52
singdata := fmt.Sprintf("%s:%s:%s:%d", secpAppID, state.deviceID, d.UserID, 0)
53
hash := sha256.Sum256([]byte(singdata))
54
data, _ := ecc.SignBytes(state.privateKey, hash[:], ecc.RecID|ecc.LowerS)
55
state.signature = hex.EncodeToString(data) //strconv.Itoa(state.nonce)
56
}
57
58
// do others that not defined in Driver interface
59
60
func (d *AliDrive) refreshToken() error {
61
url := "https://auth.alipan.com/v2/account/token"
62
var resp base.TokenResp
63
var e RespErr
64
_, err := base.RestyClient.R().
65
//ForceContentType("application/json").
66
SetBody(base.Json{"refresh_token": d.RefreshToken, "grant_type": "refresh_token"}).
67
SetResult(&resp).
68
SetError(&e).
69
Post(url)
70
if err != nil {
71
return err
72
}
73
if e.Code != "" {
74
return fmt.Errorf("failed to refresh token: %s", e.Message)
75
}
76
if resp.RefreshToken == "" {
77
return errors.New("failed to refresh token: refresh token is empty")
78
}
79
d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
80
op.MustSaveDriverStorage(d)
81
return nil
82
}
83
84
func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error, RespErr) {
85
req := base.RestyClient.R()
86
state, ok := global.Load(d.UserID)
87
if !ok {
88
if url == "https://api.alipan.com/v2/user/get" {
89
state = &State{}
90
} else {
91
return nil, fmt.Errorf("can't load user state, user_id: %s", d.UserID), RespErr{}
92
}
93
}
94
req.SetHeaders(map[string]string{
95
"Authorization": "Bearer\t" + d.AccessToken,
96
"content-type": "application/json",
97
"origin": "https://www.alipan.com",
98
"Referer": "https://alipan.com/",
99
"X-Signature": state.signature,
100
"x-request-id": uuid.NewString(),
101
"X-Canary": "client=Android,app=adrive,version=v4.1.0",
102
"X-Device-Id": state.deviceID,
103
})
104
if callback != nil {
105
callback(req)
106
} else {
107
req.SetBody("{}")
108
}
109
if resp != nil {
110
req.SetResult(resp)
111
}
112
var e RespErr
113
req.SetError(&e)
114
res, err := req.Execute(method, url)
115
if err != nil {
116
return nil, err, e
117
}
118
if e.Code != "" {
119
switch e.Code {
120
case "AccessTokenInvalid":
121
err = d.refreshToken()
122
if err != nil {
123
return nil, err, e
124
}
125
case "DeviceSessionSignatureInvalid":
126
err = d.createSession()
127
if err != nil {
128
return nil, err, e
129
}
130
default:
131
return nil, errors.New(e.Message), e
132
}
133
return d.request(url, method, callback, resp)
134
} else if res.IsError() {
135
return nil, errors.New("bad status code " + res.Status()), e
136
}
137
return res.Body(), nil, e
138
}
139
140
func (d *AliDrive) getFiles(fileId string) ([]File, error) {
141
marker := "first"
142
res := make([]File, 0)
143
for marker != "" {
144
if marker == "first" {
145
marker = ""
146
}
147
var resp Files
148
data := base.Json{
149
"drive_id": d.DriveId,
150
"fields": "*",
151
"image_thumbnail_process": "image/resize,w_400/format,jpeg",
152
"image_url_process": "image/resize,w_1920/format,jpeg",
153
"limit": 200,
154
"marker": marker,
155
"order_by": d.OrderBy,
156
"order_direction": d.OrderDirection,
157
"parent_file_id": fileId,
158
"video_thumbnail_process": "video/snapshot,t_0,f_jpg,ar_auto,w_300",
159
"url_expire_sec": 14400,
160
}
161
_, err, _ := d.request("https://api.alipan.com/v2/file/list", http.MethodPost, func(req *resty.Request) {
162
req.SetBody(data)
163
}, &resp)
164
165
if err != nil {
166
return nil, err
167
}
168
marker = resp.NextMarker
169
res = append(res, resp.Items...)
170
}
171
return res, nil
172
}
173
174
func (d *AliDrive) batch(srcId, dstId string, url string) error {
175
res, err, _ := d.request("https://api.alipan.com/v3/batch", http.MethodPost, func(req *resty.Request) {
176
req.SetBody(base.Json{
177
"requests": []base.Json{
178
{
179
"headers": base.Json{
180
"Content-Type": "application/json",
181
},
182
"method": "POST",
183
"id": srcId,
184
"body": base.Json{
185
"drive_id": d.DriveId,
186
"file_id": srcId,
187
"to_drive_id": d.DriveId,
188
"to_parent_file_id": dstId,
189
},
190
"url": url,
191
},
192
},
193
"resource": "file",
194
})
195
}, nil)
196
if err != nil {
197
return err
198
}
199
status := utils.Json.Get(res, "responses", 0, "status").ToInt()
200
if status < 400 && status >= 100 {
201
return nil
202
}
203
return errors.New(string(res))
204
}
205
206