Skip to content

Commit

Permalink
Merge remote-tracking branch 'wiserain/mod'
Browse files Browse the repository at this point in the history
  • Loading branch information
Sakura-Byte committed Sep 20, 2024
2 parents 06103cd + b387881 commit e9d24f7
Show file tree
Hide file tree
Showing 10 changed files with 429 additions and 62 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
fail-fast: false
matrix:
job_name: ['linux', 'linux_386', 'mac_amd64', 'mac_arm64', 'windows']
# job_name: ['linux', 'linux_386', 'mac_amd64', 'mac_arm64', 'windows', 'other_os', 'go1.20', 'go1.21']
# job_name: ['linux', 'linux_386', 'mac_amd64', 'mac_arm64', 'windows', 'other_os', 'go1.21', 'go1.22']

include:
- job_name: linux
Expand Down Expand Up @@ -121,7 +121,7 @@ jobs:

# - job_name: other_os
# os: ubuntu-latest
# go: '1.22.6'
# go: '>=1.23.0-rc.1'
# build_flags: '-exclude "^(windows/|darwin/|linux/)"'
# compile_all: true
# deploy: true
Expand Down
18 changes: 13 additions & 5 deletions backend/115/115.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"net/http"
"path"
"reflect"
"regexp"
"strings"
"sync"
"time"
Expand All @@ -48,8 +49,7 @@ import (
const (
domain = "www.115.com"
rootURL = "https://webapi.115.com"
appVer = "27.0.3.7"
defaultUserAgent = "Mozilla/5.0 115Browser/" + appVer
defaultUserAgent = "Mozilla/5.0 115Browser/27.0.3.7"

defaultMinSleep = fs.Duration(250 * time.Millisecond) // 4 transactions per second
maxSleep = 2 * time.Second
Expand Down Expand Up @@ -101,8 +101,6 @@ func init() {
Help: "password from share link",
Sensitive: true,
}, {
// this is useless at the moment because there's no way to upload
// without defaultUserAgent and appVer 2.0.3.6
Name: "user_agent",
Default: defaultUserAgent,
Advanced: true,
Expand Down Expand Up @@ -258,8 +256,9 @@ type Fs struct {
dirCache *dircache.DirCache // Map of directory path to directory id
pacer *fs.Pacer
rootFolderID string
appVer string // parsed from user-agent; // https://appversion.115.com/1/web/1.0/api/getMultiVer
userID string // for uploads, adding offline tasks, and receiving from share link
userkey string // only for uploads
userkey string // lazy-loaded as it's only for uploads
isShare bool // mark it is from shared or not
fileObj *fs.Object // mod
}
Expand Down Expand Up @@ -464,6 +463,15 @@ func newFs(ctx context.Context, name, path string, m configmap.Mapper) (*Fs, err
ServerSideAcrossConfigs: true, // Can copy from shared FS (this is checked in Copy/Move/DirMove)
}).Fill(ctx, f)

// setting appVer
re := regexp.MustCompile(`\d+\.\d+\.\d+(\.\d+)?$`)
if m := re.FindStringSubmatch(opt.UserAgent); m == nil {
return nil, fmt.Errorf("%q does not contain a valid app version", opt.UserAgent)
} else {
f.appVer = m[0]
fs.Debugf(nil, "Using App Version %q from User-Agent %q", f.appVer, opt.UserAgent)
}

if err := f.newClientWithPacer(ctx, opt); err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion backend/115/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ func (f *Fs) addURLs(ctx context.Context, dir string, urls []string) (info *api.
parentID, _ := f.dirCache.FindDir(ctx, dir, false)
payload := map[string]string{
"ac": "add_task_urls",
"app_ver": appVer,
"app_ver": f.appVer,
"uid": f.userID,
"wp_path_id": parentID,
}
Expand Down
28 changes: 19 additions & 9 deletions backend/115/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,21 +127,19 @@ func generateSignature(userID, fileID, target, userKey string) string {
return strings.ToUpper(hex.EncodeToString(sh1Sig[:]))
}

func generateToken(userID, fileID, fileSize, signKey, signVal, timeStamp string) string {
func generateToken(userID, fileID, fileSize, signKey, signVal, timeStamp, appVer string) string {
userIDMd5 := md5.Sum([]byte(userID))
tokenMd5 := md5.Sum([]byte(md5Salt + fileID + fileSize + signKey + signVal + userID + timeStamp + hex.EncodeToString(userIDMd5[:]) + appVer))
return hex.EncodeToString(tokenMd5[:])
}

func (f *Fs) initUpload(ctx context.Context, size int64, name, dirID, sha1sum, signKey, signVal string) (info *api.UploadInitInfo, err error) {
var (
userID = f.userID
userKey = f.userkey
filename = f.opt.Enc.FromStandardName(name)
filesize = strconv.FormatInt(size, 10)
fileID = strings.ToUpper(sha1sum)
target = "U_1_" + dirID // target id
ts = time.Now().Unix() // timestamp in int64
ts = time.Now().UnixMilli() // timestamp in int64
t = strconv.FormatInt(ts, 10) // timestamp in string
ecdhCipher *cipher.EcdhCipher
encodedToken string
Expand All @@ -161,15 +159,15 @@ func (f *Fs) initUpload(ctx context.Context, size int64, name, dirID, sha1sum, s
// form that will be encrypted
form := url.Values{}
form.Set("appid", "0")
form.Set("appversion", appVer) // const
form.Set("userid", userID)
form.Set("appversion", f.appVer)
form.Set("userid", f.userID)
form.Set("filename", filename)
form.Set("filesize", filesize)
form.Set("fileid", fileID)
form.Set("target", target)
form.Set("sig", generateSignature(userID, fileID, target, userKey))
form.Set("sig", generateSignature(f.userID, fileID, target, f.userkey))
form.Set("t", t)
form.Set("token", generateToken(userID, fileID, filesize, signKey, signVal, t))
form.Set("token", generateToken(f.userID, fileID, filesize, signKey, signVal, t, f.appVer))
if signKey != "" && signVal != "" {
form.Set("sign_key", signKey)
form.Set("sign_val", signVal)
Expand Down Expand Up @@ -279,6 +277,18 @@ func (f *Fs) newOSSBucket(ctx context.Context, ui *api.UploadInitInfo) (bucket *
return
}

// unWrapObjectInfo returns the underlying Object unwrapped as much as
// possible or nil.
func unWrapObjectInfo(oi fs.ObjectInfo) fs.Object {
if o, ok := oi.(fs.Object); ok {
return fs.UnWrapObject(o)
} else if do, ok := oi.(*fs.OverrideRemote); ok {
// Unwrap if it is an operations.OverrideRemote
return do.UnWrap()
}
return nil
}

func calcBlockSHA1(ctx context.Context, in io.Reader, src fs.ObjectInfo, rangeSpec string) (sha1sum string, err error) {
var start, end int64
if _, err = fmt.Sscanf(rangeSpec, "%d-%d", &start, &end); err != nil {
Expand All @@ -288,7 +298,7 @@ func calcBlockSHA1(ctx context.Context, in io.Reader, src fs.ObjectInfo, rangeSp
var reader io.Reader
if ra, ok := in.(io.ReaderAt); ok {
reader = io.NewSectionReader(ra, start, end-start+1)
} else if srcObj := fs.UnWrapObjectInfo(src); srcObj != nil {
} else if srcObj := unWrapObjectInfo(src); srcObj != nil {
rc, err := srcObj.Open(ctx, &fs.RangeOption{Start: start, End: end})
if err != nil {
return "", fmt.Errorf("failed to open source: %w", err)
Expand Down
66 changes: 66 additions & 0 deletions backend/pikpak/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,72 @@ type RequestDecompress struct {
DefaultParent bool `json:"default_parent,omitempty"`
}

// ------------------------------------------------------------ authorization

// CaptchaToken is a response to requestCaptchaToken api call
type CaptchaToken struct {
CaptchaToken string `json:"captcha_token"`
ExpiresIn int64 `json:"expires_in"` // currently 300s
// API doesn't provide Expiry field and thus it should be populated from ExpiresIn on retrieval
Expiry time.Time `json:"expiry,omitempty"`
URL string `json:"url,omitempty"` // a link for users to solve captcha
}

// expired reports whether the token is expired.
// t must be non-nil.
func (t *CaptchaToken) expired() bool {
if t.Expiry.IsZero() {
return false
}

expiryDelta := time.Duration(10) * time.Second // same as oauth2's defaultExpiryDelta
return t.Expiry.Round(0).Add(-expiryDelta).Before(time.Now())
}

// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
func (t *CaptchaToken) Valid() bool {
return t != nil && t.CaptchaToken != "" && !t.expired()
}

// CaptchaTokenRequest is to request for captcha token
type CaptchaTokenRequest struct {
Action string `json:"action,omitempty"`
CaptchaToken string `json:"captcha_token,omitempty"`
ClientID string `json:"client_id,omitempty"`
DeviceID string `json:"device_id,omitempty"`
Meta *CaptchaTokenMeta `json:"meta,omitempty"`
}

// CaptchaTokenMeta contains meta info for CaptchaTokenRequest
type CaptchaTokenMeta struct {
CaptchaSign string `json:"captcha_sign,omitempty"`
ClientVersion string `json:"client_version,omitempty"`
PackageName string `json:"package_name,omitempty"`
Timestamp string `json:"timestamp,omitempty"`
UserID string `json:"user_id,omitempty"` // webdrive uses this instead of UserName
UserName string `json:"username,omitempty"`
Email string `json:"email,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"`
}

// Token represents oauth2 token used for pikpak which needs to be converted to be compatible with oauth2.Token
type Token struct {
TokenType string `json:"token_type"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int `json:"expires_in"`
Sub string `json:"sub"`
}

// Expiry returns expiry from expires in, so it should be called on retrieval
// e must be non-nil.
func (e *Token) Expiry() (t time.Time) {
if v := e.ExpiresIn; v != 0 {
return time.Now().Add(time.Duration(v) * time.Second)
}
return
}

// ------------------------------------------------------------

// NOT implemented YET
Expand Down
Loading

0 comments on commit e9d24f7

Please sign in to comment.