From 4eef9cd9bc531016061f5cb303faacb79c606d54 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Mon, 14 Mar 2022 20:40:42 +0800 Subject: [PATCH 01/10] fix: nil pointer while delete baidu account (close #751) --- drivers/baidu/driver.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/baidu/driver.go b/drivers/baidu/driver.go index 7493d180..e1a33f5a 100644 --- a/drivers/baidu/driver.go +++ b/drivers/baidu/driver.go @@ -76,6 +76,9 @@ func (driver Baidu) Items() []base.Item { } func (driver Baidu) Save(account *model.Account, old *model.Account) error { + if account == nil { + return nil + } return driver.RefreshToken(account) } From 5a1b16a60106ddc058d078f29565cc6c62058564 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Mon, 14 Mar 2022 22:43:27 +0800 Subject: [PATCH 02/10] feat: set overwrite for aliyundrive upload --- drivers/alidrive/driver.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/alidrive/driver.go b/drivers/alidrive/driver.go index 0ddcf68b..8ef0c7b0 100644 --- a/drivers/alidrive/driver.go +++ b/drivers/alidrive/driver.go @@ -412,7 +412,7 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er buf := make([]byte, 1024) n, _ := file.Read(buf[:]) reqBody := base.Json{ - "check_name_mode": "auto_rename", + "check_name_mode": "overwrite", "drive_id": account.DriveId, "name": file.GetFileName(), "parent_file_id": parentFile.Id, From ef19e851e31bd4a8227c467f13d096f1a314d099 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Tue, 15 Mar 2022 14:48:39 +0800 Subject: [PATCH 03/10] fix: check local ip for 123pan --- drivers/123/driver.go | 2 +- utils/check.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 utils/check.go diff --git a/drivers/123/driver.go b/drivers/123/driver.go index 0491ab36..27ffcefb 100644 --- a/drivers/123/driver.go +++ b/drivers/123/driver.go @@ -140,7 +140,7 @@ func (driver Pan123) Link(args base.Args, account *model.Account) (*base.Link, e } var resp Pan123DownResp var headers map[string]string - if args.IP != "" && args.IP != "::1" { + if !utils.IsLocalIPAddr(args.IP) { headers = map[string]string{ //"X-Real-IP": "1.1.1.1", "X-Forwarded-For": args.IP, diff --git a/utils/check.go b/utils/check.go new file mode 100644 index 00000000..65d62ee8 --- /dev/null +++ b/utils/check.go @@ -0,0 +1,28 @@ +package utils + +import ( + "net" +) + +func IsLocalIPAddr(ip string) bool { + return IsLocalIP(net.ParseIP(ip)) +} + +func IsLocalIP(ip net.IP) bool { + if ip == nil { + return false + } + if ip.IsLoopback() { + return true + } + + ip4 := ip.To4() + if ip4 == nil { + return false + } + + return ip4[0] == 10 || // 10.0.0.0/8 + (ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12 + (ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16 + (ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16 +} From 58426613f60c3c1b275ea2ffd9a9843a849f6ae9 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Tue, 15 Mar 2022 17:05:54 +0800 Subject: [PATCH 04/10] feat: add tls config for mysql (fix #758) --- bootstrap/model.go | 4 ++-- conf/config.go | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/bootstrap/model.go b/bootstrap/model.go index 028ef8d1..94453e10 100644 --- a/bootstrap/model.go +++ b/bootstrap/model.go @@ -50,8 +50,8 @@ func InitModel() { } case "mysql": { - dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", - databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name) + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s", + databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name, databaseConfig.SslMode) db, err := gorm.Open(mysql.Open(dsn), gormConfig) if err != nil { log.Fatalf("failed to connect database:%s", err.Error()) diff --git a/conf/config.go b/conf/config.go index 280cd5df..7d4c0fe0 100644 --- a/conf/config.go +++ b/conf/config.go @@ -45,7 +45,6 @@ func DefaultConfig() *Config { Port: 0, TablePrefix: "x_", DBFile: "data/data.db", - SslMode: "disable", }, Cache: CacheConfig{ Expiration: 60, From 2dbedc245c06f10b57c8d854ee79bb2530f17edb Mon Sep 17 00:00:00 2001 From: foxxorcat <95907542+foxxorcat@users.noreply.github.com> Date: Wed, 16 Mar 2022 14:22:42 +0800 Subject: [PATCH 05/10] fix: 189 family cloud upload (#761) --- drivers/189pc/189.go | 17 +++--- drivers/189pc/driver.go | 113 +++++++++++++++++++++++++--------------- drivers/189pc/type.go | 13 +++++ 3 files changed, 91 insertions(+), 52 deletions(-) diff --git a/drivers/189pc/189.go b/drivers/189pc/189.go index e14d0081..9dcf1c68 100644 --- a/drivers/189pc/189.go +++ b/drivers/189pc/189.go @@ -16,7 +16,6 @@ import ( "github.com/Xhofe/alist/utils" "github.com/go-resty/resty/v2" "github.com/google/uuid" - jsoniter "github.com/json-iterator/go" log "github.com/sirupsen/logrus" ) @@ -110,7 +109,7 @@ func (s *State) login(account *model.Account) error { if err != nil { return err } - toUrl := jsoniter.Get(res.Body(), "toUrl").ToString() + toUrl := utils.Json.Get(res.Body(), "toUrl").ToString() if toUrl == "" { log.Error(res.String()) return fmt.Errorf(res.String()) @@ -179,10 +178,10 @@ func (s *State) getLoginParam() (*LoginParam, error) { if err != nil { return nil, err } - if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 { - return nil, errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString()) + if utils.Json.Get(vRes.Body(), "status").ToInt() != 200 { + return nil, errors.New("ocr error:" + utils.Json.Get(vRes.Body(), "msg").ToString()) } - param.vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString() + param.vCodeRS = utils.Json.Get(vRes.Body(), "result").ToString() log.Debugln("code: ", param.vCodeRS) } return param, nil @@ -308,9 +307,9 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba } if account != nil { - switch jsoniter.Get(res.Body(), "res_code").ToInt64() { + switch utils.Json.Get(res.Body(), "res_code").ToInt64() { case 11, 18: - if err := s.refreshSession(account); err != nil { + if err := s.RefreshSession(account); err != nil { return nil, err } return s.Request(method, fullUrl, params, callback, account) @@ -324,8 +323,8 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba } } - if jsoniter.Get(res.Body(), "res_code").ToInt64() != 0 { - return res, fmt.Errorf(jsoniter.Get(res.Body(), "res_message").ToString()) + if utils.Json.Get(res.Body(), "res_code").ToInt64() != 0 { + return res, fmt.Errorf(utils.Json.Get(res.Body(), "res_message").ToString()) } return res, nil } diff --git a/drivers/189pc/driver.go b/drivers/189pc/driver.go index bb01775b..1faf28d4 100644 --- a/drivers/189pc/driver.go +++ b/drivers/189pc/driver.go @@ -3,7 +3,6 @@ package _189 import ( "crypto/md5" "encoding/hex" - "encoding/json" "fmt" "io" "io/ioutil" @@ -64,9 +63,10 @@ func (driver Cloud189) Items() []base.Item { Values: "Personal,Family", }, { - Name: "site_id", - Label: "family id", - Type: base.TypeString, + Name: "site_id", + Label: "family id", + Type: base.TypeString, + Required: true, }, { Name: "order_by", @@ -96,13 +96,41 @@ func (driver Cloud189) Save(account *model.Account, old *model.Account) error { state := GetState(account) if !state.IsLogin() { - return state.Login(account) + if err := state.Login(account); err != nil { + return err + } } + + if isFamily(account) { + list, err := driver.getFamilyInfoList(account) + if err != nil { + return err + } + for _, l := range list { + if account.SiteId == "" { + account.SiteId = fmt.Sprint(l.FamilyID) + } + log.Infof("天翼家庭云 用户名:%s FamilyID %d\n", l.RemarkName, l.FamilyID) + } + } + account.Status = "work" model.SaveAccount(account) return nil } +func (driver Cloud189) getFamilyInfoList(account *model.Account) ([]FamilyInfoResp, error) { + var resp FamilyInfoListResp + _, err := GetState(account).Request("GET", API_URL+"/family/manage/getFamilyList.action", nil, func(r *resty.Request) { + r.SetQueryParams(clientSuffix()) + r.SetResult(&resp) + }, account) + if err != nil { + return nil, err + } + return resp.FamilyInfoResp, nil +} + func (driver Cloud189) File(path string, account *model.Account) (*model.File, error) { path = utils.ParsePath(path) if path == "/" { @@ -329,7 +357,7 @@ func (driver Cloud189) Move(src string, dst string, account *model.Account) erro _, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) { r.SetFormData(clientSuffix()).SetFormData(map[string]string{ "type": "MOVE", - "taskInfos": string(MustToBytes(json.Marshal( + "taskInfos": string(MustToBytes(utils.Json.Marshal( []*BatchTaskInfo{ { FileId: srcFile.Id, @@ -445,7 +473,7 @@ func (driver Cloud189) Copy(src string, dst string, account *model.Account) erro _, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) { r.SetFormData(clientSuffix()).SetFormData(map[string]string{ "type": "COPY", - "taskInfos": string(MustToBytes(json.Marshal( + "taskInfos": string(MustToBytes(utils.Json.Marshal( []*BatchTaskInfo{ { FileId: srcFile.Id, @@ -475,7 +503,7 @@ func (driver Cloud189) Delete(path string, account *model.Account) error { _, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) { r.SetFormData(clientSuffix()).SetFormData(map[string]string{ "type": "DELETE", - "taskInfos": string(MustToBytes(json.Marshal( + "taskInfos": string(MustToBytes(utils.Json.Marshal( []*BatchTaskInfo{ { FileId: srcFile.Id, @@ -546,7 +574,7 @@ func (driver Cloud189) uploadFamily(file *model.FileStream, parentFile *model.Fi } if createUpload.FileDataExists != 1 { - if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil { + if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil { return err } } @@ -601,7 +629,7 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi } if createUpload.FileDataExists != 1 { - if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil { + if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil { return err } } @@ -618,40 +646,39 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi return err } -func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) error { - var uploadFileState *UploadFileStatusResult - var err error - for i := 0; i < 10; i++ { - if uploadFileState, err = driver.getUploadFileState(createUpload.UploadFileId, account); err != nil { - return err - } - - if uploadFileState.FileDataExists == 1 || uploadFileState.DataSize == int64(file.Size) { - return nil - } - - if _, err = tempFile.Seek(uploadFileState.DataSize, io.SeekStart); err != nil { - return err - } - - _, err = GetState(account).Request("PUT", uploadFileState.FileUploadUrl, nil, func(r *resty.Request) { - r.SetQueryParams(clientSuffix()) - r.SetHeaders(map[string]string{ - "ResumePolicy": "1", - "Edrive-UploadFileId": fmt.Sprint(createUpload.UploadFileId), - "Edrive-UploadFileRange": fmt.Sprintf("bytes=%d-%d", uploadFileState.DataSize, file.Size), - "Expect": "100-continue", - }) - if isFamily(account) { - r.SetHeader("FamilyId", account.SiteId) - } - r.SetBody(tempFile) - }, account) - if err == nil { - break - } +func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) (int64, error) { + uploadFileState, err := driver.getUploadFileState(createUpload.UploadFileId, account) + if err != nil { + return 0, err } - return err + + if uploadFileState.FileDataExists == 1 || uploadFileState.DataSize == int64(file.Size) { + return uploadFileState.UploadFileId, nil + } + + if _, err = tempFile.Seek(uploadFileState.DataSize, io.SeekStart); err != nil { + return 0, err + } + + _, err = GetState(account).Request("PUT", uploadFileState.FileUploadUrl, nil, func(r *resty.Request) { + r.SetQueryParams(clientSuffix()) + r.SetHeaders(map[string]string{ + "Content-Type": "application/octet-stream", + "ResumePolicy": "1", + "Edrive-UploadFileRange": fmt.Sprintf("bytes=%d-%d", uploadFileState.DataSize, file.Size), + "Expect": "100-continue", + }) + if isFamily(account) { + r.SetHeaders(map[string]string{ + "familyId": account.SiteId, + "UploadFileId": fmt.Sprint(uploadFileState.UploadFileId), + }) + } else { + r.SetHeader("Edrive-UploadFileId", fmt.Sprint(uploadFileState.UploadFileId)) + } + r.SetBody(tempFile) + }, account) + return uploadFileState.UploadFileId, err } func (driver Cloud189) getUploadFileState(uploadFileId int64, account *model.Account) (*UploadFileStatusResult, error) { diff --git a/drivers/189pc/type.go b/drivers/189pc/type.go index d1557444..12e54087 100644 --- a/drivers/189pc/type.go +++ b/drivers/189pc/type.go @@ -60,6 +60,19 @@ type appSessionResp struct { RefreshToken string `json:"refreshToken"` } +type FamilyInfoListResp struct { + FamilyInfoResp []FamilyInfoResp `json:"familyInfoResp"` +} +type FamilyInfoResp struct { + Count int `json:"count"` + CreateTime string `json:"createTime"` + FamilyID int `json:"familyId"` + RemarkName string `json:"remarkName"` + Type int `json:"type"` + UseFlag int `json:"useFlag"` + UserRole int `json:"userRole"` +} + /*文件部分*/ // 文件 type Cloud189File struct { From b21801d5057c705ffffefbdfa94e09654953df49 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Wed, 16 Mar 2022 18:02:11 +0800 Subject: [PATCH 06/10] fix: clear cookie for 189 cloud login --- drivers/189/189.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/189/189.go b/drivers/189/189.go index 1adf8818..79d0bbc2 100644 --- a/drivers/189/189.go +++ b/drivers/189/189.go @@ -17,6 +17,7 @@ import ( "io" "math" "net/http" + "net/http/cookiejar" "path/filepath" "regexp" "strconv" @@ -105,6 +106,9 @@ func (driver Cloud189) Login(account *model.Account) error { client.SetRetryCount(3) client.SetHeader("Referer", "https://cloud.189.cn/") } + // clear cookie + jar, _ := cookiejar.New(nil) + client.SetCookieJar(jar) url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action" b := "" lt := "" From 6db09a2736c1164de5839c242f0ce4c7bad0517e Mon Sep 17 00:00:00 2001 From: foxxorcat <95907542+foxxorcat@users.noreply.github.com> Date: Thu, 17 Mar 2022 21:13:13 +0800 Subject: [PATCH 07/10] fix: xunlei upload error (#749) --- drivers/xunlei/driver.go | 108 ++++++++++++++++++++++++++++----------- drivers/xunlei/util.go | 6 +++ drivers/xunlei/xunlei.go | 73 +++++++++++++------------- 3 files changed, 120 insertions(+), 67 deletions(-) diff --git a/drivers/xunlei/driver.go b/drivers/xunlei/driver.go index 8ecde652..6d39ad22 100644 --- a/drivers/xunlei/driver.go +++ b/drivers/xunlei/driver.go @@ -4,12 +4,12 @@ import ( "fmt" "io" "io/ioutil" - "net/url" "os" "path/filepath" "strconv" "github.com/aliyun/aliyun-oss-go-sdk/oss" + "github.com/go-resty/resty/v2" "github.com/Xhofe/alist/conf" "github.com/Xhofe/alist/drivers/base" @@ -95,12 +95,13 @@ func (driver XunLeiCloud) File(path string, account *model.Account) (*model.File } func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.File, error) { + path = utils.ParsePath(path) cache, err := base.GetCache(path, account) if err == nil { files, _ := cache.([]model.File) return files, nil } - file, err := driver.File(utils.ParsePath(path), account) + file, err := driver.File(path, account) if err != nil { return nil, err } @@ -108,8 +109,16 @@ func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.Fi files := make([]model.File, 0) for { var fileList FileList - u := fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files?parent_id=%s&page_token=%s&with_audit=true&filters=%s", file.Id, fileList.NextPageToken, url.QueryEscape(`{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`)) - if err = GetState(account).Request("GET", u, nil, &fileList, account); err != nil { + _, err = GetState(account).Request("GET", FILE_API_URL, func(r *resty.Request) { + r.SetQueryParams(map[string]string{ + "parent_id": file.Id, + "page_token": fileList.NextPageToken, + "with_audit": "true", + "filters": `{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`, + }) + r.SetResult(&fileList) + }, account) + if err != nil { return nil, err } for _, file := range fileList.Files { @@ -153,7 +162,12 @@ func (driver XunLeiCloud) Link(args base.Args, account *model.Account) (*base.Li return nil, base.ErrNotFile } var lFile Files - if err = GetState(account).Request("GET", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s?&with_audit=true", file.Id), nil, &lFile, account); err != nil { + _, err = GetState(account).Request("GET", FILE_API_URL+"/{id}", func(r *resty.Request) { + r.SetPathParam("id", file.Id) + r.SetQueryParam("with_audit", "true") + r.SetResult(&lFile) + }, account) + if err != nil { return nil, err } return &base.Link{ @@ -194,7 +208,14 @@ func (driver XunLeiCloud) MakeDir(path string, account *model.Account) error { if !parentFile.IsDir() { return base.ErrNotFolder } - return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{"kind": FOLDER, "name": name, "parent_id": parentFile.Id}, nil, account) + _, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) { + r.SetBody(&base.Json{ + "kind": FOLDER, + "name": name, + "parent_id": parentFile.Id, + }) + }, account) + return err } func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) error { @@ -207,7 +228,14 @@ func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) e if err != nil { return err } - return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchMove", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account) + + _, err = GetState(account).Request("POST", FILE_API_URL+":batchMove", func(r *resty.Request) { + r.SetBody(&base.Json{ + "to": base.Json{"parent_id": dstDirFile.Id}, + "ids": []string{srcFile.Id}, + }) + }, account) + return err } func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) error { @@ -220,7 +248,13 @@ func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) e if err != nil { return err } - return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchCopy", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account) + _, err = GetState(account).Request("POST", FILE_API_URL+":batchCopy", func(r *resty.Request) { + r.SetBody(&base.Json{ + "to": base.Json{"parent_id": dstDirFile.Id}, + "ids": []string{srcFile.Id}, + }) + }, account) + return err } func (driver XunLeiCloud) Delete(path string, account *model.Account) error { @@ -228,7 +262,11 @@ func (driver XunLeiCloud) Delete(path string, account *model.Account) error { if err != nil { return err } - return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s/trash", srcFile.Id), &base.Json{}, nil, account) + _, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}/trash", func(r *resty.Request) { + r.SetPathParam("id", srcFile.Id) + r.SetBody(&base.Json{}) + }, account) + return err } func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account) error { @@ -246,7 +284,6 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account) return err } - defer tempFile.Close() defer os.Remove(tempFile.Name()) gcid, err := getGcid(io.TeeReader(file, tempFile), int64(file.Size)) @@ -254,29 +291,37 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account) return err } - var rep UploadTaskResponse - err = GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{ - "kind": FILE, - "parent_id": parentFile.Id, - "name": file.Name, - "size": fmt.Sprint(file.Size), - "hash": gcid, - "upload_type": UPLOAD_TYPE_RESUMABLE, - }, &rep, account) + tempFile.Close() + + var resp UploadTaskResponse + _, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) { + r.SetBody(&base.Json{ + "kind": FILE, + "parent_id": parentFile.Id, + "name": file.Name, + "size": fmt.Sprint(file.Size), + "hash": gcid, + "upload_type": UPLOAD_TYPE_RESUMABLE, + }) + r.SetResult(&resp) + }, account) if err != nil { return err } - param := rep.Resumable.Params - client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true), oss.HTTPClient(xunleiClient.GetClient())) - if err != nil { - return err + param := resp.Resumable.Params + if resp.UploadType == UPLOAD_TYPE_RESUMABLE { + client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true)) + if err != nil { + return err + } + bucket, err := client.Bucket(param.Bucket) + if err != nil { + return err + } + return bucket.UploadFile(param.Key, tempFile.Name(), 1<<22, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration)) } - bucket, err := client.Bucket(param.Bucket) - if err != nil { - return err - } - return bucket.UploadFile(param.Key, tempFile.Name(), 4*1024*1024, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration)) + return nil } func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account) error { @@ -285,8 +330,11 @@ func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account) if err != nil { return err } - - return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s", srcFile.Id), &base.Json{"name": dstName}, nil, account) + _, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}", func(r *resty.Request) { + r.SetPathParam("id", srcFile.Id) + r.SetBody(&base.Json{"name": dstName}) + }, account) + return err } var _ base.Driver = (*XunLeiCloud)(nil) diff --git a/drivers/xunlei/util.go b/drivers/xunlei/util.go index 6b64ab3c..6c5315c9 100644 --- a/drivers/xunlei/util.go +++ b/drivers/xunlei/util.go @@ -37,6 +37,12 @@ var Algorithms = []string{ "T78dnANexYRbiZy", } +const ( + API_URL = "https://api-pan.xunlei.com/drive/v1" + FILE_API_URL = API_URL + "/files" + XLUSER_API_URL = "https://xluser-ssl.xunlei.com/v1" +) + const ( FOLDER = "drive#folder" FILE = "drive#file" diff --git a/drivers/xunlei/xunlei.go b/drivers/xunlei/xunlei.go index 67d9b801..cd9f32f5 100644 --- a/drivers/xunlei/xunlei.go +++ b/drivers/xunlei/xunlei.go @@ -2,6 +2,7 @@ package xunlei import ( "fmt" + "net/http" "sync" "time" @@ -12,7 +13,7 @@ import ( log "github.com/sirupsen/logrus" ) -var xunleiClient = resty.New().SetTimeout(120 * time.Second) +var xunleiClient = resty.New().SetHeaders(map[string]string{"Accept": "application/json;charset=UTF-8"}) // 一个账户只允许登陆一次 var userStateCache = struct { @@ -102,16 +103,16 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account * var e Erron var resp CaptchaTokenResponse _, err := xunleiClient.R(). - SetHeader("X-Device-Id", driverID). SetBody(&creq). SetError(&e). SetResult(&resp). - Post("https://xluser-ssl.xunlei.com/v1/shield/captcha/init?client_id=" + CLIENT_ID) + SetHeader("X-Device-Id", driverID). + SetQueryParam("client_id", CLIENT_ID). + Post(XLUSER_API_URL + "/shield/captcha/init") if err != nil { return "", err } if e.ErrorCode != 0 { - log.Debugf("%+v\n %+v", e, account) return "", fmt.Errorf("%s : %s", e.Error, e.ErrorDescription) } if resp.Url != "" { @@ -120,7 +121,6 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account * s.captchaTokenExpiresTime = (ctime + resp.ExpiresIn*1000) - 30000 s.captchaToken = resp.CaptchaToken - log.Debugf("%+v\n %+v", s.captchaToken, account) return s.captchaToken, nil } @@ -136,7 +136,7 @@ func (s *State) refreshToken_(account *model.Account) error { "client_secret": CLIENT_SECRET, }). SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)).SetQueryParam("client_id", CLIENT_ID). - Post("https://xluser-ssl.xunlei.com/v1/auth/token") + Post(XLUSER_API_URL + "/auth/token") if err != nil { return err } @@ -152,7 +152,6 @@ func (s *State) refreshToken_(account *model.Account) error { s.userID = resp.UserID return nil default: - log.Debugf("%+v\n %+v", e, account) return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription) } } @@ -160,7 +159,7 @@ func (s *State) refreshToken_(account *model.Account) error { func (s *State) login(account *model.Account) error { s.init() ctime := time.Now().UnixMilli() - url := "https://xluser-ssl.xunlei.com/v1/auth/signin" + url := XLUSER_API_URL + "/auth/signin" captchaToken, err := s.newCaptchaToken(getAction("POST", url), map[string]string{"username": account.Username}, account) if err != nil { return err @@ -190,7 +189,6 @@ func (s *State) login(account *model.Account) error { defer model.SaveAccount(account) if e.ErrorCode != 0 { account.Status = e.Error - log.Debugf("%+v\n %+v", e, account) return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription) } account.Status = "work" @@ -199,67 +197,68 @@ func (s *State) login(account *model.Account) error { s.accessToken = resp.AccessToken s.refreshToken = resp.RefreshToken s.userID = resp.UserID - log.Debugf("%+v\n %+v", resp, account) return nil } -func (s *State) Request(method string, url string, body interface{}, resp interface{}, account *model.Account) error { +func (s *State) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) { s.Lock() token, err := s.getToken(account) if err != nil { - return err + return nil, err } captchaToken, err := s.getCaptchaToken(getAction(method, url), account) if err != nil { - return err + return nil, err } - s.Unlock() - var e Erron + req := xunleiClient.R(). - SetError(&e). - SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)). - SetHeader("Authorization", token). - SetHeader("X-Captcha-Token", captchaToken). + SetHeaders(map[string]string{ + "X-Device-Id": utils.GetMD5Encode(account.Username), + "Authorization": token, + "X-Captcha-Token": captchaToken, + }). SetQueryParam("client_id", CLIENT_ID) - if body != nil { - req.SetBody(body) - } - if resp != nil { - req.SetResult(resp) - } + callback(req) + s.Unlock() + var res *resty.Response switch method { case "GET": - _, err = req.Get(url) + res, err = req.Get(url) case "POST": - _, err = req.Post(url) + res, err = req.Post(url) case "DELETE": - _, err = req.Delete(url) + res, err = req.Delete(url) case "PATCH": - _, err = req.Patch(url) + res, err = req.Patch(url) case "PUT": - _, err = req.Put(url) + res, err = req.Put(url) default: - return base.ErrNotSupport + return nil, base.ErrNotSupport } if err != nil { - return err + return nil, err } + log.Debug(res.String()) + var e Erron + utils.Json.Unmarshal(res.Body(), &e) switch e.ErrorCode { - case 0: - return nil case 9: s.newCaptchaToken(getAction(method, url), nil, account) fallthrough case 4122, 4121: - return s.Request(method, url, body, resp, account) + return s.Request(method, url, callback, account) + case 0: + if res.StatusCode() == http.StatusOK { + return res, nil + } + return nil, fmt.Errorf(res.String()) default: - log.Debugf("%+v\n %+v", e, account) - return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription) + return nil, fmt.Errorf("%s : %s", e.Error, e.ErrorDescription) } } From f4f61a5787f42eca7e1aed7347637aad7eda7a3c Mon Sep 17 00:00:00 2001 From: Xhofe Date: Thu, 17 Mar 2022 21:23:10 +0800 Subject: [PATCH 08/10] fix(webdav): nil pointer error (close #749) --- server/webdav/file.go | 32 +++++++++++--------------------- server/webdav/webdav.go | 16 +++------------- 2 files changed, 14 insertions(+), 34 deletions(-) diff --git a/server/webdav/file.go b/server/webdav/file.go index 479a16df..5c8ddb2d 100644 --- a/server/webdav/file.go +++ b/server/webdav/file.go @@ -24,13 +24,8 @@ import ( type FileSystem struct{} -var upFileMap = make(map[string]*model.File) - func (fs *FileSystem) File(rawPath string) (*model.File, error) { rawPath = utils.ParsePath(rawPath) - if f, ok := upFileMap[rawPath]; ok { - return f, nil - } if model.AccountsCount() > 1 && rawPath == "/" { now := time.Now() return &model.File{ @@ -156,31 +151,26 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, rawPath string) error return operate.MakeDir(driver, account, path_, true) } -func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) error { +func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) (FileInfo, error) { rawPath = utils.ParsePath(rawPath) if model.AccountsCount() > 1 && rawPath == "/" { - return ErrNotImplemented + return nil, ErrNotImplemented } account, path_, driver, err := common.ParsePath(rawPath) if err != nil { - return err + return nil, err } - //fileSize, err := strconv.ParseUint(r.Header.Get("Content-Length"), 10, 64) fileSize := uint64(r.ContentLength) - //if err != nil { - // return err - //} filePath, fileName := filepath.Split(path_) now := time.Now() + fi := &model.File{ + Name: fileName, + Size: 0, + UpdatedAt: &now, + } if fileSize == 0 { - upFileMap[rawPath] = &model.File{ - Name: fileName, - Size: 0, - UpdatedAt: &now, - } - return nil - } else { - delete(upFileMap, rawPath) + // 如果文件大小为0,默认成功 + return fi, nil } fileData := model.FileStream{ MIMEType: r.Header.Get("Content-Type"), @@ -189,7 +179,7 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin Name: fileName, ParentPath: filePath, } - return operate.Upload(driver, account, &fileData, true) + return fi, operate.Upload(driver, account, &fileData, true) } func (fs *FileSystem) Delete(rawPath string) error { diff --git a/server/webdav/webdav.go b/server/webdav/webdav.go index 3db500e3..b5566054 100644 --- a/server/webdav/webdav.go +++ b/server/webdav/webdav.go @@ -94,7 +94,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, fs *FileSyst } } -// OK func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string, status int, err error) { token, err = h.LockSystem.Create(now, LockDetails{ Root: root, @@ -110,7 +109,6 @@ func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string return token, 0, nil } -// ok func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem) (release func(), status int, err error) { hdr := r.Header.Get("If") if hdr == "" { @@ -189,7 +187,6 @@ func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem) return nil, http.StatusPreconditionFailed, ErrLocked } -//OK func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { reqPath, status, err := h.stripPrefix(r.URL.Path) if err != nil { @@ -213,7 +210,6 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *File return 0, nil } -// OK func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { reqPath, status, err := h.stripPrefix(r.URL.Path) @@ -248,7 +244,6 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs * return 0, nil } -// OK func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { reqPath, status, err := h.stripPrefix(r.URL.Path) @@ -267,7 +262,6 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileS return http.StatusNoContent, nil } -// OK func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { reqPath, status, err := h.stripPrefix(r.URL.Path) if err != nil { @@ -283,12 +277,13 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst ctx, cancel := context.WithCancel(context.Background()) defer cancel() - err = fs.Upload(ctx, r, reqPath) + fi, err := fs.Upload(ctx, r, reqPath) if err != nil { return http.StatusMethodNotAllowed, err } - _, fi := isPathExist(ctx, fs, reqPath) + //_, fi := isPathExist(ctx, fs, reqPath) + // 为防止有些网盘上传有延时,这里认为上传没有报错就已经成功了,不再去判断是否存在 etag, err := findETag(ctx, fs, h.LockSystem, reqPath, fi) if err != nil { return http.StatusInternalServerError, err @@ -297,7 +292,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst return http.StatusCreated, nil } -// OK func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { reqPath, status, err := h.stripPrefix(r.URL.Path) if err != nil { @@ -325,7 +319,6 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSy return http.StatusCreated, nil } -// OK func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { hdr := r.Header.Get("Destination") @@ -410,7 +403,6 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *Fil return moveFiles(ctx, fs, src, dst, r.Header.Get("Overwrite") == "T") } -// OK func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (retStatus int, retErr error) { duration, err := parseTimeout(r.Header.Get("Timeout")) @@ -506,7 +498,6 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSys return 0, nil } -// OK func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { // http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the @@ -531,7 +522,6 @@ func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileS } } -// OK func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) { reqPath, status, err := h.stripPrefix(r.URL.Path) if err != nil { From 908cdd2c78c34389c95285ad88accfd05601dd37 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Thu, 17 Mar 2022 21:56:50 +0800 Subject: [PATCH 09/10] revert: undo delete upFileMap --- server/webdav/file.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/server/webdav/file.go b/server/webdav/file.go index 5c8ddb2d..995cc26b 100644 --- a/server/webdav/file.go +++ b/server/webdav/file.go @@ -24,8 +24,13 @@ import ( type FileSystem struct{} +var upFileMap = make(map[string]*model.File) + func (fs *FileSystem) File(rawPath string) (*model.File, error) { rawPath = utils.ParsePath(rawPath) + if f, ok := upFileMap[rawPath]; ok { + return f, nil + } if model.AccountsCount() > 1 && rawPath == "/" { now := time.Now() return &model.File{ @@ -170,7 +175,10 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin } if fileSize == 0 { // 如果文件大小为0,默认成功 + upFileMap[rawPath] = fi return fi, nil + } else { + delete(upFileMap, rawPath) } fileData := model.FileStream{ MIMEType: r.Header.Get("Content-Type"), From 9b23d0ab29157d4a45783e7f087b55f8626ee5c9 Mon Sep 17 00:00:00 2001 From: Xhofe Date: Fri, 18 Mar 2022 17:08:43 +0800 Subject: [PATCH 10/10] docs: add sponsors [skip ci] --- README.md | 4 ++++ README_cn.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 5c69fcfc..b3ab3133 100755 --- a/README.md +++ b/README.md @@ -69,6 +69,10 @@ Available at: . +## Special sponsors +- [Find Resources - Aliyundrive Resource Search Engine](https://zhaoziyuan.la/) +- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/) + ## License The `AList` is open-source software licensed under the AGPL-3.0 license. diff --git a/README_cn.md b/README_cn.md index 30b3599c..8790892e 100644 --- a/README_cn.md +++ b/README_cn.md @@ -69,6 +69,10 @@ +## 特别赞助 +- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/) +- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/) + ## 许可 `AList` 是在 AGPL-3.0 许可下许可的开源软件。