Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/123/util.go
1987 views
1
package _123
2
3
import (
4
"context"
5
"errors"
6
"fmt"
7
"hash/crc32"
8
"math"
9
"math/rand"
10
"net/http"
11
"net/url"
12
"strconv"
13
"strings"
14
"time"
15
16
"github.com/alist-org/alist/v3/drivers/base"
17
"github.com/alist-org/alist/v3/pkg/utils"
18
"github.com/go-resty/resty/v2"
19
jsoniter "github.com/json-iterator/go"
20
log "github.com/sirupsen/logrus"
21
)
22
23
// do others that not defined in Driver interface
24
25
const (
26
Api = "https://www.123pan.com/api"
27
AApi = "https://www.123pan.com/a/api"
28
BApi = "https://www.123pan.com/b/api"
29
LoginApi = "https://login.123pan.com/api"
30
MainApi = BApi
31
SignIn = LoginApi + "/user/sign_in"
32
Logout = MainApi + "/user/logout"
33
UserInfo = MainApi + "/user/info"
34
FileList = MainApi + "/file/list/new"
35
DownloadInfo = MainApi + "/file/download_info"
36
Mkdir = MainApi + "/file/upload_request"
37
Move = MainApi + "/file/mod_pid"
38
Rename = MainApi + "/file/rename"
39
Trash = MainApi + "/file/trash"
40
UploadRequest = MainApi + "/file/upload_request"
41
UploadComplete = MainApi + "/file/upload_complete"
42
S3PreSignedUrls = MainApi + "/file/s3_repare_upload_parts_batch"
43
S3Auth = MainApi + "/file/s3_upload_object/auth"
44
UploadCompleteV2 = MainApi + "/file/upload_complete/v2"
45
S3Complete = MainApi + "/file/s3_complete_multipart_upload"
46
SafeBoxUnlock = MainApi + "/restful/goapi/v1/file/safe_box/auth/unlockbox"
47
//AuthKeySalt = "8-8D$sL8gPjom7bk#cY"
48
)
49
50
func signPath(path string, os string, version string) (k string, v string) {
51
table := []byte{'a', 'd', 'e', 'f', 'g', 'h', 'l', 'm', 'y', 'i', 'j', 'n', 'o', 'p', 'k', 'q', 'r', 's', 't', 'u', 'b', 'c', 'v', 'w', 's', 'z'}
52
random := fmt.Sprintf("%.f", math.Round(1e7*rand.Float64()))
53
now := time.Now().In(time.FixedZone("CST", 8*3600))
54
timestamp := fmt.Sprint(now.Unix())
55
nowStr := []byte(now.Format("200601021504"))
56
for i := 0; i < len(nowStr); i++ {
57
nowStr[i] = table[nowStr[i]-48]
58
}
59
timeSign := fmt.Sprint(crc32.ChecksumIEEE(nowStr))
60
data := strings.Join([]string{timestamp, random, path, os, version, timeSign}, "|")
61
dataSign := fmt.Sprint(crc32.ChecksumIEEE([]byte(data)))
62
return timeSign, strings.Join([]string{timestamp, random, dataSign}, "-")
63
}
64
65
func GetApi(rawUrl string) string {
66
u, _ := url.Parse(rawUrl)
67
query := u.Query()
68
query.Add(signPath(u.Path, "web", "3"))
69
u.RawQuery = query.Encode()
70
return u.String()
71
}
72
73
//func GetApi(url string) string {
74
// vm := js.New()
75
// vm.Set("url", url[22:])
76
// r, err := vm.RunString(`
77
// (function(e){
78
// function A(t, e) {
79
// e = 1 < arguments.length && void 0 !== e ? e : 10;
80
// for (var n = function() {
81
// for (var t = [], e = 0; e < 256; e++) {
82
// for (var n = e, r = 0; r < 8; r++)
83
// n = 1 & n ? 3988292384 ^ n >>> 1 : n >>> 1;
84
// t[e] = n
85
// }
86
// return t
87
// }(), r = function(t) {
88
// t = t.replace(/\\r\\n/g, "\\n");
89
// for (var e = "", n = 0; n < t.length; n++) {
90
// var r = t.charCodeAt(n);
91
// r < 128 ? e += String.fromCharCode(r) : e = 127 < r && r < 2048 ? (e += String.fromCharCode(r >> 6 | 192)) + String.fromCharCode(63 & r | 128) : (e = (e += String.fromCharCode(r >> 12 | 224)) + String.fromCharCode(r >> 6 & 63 | 128)) + String.fromCharCode(63 & r | 128)
92
// }
93
// return e
94
// }(t), a = -1, i = 0; i < r.length; i++)
95
// a = a >>> 8 ^ n[255 & (a ^ r.charCodeAt(i))];
96
// return (a = (-1 ^ a) >>> 0).toString(e)
97
// }
98
//
99
// function v(t) {
100
// return (v = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(t) {
101
// return typeof t
102
// }
103
// : function(t) {
104
// return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t
105
// }
106
// )(t)
107
// }
108
//
109
// for (p in a = Math.round(1e7 * Math.random()),
110
// o = Math.round(((new Date).getTime() + 60 * (new Date).getTimezoneOffset() * 1e3 + 288e5) / 1e3).toString(),
111
// m = ["a", "d", "e", "f", "g", "h", "l", "m", "y", "i", "j", "n", "o", "p", "k", "q", "r", "s", "t", "u", "b", "c", "v", "w", "s", "z"],
112
// u = function(t, e, n) {
113
// var r;
114
// n = 2 < arguments.length && void 0 !== n ? n : 8;
115
// return 0 === arguments.length ? null : (r = "object" === v(t) ? t : (10 === "".concat(t).length && (t = 1e3 * Number.parseInt(t)),
116
// new Date(t)),
117
// t += 6e4 * new Date(t).getTimezoneOffset(),
118
// {
119
// y: (r = new Date(t + 36e5 * n)).getFullYear(),
120
// m: r.getMonth() + 1 < 10 ? "0".concat(r.getMonth() + 1) : r.getMonth() + 1,
121
// d: r.getDate() < 10 ? "0".concat(r.getDate()) : r.getDate(),
122
// h: r.getHours() < 10 ? "0".concat(r.getHours()) : r.getHours(),
123
// f: r.getMinutes() < 10 ? "0".concat(r.getMinutes()) : r.getMinutes()
124
// })
125
// }(o),
126
// h = u.y,
127
// g = u.m,
128
// l = u.d,
129
// c = u.h,
130
// u = u.f,
131
// d = [h, g, l, c, u].join(""),
132
// f = [],
133
// d)
134
// f.push(m[Number(d[p])]);
135
// return h = A(f.join("")),
136
// g = A("".concat(o, "|").concat(a, "|").concat(e, "|").concat("web", "|").concat("3", "|").concat(h)),
137
// "".concat(h, "=").concat(o, "-").concat(a, "-").concat(g);
138
// })(url)
139
// `)
140
// if err != nil {
141
// fmt.Println(err)
142
// return url
143
// }
144
// v, _ := r.Export().(string)
145
// return url + "?" + v
146
//}
147
148
func (d *Pan123) login() error {
149
var body base.Json
150
if utils.IsEmailFormat(d.Username) {
151
body = base.Json{
152
"mail": d.Username,
153
"password": d.Password,
154
"type": 2,
155
}
156
} else {
157
body = base.Json{
158
"passport": d.Username,
159
"password": d.Password,
160
"remember": true,
161
}
162
}
163
res, err := base.RestyClient.R().
164
SetHeaders(map[string]string{
165
"origin": "https://www.123pan.com",
166
"referer": "https://www.123pan.com/",
167
//"user-agent": "Dart/2.19(dart:io)-alist",
168
"platform": "web",
169
"app-version": "3",
170
"user-agent": base.UserAgent,
171
}).
172
SetBody(body).Post(SignIn)
173
if err != nil {
174
return err
175
}
176
if utils.Json.Get(res.Body(), "code").ToInt() != 200 {
177
err = fmt.Errorf(utils.Json.Get(res.Body(), "message").ToString())
178
} else {
179
d.AccessToken = utils.Json.Get(res.Body(), "data", "token").ToString()
180
}
181
return err
182
}
183
184
//func authKey(reqUrl string) (*string, error) {
185
// reqURL, err := url.Parse(reqUrl)
186
// if err != nil {
187
// return nil, err
188
// }
189
//
190
// nowUnix := time.Now().Unix()
191
// random := rand.Intn(0x989680)
192
//
193
// p4 := fmt.Sprintf("%d|%d|%s|%s|%s|%s", nowUnix, random, reqURL.Path, "web", "3", AuthKeySalt)
194
// authKey := fmt.Sprintf("%d-%d-%x", nowUnix, random, md5.Sum([]byte(p4)))
195
// return &authKey, nil
196
//}
197
198
func (d *Pan123) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
199
isRetry := false
200
do:
201
req := base.RestyClient.R()
202
req.SetHeaders(map[string]string{
203
"origin": "https://www.123pan.com",
204
"referer": "https://www.123pan.com/",
205
"authorization": "Bearer " + d.AccessToken,
206
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
207
"platform": "web",
208
"app-version": "3",
209
//"user-agent": base.UserAgent,
210
})
211
if callback != nil {
212
callback(req)
213
}
214
if resp != nil {
215
req.SetResult(resp)
216
}
217
//authKey, err := authKey(url)
218
//if err != nil {
219
// return nil, err
220
//}
221
//req.SetQueryParam("auth-key", *authKey)
222
res, err := req.Execute(method, GetApi(url))
223
if err != nil {
224
return nil, err
225
}
226
body := res.Body()
227
code := utils.Json.Get(body, "code").ToInt()
228
if code != 0 {
229
if !isRetry && code == 401 {
230
err := d.login()
231
if err != nil {
232
return nil, err
233
}
234
isRetry = true
235
goto do
236
}
237
return nil, errors.New(jsoniter.Get(body, "message").ToString())
238
}
239
return body, nil
240
}
241
242
func (d *Pan123) unlockSafeBox(fileId int64) error {
243
if _, ok := d.safeBoxUnlocked.Load(fileId); ok {
244
return nil
245
}
246
data := base.Json{"password": d.SafePassword}
247
url := fmt.Sprintf("%s?fileId=%d", SafeBoxUnlock, fileId)
248
_, err := d.Request(url, http.MethodPost, func(req *resty.Request) {
249
req.SetBody(data)
250
}, nil)
251
if err != nil {
252
return err
253
}
254
d.safeBoxUnlocked.Store(fileId, true)
255
return nil
256
}
257
258
func (d *Pan123) getFiles(ctx context.Context, parentId string, name string) ([]File, error) {
259
page := 1
260
total := 0
261
res := make([]File, 0)
262
// 2024-02-06 fix concurrency by 123pan
263
for {
264
if err := d.APIRateLimit(ctx, FileList); err != nil {
265
return nil, err
266
}
267
var resp Files
268
query := map[string]string{
269
"driveId": "0",
270
"limit": "100",
271
"next": "0",
272
"orderBy": "file_id",
273
"orderDirection": "desc",
274
"parentFileId": parentId,
275
"trashed": "false",
276
"SearchData": "",
277
"Page": strconv.Itoa(page),
278
"OnlyLookAbnormalFile": "0",
279
"event": "homeListFile",
280
"operateType": "4",
281
"inDirectSpace": "false",
282
}
283
_res, err := d.Request(FileList, http.MethodGet, func(req *resty.Request) {
284
req.SetQueryParams(query)
285
}, &resp)
286
if err != nil {
287
msg := strings.ToLower(err.Error())
288
if strings.Contains(msg, "safe box") || strings.Contains(err.Error(), "保险箱") {
289
if fid, e := strconv.ParseInt(parentId, 10, 64); e == nil {
290
if e = d.unlockSafeBox(fid); e == nil {
291
return d.getFiles(ctx, parentId, name)
292
}
293
return nil, e
294
}
295
}
296
return nil, err
297
}
298
log.Debug(string(_res))
299
page++
300
res = append(res, resp.Data.InfoList...)
301
total = resp.Data.Total
302
if len(resp.Data.InfoList) == 0 || resp.Data.Next == "-1" {
303
break
304
}
305
}
306
if len(res) != total {
307
log.Warnf("incorrect file count from remote at %s: expected %d, got %d", name, total, len(res))
308
}
309
return res, nil
310
}
311
312