From 453d7da62290664f3d4c762e29286a1bc516c4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BF=AB=E4=B9=90=E7=9A=84=E8=80=81=E9=BC=A0=E5=AE=9D?= =?UTF-8?q?=E5=AE=9D?= Date: Fri, 28 Jun 2024 23:47:21 +0800 Subject: [PATCH 1/4] docs: change outdated repository link to alist-org (#6007) --- .github/ISSUE_TEMPLATE/config.yml | 2 +- README.md | 14 +++++++------- README_cn.md | 14 +++++++------- README_ja.md | 14 +++++++------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index be284ab6178..9012760c8f3 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Questions & Discussions - url: https://github.com/Xhofe/alist/discussions + url: https://github.com/alist-org/alist/discussions about: Use GitHub discussions for message-board style questions and discussions. \ No newline at end of file diff --git a/README.md b/README.md index 702638e11c7..9f0b7ab8312 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,13 @@ latest version - + License - + Build status - + latest version @@ -19,13 +19,13 @@
- + discussions discussions - + Downloads @@ -106,7 +106,7 @@ English | [中文](./README_cn.md)| [日本語](./README_ja.md) | [Contributing] ## Discussion -Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports and feature requests only.** +Please go to our [discussion forum](https://github.com/alist-org/alist/discussions) for general questions, **issues are for bug reports and feature requests only.** ## Sponsor @@ -138,4 +138,4 @@ The `AList` is open-source software licensed under the AGPL-3.0 license. --- -> [@Blog](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2) +> [@Blog](https://nn.ci/) · [@GitHub](https://github.com/alist-org) · [@TelegramGroup](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2) diff --git a/README_cn.md b/README_cn.md index f268d383c8b..ec45c6ef9bc 100644 --- a/README_cn.md +++ b/README_cn.md @@ -5,13 +5,13 @@ latest version - + License - + Build status - + latest version @@ -19,13 +19,13 @@
- + discussions discussions - + Downloads @@ -105,7 +105,7 @@ ## 讨论 -一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ,**issue仅针对错误报告和功能请求。** +一般问题请到[讨论论坛](https://github.com/alist-org/alist/discussions) ,**issue仅针对错误报告和功能请求。** ## 赞助 @@ -136,4 +136,4 @@ Thanks goes to these wonderful people: --- -> [@博客](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@Telegram群](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2) +> [@博客](https://nn.ci/) · [@GitHub](https://github.com/alist-org) · [@Telegram群](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2) diff --git a/README_ja.md b/README_ja.md index 7cef979f75e..ef1351dfd8b 100644 --- a/README_ja.md +++ b/README_ja.md @@ -5,13 +5,13 @@ latest version - + License - + Build status - + latest version @@ -19,13 +19,13 @@
- + discussions discussions - + Downloads @@ -106,7 +106,7 @@ ## ディスカッション -一般的なご質問は[ディスカッションフォーラム](https://github.com/Xhofe/alist/discussions)をご利用ください。**問題はバグレポートと機能リクエストのみです。** +一般的なご質問は[ディスカッションフォーラム](https://github.com/alist-org/alist/discussions)をご利用ください。**問題はバグレポートと機能リクエストのみです。** ## スポンサー @@ -138,4 +138,4 @@ https://alist.nn.ci/guide/sponsor.html --- -> [@Blog](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@TelegramGroup](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2) +> [@Blog](https://nn.ci/) · [@GitHub](https://github.com/alist-org) · [@TelegramGroup](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2) From 227d034db85015f286b43091643ee8b42233dbdf Mon Sep 17 00:00:00 2001 From: XZB-1248 <28593573+XZB-1248@users.noreply.github.com> Date: Fri, 28 Jun 2024 23:50:00 +0800 Subject: [PATCH 2/4] feat(sftp): add suport for passphrase of private key (#6624 close #6592) Co-authored-by: XZB --- drivers/sftp/meta.go | 1 + drivers/sftp/util.go | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/sftp/meta.go b/drivers/sftp/meta.go index bdc3d827ff2..9b1665679cd 100644 --- a/drivers/sftp/meta.go +++ b/drivers/sftp/meta.go @@ -10,6 +10,7 @@ type Addition struct { Username string `json:"username" required:"true"` PrivateKey string `json:"private_key" type:"text"` Password string `json:"password"` + Passphrase string `json:"passphrase"` driver.RootPath IgnoreSymlinkError bool `json:"ignore_symlink_error" default:"false" info:"Ignore symlink error"` } diff --git a/drivers/sftp/util.go b/drivers/sftp/util.go index eaeeaff5814..53f9c379e04 100644 --- a/drivers/sftp/util.go +++ b/drivers/sftp/util.go @@ -12,8 +12,14 @@ import ( func (d *SFTP) initClient() error { var auth ssh.AuthMethod - if d.PrivateKey != "" { - signer, err := ssh.ParsePrivateKey([]byte(d.PrivateKey)) + if len(d.PrivateKey) > 0 { + var err error + var signer ssh.Signer + if len(d.Passphrase) > 0 { + signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(d.PrivateKey), []byte(d.Passphrase)) + } else { + signer, err = ssh.ParsePrivateKey([]byte(d.PrivateKey)) + } if err != nil { return err } From 432901db5af14891b210b6dd792c44ac9409ad88 Mon Sep 17 00:00:00 2001 From: YangXu <47767754+Three-taile-dragon@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:59:07 +0800 Subject: [PATCH 3/4] feat(thunderx): generate UserAgent automatically (#6664) --- drivers/thunderx/driver.go | 120 +++++++++++++++++++++++-------------- drivers/thunderx/meta.go | 14 ++--- drivers/thunderx/util.go | 99 +++++++++++++++++++++++++++++- 3 files changed, 179 insertions(+), 54 deletions(-) diff --git a/drivers/thunderx/driver.go b/drivers/thunderx/driver.go index 7b5daf414ac..b9ee668c2f9 100644 --- a/drivers/thunderx/driver.go +++ b/drivers/thunderx/driver.go @@ -3,10 +3,6 @@ package thunderx import ( "context" "fmt" - "github.com/go-resty/resty/v2" - "net/http" - "strings" - "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/driver" "github.com/alist-org/alist/v3/internal/errs" @@ -18,6 +14,9 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/go-resty/resty/v2" + "net/http" + "strings" ) type ThunderX struct { @@ -41,26 +40,15 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { if x.XunLeiXCommon == nil { x.XunLeiXCommon = &XunLeiXCommon{ Common: &Common{ - client: base.NewRestyClient(), - Algorithms: []string{ - "lHwINjLeqssT28Ym99p5MvR", - "xvFcxvtqPKCa9Ajf", - "2ywOP8spKHzfuhZMUYZ9IpsViq0t8vT0", - "FTBrJism20SHKQ2m2", - "BHrWJsPwjnr5VeLtOUr2191X9uXhWmt", - "yu0QgHEjNmDoPNwXN17so2hQlDT83T", - "OcaMfLMCGZ7oYlvZGIbTqb4U7cCY", - "jBGGu0GzXOjtCXYwkOBb+c6TZ/Nymv", - "YLWRjVor2rOuYEL", - "94wjoPazejyNC+gRpOj+JOm1XXvxa", - }, + client: base.NewRestyClient(), + Algorithms: Algorithms, DeviceID: utils.GetMD5EncodeStr(x.Username + x.Password), - ClientID: "ZQL_zwA4qhHcoe_2", - ClientSecret: "Og9Vr1L8Ee6bh0olFxFDRg", - ClientVersion: "1.05.0.2115", - PackageName: "com.thunder.downloader", - UserAgent: "ANDROID-com.thunder.downloader/1.05.0.2115 netWorkType/5G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_14_186-perf-gddfs8vbb238b) (JAVA 0)", - DownloadUserAgent: "Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)", + ClientID: ClientID, + ClientSecret: ClientSecret, + ClientVersion: ClientVersion, + PackageName: PackageName, + UserAgent: BuildCustomUserAgent(utils.GetMD5EncodeStr(x.Username+x.Password), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, ""), + DownloadUserAgent: DownloadUserAgent, UseVideoUrl: x.UseVideoUrl, refreshCTokenCk: func(token string) { @@ -76,6 +64,10 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { token, err = x.Login(x.Username, x.Password) if err != nil { x.GetStorage().SetStatus(fmt.Sprintf("%+v", err.Error())) + if token.UserID != "" { + x.SetUserID(token.UserID) + x.UserAgent = BuildCustomUserAgent(utils.GetMD5EncodeStr(x.Username+x.Password), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, token.UserID) + } op.MustSaveDriverStorage(x) } } @@ -86,10 +78,14 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { } // 自定义验证码token - ctoekn := strings.TrimSpace(x.CaptchaToken) - if ctoekn != "" { - x.SetCaptchaToken(ctoekn) + ctoken := strings.TrimSpace(x.CaptchaToken) + if ctoken != "" { + x.SetCaptchaToken(ctoken) + } + if x.DeviceID == "" { + x.SetDeviceID(utils.GetMD5EncodeStr(x.Username + x.Password)) } + x.XunLeiXCommon.UseVideoUrl = x.UseVideoUrl x.Addition.RootFolderID = x.RootFolderID // 防止重复登录 @@ -102,6 +98,10 @@ func (x *ThunderX) Init(ctx context.Context) (err error) { return err } x.SetTokenResp(token) + if token.UserID != "" { + x.SetUserID(token.UserID) + x.UserAgent = BuildCustomUserAgent(x.DeviceID, ClientID, PackageName, SdkVersion, ClientVersion, PackageName, token.UserID) + } } return nil } @@ -137,18 +137,33 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { DeviceID: func() string { if len(x.DeviceID) != 32 { - return utils.GetMD5EncodeStr(x.DeviceID) + if x.LoginType == "user" { + return utils.GetMD5EncodeStr(x.Username + x.Password) + } + return utils.GetMD5EncodeStr(x.ExpertAddition.RefreshToken) } return x.DeviceID }(), - ClientID: x.ClientID, - ClientSecret: x.ClientSecret, - ClientVersion: x.ClientVersion, - PackageName: x.PackageName, - UserAgent: x.UserAgent, - DownloadUserAgent: x.DownloadUserAgent, - UseVideoUrl: x.UseVideoUrl, - + ClientID: x.ClientID, + ClientSecret: x.ClientSecret, + ClientVersion: x.ClientVersion, + PackageName: x.PackageName, + UserAgent: func() string { + if x.ExpertAddition.UserAgent != "" { + return x.ExpertAddition.UserAgent + } + if x.LoginType == "user" { + return BuildCustomUserAgent(utils.GetMD5EncodeStr(x.Username+x.Password), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, "") + } + return BuildCustomUserAgent(utils.GetMD5EncodeStr(x.ExpertAddition.RefreshToken), ClientID, PackageName, SdkVersion, ClientVersion, PackageName, "") + }(), + DownloadUserAgent: func() string { + if x.ExpertAddition.DownloadUserAgent != "" { + return x.ExpertAddition.DownloadUserAgent + } + return DownloadUserAgent + }(), + UseVideoUrl: x.UseVideoUrl, refreshCTokenCk: func(token string) { x.CaptchaToken = token op.MustSaveDriverStorage(x) @@ -156,8 +171,17 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { }, } - if x.CaptchaToken != "" { - x.SetCaptchaToken(x.CaptchaToken) + if x.ExpertAddition.CaptchaToken != "" { + x.SetCaptchaToken(x.ExpertAddition.CaptchaToken) + op.MustSaveDriverStorage(x) + } + if x.Common.DeviceID != "" { + x.ExpertAddition.DeviceID = x.Common.DeviceID + op.MustSaveDriverStorage(x) + } + if x.Common.DownloadUserAgent != "" { + x.ExpertAddition.DownloadUserAgent = x.Common.DownloadUserAgent + op.MustSaveDriverStorage(x) } x.XunLeiXCommon.UseVideoUrl = x.UseVideoUrl x.ExpertAddition.RootFolderID = x.RootFolderID @@ -177,7 +201,6 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { return err } x.SetTokenResp(token) - // 刷新token方法 x.SetRefreshTokenFunc(func() error { token, err := x.XunLeiXCommon.RefreshToken(x.TokenResp.RefreshToken) @@ -208,13 +231,19 @@ func (x *ThunderXExpert) Init(ctx context.Context) (err error) { return err }) } + // 更新 UserAgent + if x.TokenResp.UserID != "" { + x.ExpertAddition.UserAgent = BuildCustomUserAgent(x.ExpertAddition.DeviceID, ClientID, PackageName, SdkVersion, ClientVersion, PackageName, x.TokenResp.UserID) + x.SetUserAgent(x.ExpertAddition.UserAgent) + op.MustSaveDriverStorage(x) + } } else { // 仅修改验证码token if x.CaptchaToken != "" { x.SetCaptchaToken(x.CaptchaToken) } - x.XunLeiXCommon.UserAgent = x.UserAgent - x.XunLeiXCommon.DownloadUserAgent = x.DownloadUserAgent + x.XunLeiXCommon.UserAgent = x.ExpertAddition.UserAgent + x.XunLeiXCommon.DownloadUserAgent = x.ExpertAddition.UserAgent x.XunLeiXCommon.UseVideoUrl = x.UseVideoUrl x.ExpertAddition.RootFolderID = x.RootFolderID } @@ -426,17 +455,17 @@ func (xc *XunLeiXCommon) getFiles(ctx context.Context, folderId string) ([]model return files, nil } -// 设置刷新Token的方法 +// SetRefreshTokenFunc 设置刷新Token的方法 func (xc *XunLeiXCommon) SetRefreshTokenFunc(fn func() error) { xc.refreshTokenFunc = fn } -// 设置Token +// SetTokenResp 设置Token func (xc *XunLeiXCommon) SetTokenResp(tr *TokenResp) { xc.TokenResp = tr } -// 携带Authorization和CaptchaToken的请求 +// Request 携带Authorization和CaptchaToken的请求 func (xc *XunLeiXCommon) Request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { data, err := xc.Common.Request(url, method, func(req *resty.Request) { req.SetHeaders(map[string]string{ @@ -473,7 +502,7 @@ func (xc *XunLeiXCommon) Request(url string, method string, callback base.ReqCal return xc.Request(url, method, callback, resp) } -// 刷新Token +// RefreshToken 刷新Token func (xc *XunLeiXCommon) RefreshToken(refreshToken string) (*TokenResp, error) { var resp TokenResp _, err := xc.Common.Request(XLUSER_API_URL+"/auth/token", http.MethodPost, func(req *resty.Request) { @@ -491,10 +520,11 @@ func (xc *XunLeiXCommon) RefreshToken(refreshToken string) (*TokenResp, error) { if resp.RefreshToken == "" { return nil, errs.EmptyToken } + resp.UserID = resp.Sub return &resp, nil } -// 登录 +// Login 登录 func (xc *XunLeiXCommon) Login(username, password string) (*TokenResp, error) { url := XLUSER_API_URL + "/auth/signin" err := xc.RefreshCaptchaTokenInLogin(GetAction(http.MethodPost, url), username) diff --git a/drivers/thunderx/meta.go b/drivers/thunderx/meta.go index 2c114c0f284..fa60ebbdb0a 100644 --- a/drivers/thunderx/meta.go +++ b/drivers/thunderx/meta.go @@ -23,7 +23,7 @@ type ExpertAddition struct { RefreshToken string `json:"refresh_token" required:"true" help:"login type is refresh_token,this is required"` // 签名方法1 - Algorithms string `json:"algorithms" required:"true" help:"sign type is algorithms,this is required" default:"lHwINjLeqssT28Ym99p5MvR,xvFcxvtqPKCa9Ajf,2ywOP8spKHzfuhZMUYZ9IpsViq0t8vT0,FTBrJism20SHKQ2m2,BHrWJsPwjnr5VeLtOUr2191X9uXhWmt,yu0QgHEjNmDoPNwXN17so2hQlDT83T,OcaMfLMCGZ7oYlvZGIbTqb4U7cCY,jBGGu0GzXOjtCXYwkOBb+c6TZ/Nymv,YLWRjVor2rOuYEL,94wjoPazejyNC+gRpOj+JOm1XXvxa"` + Algorithms string `json:"algorithms" required:"true" help:"sign type is algorithms,this is required" default:"kVy0WbPhiE4v6oxXZ88DvoA3Q,lON/AUoZKj8/nBtcE85mVbkOaVdVa,rLGffQrfBKH0BgwQ33yZofvO3Or,FO6HWqw,GbgvyA2,L1NU9QvIQIH7DTRt,y7llk4Y8WfYflt6,iuDp1WPbV3HRZudZtoXChxH4HNVBX5ZALe,8C28RTXmVcco0,X5Xh,7xe25YUgfGgD0xW3ezFS,,CKCR,8EmDjBo6h3eLaK7U6vU2Qys0NsMx,t2TeZBXKqbdP09Arh9C3"` // 签名方法2 CaptchaSign string `json:"captcha_sign" required:"true" help:"sign type is captcha_sign,this is required"` Timestamp string `json:"timestamp" required:"true" help:"sign type is captcha_sign,this is required"` @@ -32,15 +32,15 @@ type ExpertAddition struct { CaptchaToken string `json:"captcha_token"` // 必要且影响登录,由签名决定 - DeviceID string `json:"device_id" required:"true" default:"9aa5c268e7bcfc197a9ad88e2fb330e5"` + DeviceID string `json:"device_id" required:"false" default:""` ClientID string `json:"client_id" required:"true" default:"ZQL_zwA4qhHcoe_2"` ClientSecret string `json:"client_secret" required:"true" default:"Og9Vr1L8Ee6bh0olFxFDRg"` - ClientVersion string `json:"client_version" required:"true" default:"1.05.0.2115"` + ClientVersion string `json:"client_version" required:"true" default:"1.06.0.2132"` PackageName string `json:"package_name" required:"true" default:"com.thunder.downloader"` - //不影响登录,影响下载速度 - UserAgent string `json:"user_agent" required:"true" default:"ANDROID-com.thunder.downloader/1.05.0.2115 netWorkType/4G appid/40 deviceName/Xiaomi_M2004j7ac deviceModel/M2004J7AC OSVersion/12 protocolVersion/301 platformVersion/10 sdkVersion/220200 Oauth2Client/0.9 (Linux 4_14_186-perf-gdcf98eab238b) (JAVA 0)"` - DownloadUserAgent string `json:"download_user_agent" required:"true" default:"Dalvik/2.1.0 (Linux; U; Android 12; M2004J7AC Build/SP1A.210812.016)"` + ////不影响登录,影响下载速度 + UserAgent string `json:"user_agent" required:"false" default:""` + DownloadUserAgent string `json:"download_user_agent" required:"false" default:""` //优先使用视频链接代替下载链接 UseVideoUrl bool `json:"use_video_url"` @@ -85,7 +85,7 @@ func (i *Addition) GetIdentity() string { var config = driver.Config{ Name: "ThunderX", LocalSort: true, - OnlyProxy: true, + OnlyProxy: false, } var configExpert = driver.Config{ diff --git a/drivers/thunderx/util.go b/drivers/thunderx/util.go index 6fa323ebc28..661da87e0b0 100644 --- a/drivers/thunderx/util.go +++ b/drivers/thunderx/util.go @@ -1,12 +1,14 @@ package thunderx import ( + "crypto/md5" "crypto/sha1" "encoding/hex" "fmt" "io" "net/http" "regexp" + "strings" "time" "github.com/alist-org/alist/v3/drivers/base" @@ -20,6 +22,33 @@ const ( XLUSER_API_URL = "https://xluser-ssl.xunleix.com/v1" ) +var Algorithms = []string{ + "kVy0WbPhiE4v6oxXZ88DvoA3Q", + "lON/AUoZKj8/nBtcE85mVbkOaVdVa", + "rLGffQrfBKH0BgwQ33yZofvO3Or", + "FO6HWqw", + "GbgvyA2", + "L1NU9QvIQIH7DTRt", + "y7llk4Y8WfYflt6", + "iuDp1WPbV3HRZudZtoXChxH4HNVBX5ZALe", + "8C28RTXmVcco0", + "X5Xh", + "7xe25YUgfGgD0xW3ezFS", + "", + "CKCR", + "8EmDjBo6h3eLaK7U6vU2Qys0NsMx", + "t2TeZBXKqbdP09Arh9C3", +} + +const ( + ClientID = "ZQL_zwA4qhHcoe_2" + ClientSecret = "Og9Vr1L8Ee6bh0olFxFDRg" + ClientVersion = "1.06.0.2132" + PackageName = "com.thunder.downloader" + DownloadUserAgent = "Dalvik/2.1.0 (Linux; U; Android 13; M2004J7AC Build/SP1A.210812.016)" + SdkVersion = "2.0.3.203100 " +) + const ( FOLDER = "drive#folder" FILE = "drive#file" @@ -42,7 +71,7 @@ type Common struct { client *resty.Client captchaToken string - + userID string // 签名相关,二选一 Algorithms []string Timestamp, CaptchaSign string @@ -61,6 +90,18 @@ type Common struct { refreshCTokenCk func(token string) } +func (c *Common) SetDeviceID(deviceID string) { + c.DeviceID = deviceID +} + +func (c *Common) SetUserID(userID string) { + c.userID = userID +} + +func (c *Common) SetUserAgent(userAgent string) { + c.UserAgent = userAgent +} + func (c *Common) SetCaptchaToken(captchaToken string) { c.captchaToken = captchaToken } @@ -145,7 +186,7 @@ func (c *Common) refreshCaptchaToken(action string, metas map[string]string) err return nil } -// 只有基础信息的请求 +// Request 只有基础信息的请求 func (c *Common) Request(url, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) { req := c.client.R().SetHeaders(map[string]string{ "user-agent": c.UserAgent, @@ -200,3 +241,57 @@ func getGcid(r io.Reader, size int64) (string, error) { } return hex.EncodeToString(hash1.Sum(nil)), nil } + +func generateDeviceSign(deviceID, packageName string) string { + + signatureBase := fmt.Sprintf("%s%s%s%s", deviceID, packageName, "1", "appkey") + + sha1Hash := sha1.New() + sha1Hash.Write([]byte(signatureBase)) + sha1Result := sha1Hash.Sum(nil) + + sha1String := hex.EncodeToString(sha1Result) + + md5Hash := md5.New() + md5Hash.Write([]byte(sha1String)) + md5Result := md5Hash.Sum(nil) + + md5String := hex.EncodeToString(md5Result) + + deviceSign := fmt.Sprintf("div101.%s%s", deviceID, md5String) + + return deviceSign +} + +func BuildCustomUserAgent(deviceID, clientID, appName, sdkVersion, clientVersion, packageName, userID string) string { + deviceSign := generateDeviceSign(deviceID, packageName) + var sb strings.Builder + + sb.WriteString(fmt.Sprintf("ANDROID-%s/%s ", appName, clientVersion)) + sb.WriteString("protocolVersion/200 ") + sb.WriteString("accesstype/ ") + sb.WriteString(fmt.Sprintf("clientid/%s ", clientID)) + sb.WriteString(fmt.Sprintf("clientversion/%s ", clientVersion)) + sb.WriteString("action_type/ ") + sb.WriteString("networktype/WIFI ") + sb.WriteString("sessionid/ ") + sb.WriteString(fmt.Sprintf("deviceid/%s ", deviceID)) + sb.WriteString("providername/NONE ") + sb.WriteString(fmt.Sprintf("devicesign/%s ", deviceSign)) + sb.WriteString("refresh_token/ ") + sb.WriteString(fmt.Sprintf("sdkversion/%s ", sdkVersion)) + sb.WriteString(fmt.Sprintf("datetime/%d ", time.Now().UnixMilli())) + sb.WriteString(fmt.Sprintf("usrno/%s ", userID)) + sb.WriteString(fmt.Sprintf("appname/%s ", appName)) + sb.WriteString(fmt.Sprintf("session_origin/ ")) + sb.WriteString(fmt.Sprintf("grant_type/ ")) + sb.WriteString(fmt.Sprintf("appid/ ")) + sb.WriteString(fmt.Sprintf("clientip/ ")) + sb.WriteString(fmt.Sprintf("devicename/Xiaomi_M2004j7ac ")) + sb.WriteString(fmt.Sprintf("osversion/13 ")) + sb.WriteString(fmt.Sprintf("platformversion/10 ")) + sb.WriteString(fmt.Sprintf("accessmode/ ")) + sb.WriteString(fmt.Sprintf("devicemodel/M2004J7AC ")) + + return sb.String() +} From 270587723579f717d6da0c3e5b7131d72f919b1a Mon Sep 17 00:00:00 2001 From: lany Date: Tue, 2 Jul 2024 15:30:00 +0800 Subject: [PATCH 4/4] fix(iLanZou): resolve resource access issue (#6673) * fix(drivers/iLanZou): resolve resource access issue on iLanZou driver mount The driver failed to mount due to incorrect URL parameter ordering which the backend did not accept This commit reorders the parameters to meet the backend's expectations and ensures successful mounting of the iLanZou driver. Closes #6271, Closes #6415 * fix(drivers/iLanZou): Fixed the error ID number returned when creating a folder Closes #6610, Closes #6333 --------- Co-authored-by: maye174 <96584640+maye174@users.noreply.github.com> --- drivers/ilanzou/driver.go | 72 +++++++++++++++++++-------------------- drivers/ilanzou/util.go | 49 +++++++++++++++----------- 2 files changed, 64 insertions(+), 57 deletions(-) diff --git a/drivers/ilanzou/driver.go b/drivers/ilanzou/driver.go index 63d86363962..ab5ebe7ee5d 100644 --- a/drivers/ilanzou/driver.go +++ b/drivers/ilanzou/driver.go @@ -66,18 +66,18 @@ func (d *ILanZou) Drop(ctx context.Context) error { } func (d *ILanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { - offset := 1 - limit := 60 var res []ListItem for { var resp ListResp _, err := d.proved("/record/file/list", http.MethodGet, func(req *resty.Request) { - req.SetQueryParams(map[string]string{ - "type": "0", - "folderId": dir.GetID(), - "offset": strconv.Itoa(offset), - "limit": strconv.Itoa(limit), - }).SetResult(&resp) + params := []string{ + "offset=1", + "limit=60", + "folderId=" + dir.GetID(), + "type=0", + } + queryString := strings.Join(params, "&") + req.SetQueryString(queryString).SetResult(&resp) }) if err != nil { return nil, err @@ -86,7 +86,6 @@ func (d *ILanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) if resp.TotalPage <= resp.Offset { break } - offset++ } return utils.SliceConvert(res, func(f ListItem) (model.Obj, error) { updTime, err := time.ParseInLocation("2006-01-02 15:04:05", f.UpdTime, time.Local) @@ -118,31 +117,33 @@ func (d *ILanZou) Link(ctx context.Context, file model.Obj, args model.LinkArgs) if err != nil { return nil, err } - query := u.Query() - query.Set("uuid", d.UUID) - query.Set("devType", "6") - query.Set("devCode", d.UUID) - query.Set("devModel", "chrome") - query.Set("devVersion", d.conf.devVersion) - query.Set("appVersion", "") - ts, err := getTimestamp(d.conf.secret) - if err != nil { - return nil, err + ts, ts_str, err := getTimestamp(d.conf.secret) + + params := []string{ + "uuid=" + url.QueryEscape(d.UUID), + "devType=6", + "devCode=" + url.QueryEscape(d.UUID), + "devModel=chrome", + "devVersion=" + url.QueryEscape(d.conf.devVersion), + "appVersion=", + "timestamp=" + ts_str, + "appToken=" + url.QueryEscape(d.Token), + "enable=0", } - query.Set("timestamp", ts) - query.Set("appToken", d.Token) - query.Set("enable", "1") + downloadId, err := mopan.AesEncrypt([]byte(fmt.Sprintf("%s|%s", file.GetID(), d.userID)), d.conf.secret) if err != nil { return nil, err } - query.Set("downloadId", hex.EncodeToString(downloadId)) - auth, err := mopan.AesEncrypt([]byte(fmt.Sprintf("%s|%d", file.GetID(), time.Now().UnixMilli())), d.conf.secret) + params = append(params, "downloadId="+url.QueryEscape(hex.EncodeToString(downloadId))) + + auth, err := mopan.AesEncrypt([]byte(fmt.Sprintf("%s|%d", file.GetID(), ts)), d.conf.secret) if err != nil { return nil, err } - query.Set("auth", hex.EncodeToString(auth)) - u.RawQuery = query.Encode() + params = append(params, "auth="+url.QueryEscape(hex.EncodeToString(auth))) + + u.RawQuery = strings.Join(params, "&") realURL := u.String() // get the url after redirect res, err := base.NoRedirectClient.R().SetHeaders(map[string]string{ @@ -156,12 +157,7 @@ func (d *ILanZou) Link(ctx context.Context, file model.Obj, args model.LinkArgs) if res.StatusCode() == 302 { realURL = res.Header().Get("location") } else { - contentLengthStr := res.Header().Get("Content-Length") - contentLength, err := strconv.Atoi(contentLengthStr) - if err != nil || contentLength == 0 || contentLength > 1024*10 { - return nil, fmt.Errorf("redirect failed, status: %d", res.StatusCode()) - } - return nil, fmt.Errorf("redirect failed, content: %s", res.String()) + return nil, fmt.Errorf("redirect failed, status: %d, msg: %s", res.StatusCode(), utils.Json.Get(res.Body(), "msg").ToString()) } link := model.Link{URL: realURL} return &link, nil @@ -179,7 +175,7 @@ func (d *ILanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName stri return nil, err } return &model.Object{ - ID: utils.Json.Get(res, "list", "0", "id").ToString(), + ID: utils.Json.Get(res, "list", 0, "id").ToString(), //Path: "", Name: dirName, Size: 0, @@ -348,10 +344,12 @@ func (d *ILanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileSt var resp UploadResultResp for i := 0; i < 10; i++ { _, err = d.unproved("/7n/results", http.MethodPost, func(req *resty.Request) { - req.SetQueryParams(map[string]string{ - "tokenList": token, - "tokenTime": time.Now().Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)"), - }).SetResult(&resp) + params := []string{ + "tokenList=" + token, + "tokenTime=" + time.Now().Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)"), + } + queryString := strings.Join(params, "&") + req.SetQueryString(queryString).SetResult(&resp) }) if err != nil { return nil, err diff --git a/drivers/ilanzou/util.go b/drivers/ilanzou/util.go index c9a30765b7f..a57e2a4a6be 100644 --- a/drivers/ilanzou/util.go +++ b/drivers/ilanzou/util.go @@ -4,7 +4,9 @@ import ( "encoding/hex" "fmt" "net/http" + "net/url" "strconv" + "strings" "time" "github.com/alist-org/alist/v3/drivers/base" @@ -31,45 +33,52 @@ func (d *ILanZou) login() error { return nil } -func getTimestamp(secret []byte) (string, error) { +func getTimestamp(secret []byte) (int64, string, error) { ts := time.Now().UnixMilli() tsStr := strconv.FormatInt(ts, 10) res, err := mopan.AesEncrypt([]byte(tsStr), secret) if err != nil { - return "", err + return 0, "", err } - return hex.EncodeToString(res), nil + return ts, hex.EncodeToString(res), nil } func (d *ILanZou) request(pathname, method string, callback base.ReqCallback, proved bool, retry ...bool) ([]byte, error) { - req := base.RestyClient.R() - ts, err := getTimestamp(d.conf.secret) + _, ts_str, err := getTimestamp(d.conf.secret) if err != nil { return nil, err } - req.SetQueryParams(map[string]string{ - "uuid": d.UUID, - "devType": "6", - "devCode": d.UUID, - "devModel": "chrome", - "devVersion": d.conf.devVersion, - "appVersion": "", - "timestamp": ts, - //"appToken": d.Token, - "extra": "2", - }) + + params := []string{ + "uuid=" + url.QueryEscape(d.UUID), + "devType=6", + "devCode=" + url.QueryEscape(d.UUID), + "devModel=chrome", + "devVersion=" + url.QueryEscape(d.conf.devVersion), + "appVersion=", + "timestamp=" + ts_str, + } + + if proved { + params = append(params, "appToken="+url.QueryEscape(d.Token)) + } + + params = append(params, "extra=2") + + queryString := strings.Join(params, "&") + + req := base.RestyClient.R() req.SetHeaders(map[string]string{ "Origin": d.conf.site, "Referer": d.conf.site + "/", "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0", }) - if proved { - req.SetQueryParam("appToken", d.Token) - } + if callback != nil { callback(req) } - res, err := req.Execute(method, d.conf.base+pathname) + + res, err := req.Execute(method, d.conf.base+pathname+"?"+queryString) if err != nil { if res != nil { log.Errorf("[iLanZou] request error: %s", res.String())