feat(lanzou): support login with account (close #4880 in #4885)

pull/4882/head
foxxorcat 2023-08-01 19:44:57 +08:00 committed by GitHub
parent 9f08353d31
commit c9ea9bce81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 33 deletions

View File

@ -2,9 +2,7 @@ package lanzou
import (
"context"
"fmt"
"net/http"
"regexp"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/driver"
@ -19,6 +17,8 @@ type LanZou struct {
model.Storage
uid string
vei string
flag int32
}
func (d *LanZou) Config() driver.Config {
@ -30,16 +30,18 @@ func (d *LanZou) GetAddition() driver.Additional {
}
func (d *LanZou) Init(ctx context.Context) (err error) {
if d.IsCookie() {
switch d.Type {
case "account":
_, err := d.Login()
if err != nil {
return err
}
fallthrough
case "cookie":
if d.RootFolderID == "" {
d.RootFolderID = "-1"
}
ylogin := regexp.MustCompile("ylogin=(.*?);").FindStringSubmatch(d.Cookie)
if len(ylogin) < 2 {
return fmt.Errorf("cookie does not contain ylogin")
}
d.uid = ylogin[1]
d.vei, err = d.getVei()
d.vei, d.uid, err = d.getVeiAndUid()
}
return
}
@ -51,7 +53,7 @@ func (d *LanZou) Drop(ctx context.Context) error {
// 获取的大小和时间不准确
func (d *LanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
if d.IsCookie() {
if d.IsCookie() || d.IsAccount() {
return d.GetAllFiles(dir.GetID())
} else {
return d.GetFileOrFolderByShareUrl(dir.GetID(), d.SharePassword)
@ -119,7 +121,7 @@ func (d *LanZou) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
}
func (d *LanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) {
if d.IsCookie() {
if d.IsCookie() || d.IsAccount() {
data, err := d.doupload(func(req *resty.Request) {
req.SetContext(ctx)
req.SetFormData(map[string]string{
@ -137,11 +139,11 @@ func (d *LanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName strin
FolID: utils.Json.Get(data, "text").ToString(),
}, nil
}
return nil, errs.NotImplement
return nil, errs.NotSupport
}
func (d *LanZou) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
if d.IsCookie() {
if d.IsCookie() || d.IsAccount() {
if !srcObj.IsDir() {
_, err := d.doupload(func(req *resty.Request) {
req.SetContext(ctx)
@ -157,11 +159,11 @@ func (d *LanZou) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj,
return srcObj, nil
}
}
return nil, errs.NotImplement
return nil, errs.NotSupport
}
func (d *LanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) {
if d.IsCookie() {
if d.IsCookie() || d.IsAccount() {
if !srcObj.IsDir() {
_, err := d.doupload(func(req *resty.Request) {
req.SetContext(ctx)
@ -179,11 +181,11 @@ func (d *LanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) (
return srcObj, nil
}
}
return nil, errs.NotImplement
return nil, errs.NotSupport
}
func (d *LanZou) Remove(ctx context.Context, obj model.Obj) error {
if d.IsCookie() {
if d.IsCookie() || d.IsAccount() {
_, err := d.doupload(func(req *resty.Request) {
req.SetContext(ctx)
if obj.IsDir() {
@ -200,13 +202,13 @@ func (d *LanZou) Remove(ctx context.Context, obj model.Obj) error {
}, nil)
return err
}
return errs.NotImplement
return errs.NotSupport
}
func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) {
if d.IsCookie() {
if d.IsCookie() || d.IsAccount() {
var resp RespText[[]FileOrFolder]
_, err := d._post(d.BaseUrl+"/fileup.php", func(req *resty.Request) {
_, err := d._post(d.BaseUrl+"/html5up.php", func(req *resty.Request) {
req.SetFormData(map[string]string{
"task": "1",
"vie": "2",
@ -221,5 +223,5 @@ func (d *LanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
}
return &resp.Text[0], nil
}
return nil, errs.NotImplement
return nil, errs.NotSupport
}

View File

@ -3,6 +3,7 @@ package lanzou
import (
"bytes"
"fmt"
"net/http"
"regexp"
"strconv"
"strings"
@ -190,3 +191,14 @@ func GetExpirationTime(url string) (etime time.Duration) {
etime = time.Duration(timestamp-time.Now().Unix()) * time.Second
return
}
func CookieToString(cookies []*http.Cookie) string {
if cookies == nil {
return ""
}
cookieStrings := make([]string, len(cookies))
for i, cookie := range cookies {
cookieStrings[i] = cookie.Name + "=" + cookie.Value
}
return strings.Join(cookieStrings, ";")
}

View File

@ -6,8 +6,13 @@ import (
)
type Addition struct {
Type string `json:"type" type:"select" options:"cookie,url" default:"cookie"`
Cookie string `json:"cookie" required:"true" help:"about 15 days valid, ignore if shareUrl is used"`
Type string `json:"type" type:"select" options:"account,cookie,url" default:"cookie"`
Account string `json:"account"`
Password string `json:"password"`
Cookie string `json:"cookie" help:"about 15 days valid, ignore if shareUrl is used"`
driver.RootID
SharePassword string `json:"share_password"`
BaseUrl string `json:"baseUrl" required:"true" default:"https://pc.woozooo.com" help:"basic URL for file operation"`
@ -19,6 +24,10 @@ func (a *Addition) IsCookie() bool {
return a.Type == "cookie"
}
func (a *Addition) IsAccount() bool {
return a.Type == "account"
}
var config = driver.Config{
Name: "Lanzou",
LocalSort: true,

View File

@ -8,6 +8,7 @@ import (
var ErrFileShareCancel = errors.New("file sharing cancellation")
var ErrFileNotExist = errors.New("file does not exist")
var ErrCookieExpiration = errors.New("cookie expiration")
type RespText[T any] struct {
Text T `json:"text"`

View File

@ -5,13 +5,16 @@ import (
"fmt"
"net/http"
"regexp"
"runtime"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
@ -37,7 +40,24 @@ func (d *LanZou) get(url string, callback base.ReqCallback) ([]byte, error) {
}
func (d *LanZou) post(url string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
return d._post(url, callback, resp, false)
data, err := d._post(url, callback, resp, false)
if err == ErrCookieExpiration && d.IsAccount() {
if atomic.CompareAndSwapInt32(&d.flag, 0, 1) {
_, err2 := d.Login()
atomic.SwapInt32(&d.flag, 0)
if err2 != nil {
err = errors.Join(err, err2)
d.Status = err.Error()
op.MustSaveDriverStorage(d)
return data, err
}
}
for atomic.LoadInt32(&d.flag) != 0 {
runtime.Gosched()
}
return d._post(url, callback, resp, false)
}
return data, err
}
func (d *LanZou) _post(url string, callback base.ReqCallback, resp interface{}, up bool) ([]byte, error) {
@ -49,10 +69,12 @@ func (d *LanZou) _post(url string, callback base.ReqCallback, resp interface{},
}
return false
})
callback(req)
if callback != nil {
callback(req)
}
}, up)
if err != nil {
return nil, err
return data, err
}
switch utils.Json.Get(data, "zt").ToInt() {
case 1, 2, 4:
@ -61,12 +83,14 @@ func (d *LanZou) _post(url string, callback base.ReqCallback, resp interface{},
utils.Json.Unmarshal(data, resp)
}
return data, nil
case 9: // 登录过期
return data, ErrCookieExpiration
default:
info := utils.Json.Get(data, "inf").ToString()
if info == "" {
info = utils.Json.Get(data, "info").ToString()
}
return nil, fmt.Errorf(info)
return data, fmt.Errorf(info)
}
}
@ -101,6 +125,28 @@ func (d *LanZou) request(url string, method string, callback base.ReqCallback, u
return res.Body(), err
}
func (d *LanZou) Login() ([]*http.Cookie, error) {
resp, err := base.NewRestyClient().SetRedirectPolicy(resty.NoRedirectPolicy()).
R().SetFormData(map[string]string{
"task": "3",
"uid": d.Account,
"pwd": d.Password,
"setSessionId": "",
"setSig": "",
"setScene": "",
"setTocen": "",
"formhash": "",
}).Post("https://up.woozooo.com/mlogin.php")
if err != nil {
return nil, err
}
if utils.Json.Get(resp.Body(), "zt").ToInt() != 1 {
return nil, fmt.Errorf("login err: %s", resp.Body())
}
d.Cookie = CookieToString(resp.Cookies())
return resp.Cookies(), nil
}
/*
cookie
*/
@ -451,21 +497,32 @@ func (d *LanZou) getFileRealInfo(downURL string) (*int64, *time.Time) {
return &size, &time
}
func (d *LanZou) getVei() (string, error) {
resp, err := d.get("https://pc.woozooo.com/mydisk.php", func(req *resty.Request) {
func (d *LanZou) getVeiAndUid() (vei string, uid string, err error) {
var resp []byte
resp, err = d.get("https://pc.woozooo.com/mydisk.php", func(req *resty.Request) {
req.SetQueryParams(map[string]string{
"item": "files",
"action": "index",
"u": d.uid,
})
})
if err != nil {
return "", err
return
}
// uid
uids := regexp.MustCompile(`uid=([^'"&;]+)`).FindStringSubmatch(string(resp))
if len(uids) < 2 {
err = fmt.Errorf("uid variable not find")
return
}
uid = uids[1]
// vei
html := RemoveNotes(string(resp))
data, err := htmlJsonToMap(html)
if err != nil {
return "", err
return
}
return data["vei"], nil
vei = data["vei"]
return
}