Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alist-org
GitHub Repository: alist-org/alist
Path: blob/main/drivers/terabox/util.go
1520 views
1
package terabox
2
3
import (
4
"encoding/base64"
5
"fmt"
6
"net/http"
7
"net/url"
8
"regexp"
9
"strconv"
10
"strings"
11
"time"
12
13
"github.com/alist-org/alist/v3/drivers/base"
14
"github.com/alist-org/alist/v3/internal/model"
15
"github.com/alist-org/alist/v3/pkg/utils"
16
"github.com/go-resty/resty/v2"
17
log "github.com/sirupsen/logrus"
18
)
19
20
const (
21
initialChunkSize int64 = 4 << 20 // 4MB
22
initialSizeThreshold int64 = 4 << 30 // 4GB
23
)
24
25
func getStrBetween(raw, start, end string) string {
26
regexPattern := fmt.Sprintf(`%s(.*?)%s`, regexp.QuoteMeta(start), regexp.QuoteMeta(end))
27
regex := regexp.MustCompile(regexPattern)
28
matches := regex.FindStringSubmatch(raw)
29
if len(matches) < 2 {
30
return ""
31
}
32
mid := matches[1]
33
return mid
34
}
35
36
func (d *Terabox) resetJsToken() error {
37
u := d.base_url
38
res, err := base.RestyClient.R().SetHeaders(map[string]string{
39
"Cookie": d.Cookie,
40
"Accept": "application/json, text/plain, */*",
41
"Referer": d.base_url,
42
"User-Agent": base.UserAgent,
43
"X-Requested-With": "XMLHttpRequest",
44
}).Get(u)
45
if err != nil {
46
return err
47
}
48
html := res.String()
49
jsToken := getStrBetween(html, "`function%20fn%28a%29%7Bwindow.jsToken%20%3D%20a%7D%3Bfn%28%22", "%22%29`")
50
if jsToken == "" {
51
return fmt.Errorf("jsToken not found, html: %s", html)
52
}
53
d.JsToken = jsToken
54
return nil
55
}
56
57
func (d *Terabox) request(rurl string, method string, callback base.ReqCallback, resp interface{}, noRetry ...bool) ([]byte, error) {
58
req := base.RestyClient.R()
59
req.SetHeaders(map[string]string{
60
"Cookie": d.Cookie,
61
"Accept": "application/json, text/plain, */*",
62
"Referer": d.base_url,
63
"User-Agent": base.UserAgent,
64
"X-Requested-With": "XMLHttpRequest",
65
})
66
req.SetQueryParams(map[string]string{
67
"app_id": "250528",
68
"web": "1",
69
"channel": "dubox",
70
"clienttype": "0",
71
"jsToken": d.JsToken,
72
})
73
if callback != nil {
74
callback(req)
75
}
76
if resp != nil {
77
req.SetResult(resp)
78
}
79
res, err := req.Execute(method, d.base_url+rurl)
80
if err != nil {
81
return nil, err
82
}
83
errno := utils.Json.Get(res.Body(), "errno").ToInt()
84
if errno == 4000023 {
85
// reget jsToken
86
err = d.resetJsToken()
87
if err != nil {
88
return nil, err
89
}
90
if !utils.IsBool(noRetry...) {
91
return d.request(rurl, method, callback, resp, true)
92
}
93
} else if errno == -6 {
94
header := res.Header()
95
log.Debugln(header)
96
urlDomainPrefix := header.Get("Url-Domain-Prefix")
97
if len(urlDomainPrefix) > 0 {
98
d.url_domain_prefix = urlDomainPrefix
99
d.base_url = "https://" + d.url_domain_prefix + ".terabox.com"
100
log.Debugln("Redirect base_url to", d.base_url)
101
return d.request(rurl, method, callback, resp, noRetry...)
102
}
103
}
104
return res.Body(), nil
105
}
106
107
func (d *Terabox) get(pathname string, params map[string]string, resp interface{}) ([]byte, error) {
108
return d.request(pathname, http.MethodGet, func(req *resty.Request) {
109
if params != nil {
110
req.SetQueryParams(params)
111
}
112
}, resp)
113
}
114
115
func (d *Terabox) post(pathname string, params map[string]string, data interface{}, resp interface{}) ([]byte, error) {
116
return d.request(pathname, http.MethodPost, func(req *resty.Request) {
117
if params != nil {
118
req.SetQueryParams(params)
119
}
120
req.SetBody(data)
121
}, resp)
122
}
123
124
func (d *Terabox) post_form(pathname string, params map[string]string, data map[string]string, resp interface{}) ([]byte, error) {
125
return d.request(pathname, http.MethodPost, func(req *resty.Request) {
126
if params != nil {
127
req.SetQueryParams(params)
128
}
129
req.SetFormData(data)
130
}, resp)
131
}
132
133
func (d *Terabox) getFiles(dir string) ([]File, error) {
134
page := 1
135
num := 100
136
params := map[string]string{
137
"dir": dir,
138
}
139
if d.OrderBy != "" {
140
params["order"] = d.OrderBy
141
if d.OrderDirection == "desc" {
142
params["desc"] = "1"
143
}
144
}
145
res := make([]File, 0)
146
for {
147
params["page"] = strconv.Itoa(page)
148
params["num"] = strconv.Itoa(num)
149
var resp ListResp
150
_, err := d.get("/api/list", params, &resp)
151
if err != nil {
152
return nil, err
153
}
154
if resp.Errno == 9000 {
155
return nil, fmt.Errorf("terabox is not yet available in this area")
156
}
157
if len(resp.List) == 0 {
158
break
159
}
160
res = append(res, resp.List...)
161
page++
162
}
163
return res, nil
164
}
165
166
func sign(s1, s2 string) string {
167
var a = make([]int, 256)
168
var p = make([]int, 256)
169
var o []byte
170
var v = len(s1)
171
for q := 0; q < 256; q++ {
172
a[q] = int(s1[(q % v) : (q%v)+1][0])
173
p[q] = q
174
}
175
for u, q := 0, 0; q < 256; q++ {
176
u = (u + p[q] + a[q]) % 256
177
p[q], p[u] = p[u], p[q]
178
}
179
for i, u, q := 0, 0, 0; q < len(s2); q++ {
180
i = (i + 1) % 256
181
u = (u + p[i]) % 256
182
p[i], p[u] = p[u], p[i]
183
k := p[((p[i] + p[u]) % 256)]
184
o = append(o, byte(int(s2[q])^k))
185
}
186
return base64.StdEncoding.EncodeToString(o)
187
}
188
189
func (d *Terabox) genSign() (string, error) {
190
var resp HomeInfoResp
191
_, err := d.get("/api/home/info", map[string]string{}, &resp)
192
if err != nil {
193
return "", err
194
}
195
return sign(resp.Data.Sign3, resp.Data.Sign1), nil
196
}
197
198
func (d *Terabox) linkOfficial(file model.Obj, args model.LinkArgs) (*model.Link, error) {
199
var resp DownloadResp
200
signString, err := d.genSign()
201
if err != nil {
202
return nil, err
203
}
204
params := map[string]string{
205
"type": "dlink",
206
"fidlist": fmt.Sprintf("[%s]", file.GetID()),
207
"sign": signString,
208
"vip": "2",
209
"timestamp": strconv.FormatInt(time.Now().Unix(), 10),
210
}
211
_, err = d.get("/api/download", params, &resp)
212
if err != nil {
213
return nil, err
214
}
215
216
if len(resp.Dlink) == 0 {
217
return nil, fmt.Errorf("fid %s no dlink found, errno: %d", file.GetID(), resp.Errno)
218
}
219
220
res, err := base.NoRedirectClient.R().SetHeader("Cookie", d.Cookie).SetHeader("User-Agent", base.UserAgent).Get(resp.Dlink[0].Dlink)
221
if err != nil {
222
return nil, err
223
}
224
u := res.Header().Get("location")
225
return &model.Link{
226
URL: u,
227
Header: http.Header{
228
"User-Agent": []string{base.UserAgent},
229
},
230
}, nil
231
}
232
233
func (d *Terabox) linkCrack(file model.Obj, args model.LinkArgs) (*model.Link, error) {
234
var resp DownloadResp2
235
param := map[string]string{
236
"target": fmt.Sprintf("[\"%s\"]", file.GetPath()),
237
"dlink": "1",
238
"origin": "dlna",
239
}
240
_, err := d.get("/api/filemetas", param, &resp)
241
if err != nil {
242
return nil, err
243
}
244
return &model.Link{
245
URL: resp.Info[0].Dlink,
246
Header: http.Header{
247
"User-Agent": []string{base.UserAgent},
248
},
249
}, nil
250
}
251
252
func (d *Terabox) manage(opera string, filelist interface{}) ([]byte, error) {
253
params := map[string]string{
254
"onnest": "fail",
255
"opera": opera,
256
}
257
marshal, err := utils.Json.Marshal(filelist)
258
if err != nil {
259
return nil, err
260
}
261
data := fmt.Sprintf("async=0&filelist=%s&ondup=newcopy", encodeURIComponent(string(marshal)))
262
return d.post("/api/filemanager", params, data, nil)
263
}
264
265
func encodeURIComponent(str string) string {
266
r := url.QueryEscape(str)
267
r = strings.ReplaceAll(r, "+", "%20")
268
return r
269
}
270
271
func calculateChunkSize(streamSize int64) int64 {
272
chunkSize := initialChunkSize
273
sizeThreshold := initialSizeThreshold
274
275
if streamSize < chunkSize {
276
return streamSize
277
}
278
279
for streamSize > sizeThreshold {
280
chunkSize <<= 1
281
sizeThreshold <<= 1
282
}
283
284
return chunkSize
285
}
286
287