diff --git a/drivers/123/123.go b/drivers/123/123.go index 3f7eda2e..f647a840 100644 --- a/drivers/123/123.go +++ b/drivers/123/123.go @@ -1,12 +1,17 @@ package _23 import ( + "crypto/hmac" + "crypto/sha256" + "errors" "fmt" "github.com/Xhofe/alist/conf" "github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/model" "github.com/Xhofe/alist/utils" "github.com/go-resty/resty/v2" + jsoniter "github.com/json-iterator/go" + "math/rand" "path/filepath" "strconv" "time" @@ -122,6 +127,20 @@ func (driver Pan123) GetFiles(parentId string, account *model.Account) ([]Pan123 return res, nil } +func (driver Pan123) Post(url string, data base.Json, account *model.Account) ([]byte, error) { + res, err := pan123Client.R(). + SetHeader("authorization", "Bearer "+account.AccessToken). + SetBody(data).Post(url) + if err != nil { + return nil, err + } + body := res.Body() + if jsoniter.Get(body, "code").ToInt() != 0 { + return nil, errors.New(jsoniter.Get(body, "message").ToString()) + } + return body, nil +} + func (driver Pan123) GetFile(path string, account *model.Account) (*Pan123File, error) { dir, name := filepath.Split(path) dir = utils.ParsePath(dir) @@ -143,6 +162,28 @@ func (driver Pan123) GetFile(path string, account *model.Account) (*Pan123File, return nil, base.ErrPathNotFound } +func RandStr(length int) string { + str := "123456789abcdefghijklmnopqrstuvwxyz" + bytes := []byte(str) + var result []byte + rand.Seed(time.Now().UnixNano()+ int64(rand.Intn(100))) + for i := 0; i < length; i++ { + result = append(result, bytes[rand.Intn(len(bytes))]) + } + return string(result) +} + +func HMAC(message string, secret string) string { + key := []byte(secret) + h := hmac.New(sha256.New, key) + h.Write([]byte(message)) + // fmt.Println(h.Sum(nil)) + //sha := hex.EncodeToString(h.Sum(nil)) + // fmt.Println(sha) + //return sha + return string(h.Sum(nil)) +} + func init() { base.RegisterDriver(&Pan123{}) pan123Client.SetRetryCount(3) diff --git a/drivers/123/driver.go b/drivers/123/driver.go index bde001e1..a469d512 100644 --- a/drivers/123/driver.go +++ b/drivers/123/driver.go @@ -1,15 +1,21 @@ package _23 import ( + "encoding/hex" + "encoding/xml" "fmt" "github.com/Xhofe/alist/conf" "github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/model" "github.com/Xhofe/alist/utils" "github.com/gin-gonic/gin" + jsoniter "github.com/json-iterator/go" log "github.com/sirupsen/logrus" url "net/url" "path/filepath" + "strconv" + "strings" + "time" ) type Pan123 struct{} @@ -161,7 +167,7 @@ func (driver Pan123) Link(path string, account *model.Account) (*base.Link, erro link := base.Link{} if res.StatusCode() == 302 { link.Url = res.Header().Get("location") - }else { + } else { link.Url = resp.Data.DownloadUrl } return &link, nil @@ -198,22 +204,145 @@ func (driver Pan123) Preview(path string, account *model.Account) (interface{}, } func (driver Pan123) MakeDir(path string, account *model.Account) error { - return base.ErrNotImplement + dir, name := filepath.Split(path) + parentFile, err := driver.File(dir, account) + if err != nil { + return err + } + if !parentFile.IsDir() { + return base.ErrNotFolder + } + parentFileId, _ := strconv.Atoi(parentFile.Id) + data := base.Json{ + "driveId": 0, + "etag": "", + "fileName": name, + "parentFileId": parentFileId, + "size": 0, + "type": 1, + } + _, err = driver.Post("https://www.123pan.com/api/file/upload_request", data, account) + if err == nil { + _ = base.DeleteCache(dir, account) + } + return err } func (driver Pan123) Move(src string, dst string, account *model.Account) error { - return base.ErrNotImplement + srcDir, _ := filepath.Split(src) + dstDir, dstName := filepath.Split(dst) + srcFile, err := driver.File(src, account) + if err != nil { + return err + } + fileId, _ := strconv.Atoi(srcFile.Id) + // rename + if srcDir == dstDir { + data := base.Json{ + "driveId": 0, + "fileId": fileId, + "fileName": dstName, + } + _, err = driver.Post("https://www.123pan.com/api/file/rename", data, account) + } else { + // move + dstDirFile, err := driver.File(dstDir, account) + if err != nil { + return err + } + parentFileId, _ := strconv.Atoi(dstDirFile.Id) + data := base.Json{ + "fileId": fileId, + "parentFileId": parentFileId, + } + _, err = driver.Post("https://www.123pan.com/api/file/mod_pid", data, account) + } + if err != nil { + _ = base.DeleteCache(srcDir, account) + _ = base.DeleteCache(dstDir, account) + } + return err } func (driver Pan123) Copy(src string, dst string, account *model.Account) error { - return base.ErrNotImplement + return base.ErrNotSupport } func (driver Pan123) Delete(path string, account *model.Account) error { - return base.ErrNotImplement + file, err := driver.GetFile(path, account) + if err != nil { + return err + } + data := base.Json{ + "driveId": 0, + "operation": true, + "fileTrashInfoList": file, + } + _, err = driver.Post("https://www.123pan.com/api/file/trash", data, account) + if err == nil { + _ = base.DeleteCache(utils.Dir(path), account) + } + return err } +type UploadResp struct { + XMLName xml.Name `xml:"InitiateMultipartUploadResult"` + Bucket string `xml:"Bucket"` + Key string `xml:"Key"` + UploadId string `xml:"UploadId"` +} + +// TODO unfinished func (driver Pan123) Upload(file *model.FileStream, account *model.Account) error { + parentFile, err := driver.File(file.ParentPath, account) + if err != nil { + return err + } + if !parentFile.IsDir() { + return base.ErrNotFolder + } + parentFileId, _ := strconv.Atoi(parentFile.Id) + data := base.Json{ + "driveId": 0, + "duplicate": true, + "etag": RandStr(32), //maybe file's md5 + "fileName": file.GetFileName(), + "parentFileId": parentFileId, + "size": file.GetSize(), + "type": 0, + } + res, err := driver.Post("https://www.123pan.com/api/file/upload_request", data, account) + if err != nil { + return err + } + baseUrl := fmt.Sprintf("https://file.123pan.com/%s/%s", jsoniter.Get(res, "data.Bucket").ToString(), jsoniter.Get(res, "data.Key").ToString()) + var resp UploadResp + kSecret := jsoniter.Get(res, "data.SecretAccessKey").ToString() + nowTimeStr := time.Now().String() + Date := strings.ReplaceAll(strings.Split(nowTimeStr, "T")[0],"-","") + + StringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", + "AWS4-HMAC-SHA256", + nowTimeStr, + fmt.Sprintf("%s/us-east-1/s3/aws4_request", Date), + ) + + kDate := HMAC("AWS4"+kSecret, Date) + kRegion := HMAC(kDate, "us-east-1") + kService := HMAC(kRegion, "s3") + kSigning := HMAC(kService, "aws4_request") + _, err = pan123Client.R().SetResult(&resp).SetHeaders(map[string]string{ + "Authorization": fmt.Sprintf("AWS4-HMAC-SHA256 Credential=%s/%s/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date;x-amz-security-token;x-amz-user-agent, Signature=%s", + jsoniter.Get(res, "data.AccessKeyId"), + Date, + hex.EncodeToString([]byte(HMAC(StringToSign, kSigning)))), + "X-Amz-Content-Sha256": "UNSIGNED-PAYLOAD", + "X-Amz-Date": nowTimeStr, + "x-amz-security-token": jsoniter.Get(res, "data.SessionToken").ToString(), + }).Post(fmt.Sprintf("%s?uploads", baseUrl)) + if err != nil { + return err + } return base.ErrNotImplement }