alist/drivers/189pc/189.go

312 lines
8.1 KiB
Go

package _189
import (
"bytes"
"errors"
"fmt"
"net/http"
"net/http/cookiejar"
"net/url"
"regexp"
"sync"
"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"
"github.com/google/uuid"
log "github.com/sirupsen/logrus"
)
var userStateCache = struct {
sync.Mutex
States map[string]*State
}{States: make(map[string]*State)}
func GetState(account *model.Account) *State {
userStateCache.Lock()
defer userStateCache.Unlock()
if v, ok := userStateCache.States[account.Username]; ok && v != nil {
return v
}
state := &State{client: resty.New().
SetHeaders(map[string]string{
"Accept": "application/json;charset=UTF-8",
"User-Agent": base.UserAgent,
}).SetTimeout(base.DefaultTimeout),
}
userStateCache.States[account.Username] = state
return state
}
type State struct {
sync.Mutex
client *resty.Client
RsaPublicKey string
SessionKey string
SessionSecret string
FamilySessionKey string
FamilySessionSecret string
AccessToken string
//怎么刷新的???
RefreshToken string
}
func (s *State) login(account *model.Account) error {
// 清除cookie
jar, _ := cookiejar.New(nil)
s.client.SetCookieJar(jar)
var err error
var res *resty.Response
defer func() {
account.Status = "work"
if err != nil {
account.Status = err.Error()
}
model.SaveAccount(account)
if res != nil {
log.Debug(res.String())
}
}()
var param *LoginParam
param, err = s.getLoginParam()
if err != nil {
return err
}
// 提交登录
s.RsaPublicKey = fmt.Sprintf("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----", param.jRsaKey)
res, err = s.client.R().
SetHeaders(map[string]string{
"Referer": AUTH_URL,
"REQID": param.ReqId,
"lt": param.Lt,
}).
SetFormData(map[string]string{
"appKey": APP_ID,
"accountType": "02",
"userName": "{RSA}" + rsaEncrypt(s.RsaPublicKey, account.Username),
"password": "{RSA}" + rsaEncrypt(s.RsaPublicKey, account.Password),
"validateCode": param.vCodeRS,
"captchaToken": param.CaptchaToken,
"returnUrl": RETURN_URL,
"mailSuffix": "@189.cn",
"dynamicCheck": "FALSE",
"clientType": CLIENT_TYPE,
"cb_SaveName": "1",
"isOauth2": "false",
"state": "",
"paramId": param.ParamId,
}).
Post(AUTH_URL + "/api/logbox/oauth2/loginSubmit.do")
if err != nil {
return err
}
toUrl := utils.Json.Get(res.Body(), "toUrl").ToString()
if toUrl == "" {
log.Error(res.String())
return fmt.Errorf(res.String())
}
// 获取Session
var erron Erron
var sessionResp appSessionResp
res, err = s.client.R().
SetResult(&sessionResp).SetError(&erron).
SetQueryParams(clientSuffix()).
SetQueryParam("redirectURL", url.QueryEscape(toUrl)).
Post(API_URL + "/getSessionForPC.action")
if err != nil {
return err
}
if erron.ResCode != "" {
err = fmt.Errorf(erron.ResMessage)
return err
}
if sessionResp.ResCode != 0 {
err = fmt.Errorf(sessionResp.ResMessage)
return err
}
s.SessionKey = sessionResp.SessionKey
s.SessionSecret = sessionResp.SessionSecret
s.FamilySessionKey = sessionResp.FamilySessionKey
s.FamilySessionSecret = sessionResp.FamilySessionSecret
s.AccessToken = sessionResp.AccessToken
s.RefreshToken = sessionResp.RefreshToken
return err
}
func (s *State) getLoginParam() (*LoginParam, error) {
res, err := s.client.R().
SetQueryParams(map[string]string{
"appId": APP_ID,
"clientType": CLIENT_TYPE,
"returnURL": RETURN_URL,
"timeStamp": fmt.Sprint(timestamp()),
}).
Get(WEB_URL + "/api/portal/unifyLoginForPC.action")
if err != nil {
return nil, err
}
log.Debug(res.String())
param := &LoginParam{
CaptchaToken: regexp.MustCompile(`'captchaToken' value='(.+?)'`).FindStringSubmatch(res.String())[1],
Lt: regexp.MustCompile(`lt = "(.+?)"`).FindStringSubmatch(res.String())[1],
ParamId: regexp.MustCompile(`paramId = "(.+?)"`).FindStringSubmatch(res.String())[1],
ReqId: regexp.MustCompile(`reqId = "(.+?)"`).FindStringSubmatch(res.String())[1],
jRsaKey: regexp.MustCompile(`"j_rsaKey" value="(.+?)"`).FindStringSubmatch(res.String())[1],
vCodeID: regexp.MustCompile(`token=([A-Za-z0-9&=]+)`).FindStringSubmatch(res.String())[1],
}
imgRes, err := s.client.R().Get(fmt.Sprint(AUTH_URL, "/api/logbox/oauth2/picCaptcha.do?token=", param.vCodeID, timestamp()))
if err != nil {
return nil, err
}
if len(imgRes.Body()) > 0 {
vRes, err := resty.New().R().
SetMultipartField("image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())).
Post(conf.GetStr("ocr api"))
if err != nil {
return nil, err
}
if utils.Json.Get(vRes.Body(), "status").ToInt() != 200 {
return nil, errors.New("ocr error:" + utils.Json.Get(vRes.Body(), "msg").ToString())
}
param.vCodeRS = utils.Json.Get(vRes.Body(), "result").ToString()
log.Debugln("code: ", param.vCodeRS)
}
return param, nil
}
func (s *State) refreshSession(account *model.Account) error {
var erron Erron
var userSessionResp UserSessionResp
res, err := s.client.R().
SetResult(&userSessionResp).SetError(&erron).
SetQueryParams(clientSuffix()).
SetQueryParams(map[string]string{
"appId": APP_ID,
"accessToken": s.AccessToken,
}).
SetHeader("X-Request-ID", uuid.NewString()).
Get(API_URL + "/getSessionForPC.action")
if err != nil {
return err
}
log.Debug(res.String())
if erron.ResCode != "" {
return fmt.Errorf(erron.ResMessage)
}
switch userSessionResp.ResCode {
case 0:
s.SessionKey = userSessionResp.SessionKey
s.SessionSecret = userSessionResp.SessionSecret
s.FamilySessionKey = userSessionResp.FamilySessionKey
s.FamilySessionSecret = userSessionResp.FamilySessionSecret
case 11, 18:
return s.login(account)
default:
account.Status = userSessionResp.ResMessage
_ = model.SaveAccount(account)
return fmt.Errorf(userSessionResp.ResMessage)
}
return nil
}
func (s *State) IsLogin(account *model.Account) bool {
_, err := s.Request(http.MethodGet, API_URL+"/getUserInfo.action", nil, func(r *resty.Request) { r.SetQueryParams(clientSuffix()) }, account)
return err == nil
}
func (s *State) Login(account *model.Account) error {
s.Lock()
defer s.Unlock()
return s.login(account)
}
func (s *State) RefreshSession(account *model.Account) error {
s.Lock()
defer s.Unlock()
return s.refreshSession(account)
}
func (s *State) Request(method string, fullUrl string, params Params, callback func(*resty.Request), account *model.Account) (*resty.Response, error) {
s.Lock()
dateOfGmt := getHttpDateStr()
sessionKey := s.SessionKey
sessionSecret := s.SessionSecret
if isFamily(account) {
sessionKey = s.FamilySessionKey
sessionSecret = s.FamilySessionSecret
}
req := s.client.R()
req.SetHeaders(map[string]string{
"Date": dateOfGmt,
"SessionKey": sessionKey,
"X-Request-ID": uuid.NewString(),
})
// 设置params
var paramsData string
if params != nil {
paramsData = AesECBEncrypt(params.Encode(), s.SessionSecret[:16])
req.SetQueryParam("params", paramsData)
}
req.SetHeader("Signature", signatureOfHmac(sessionSecret, sessionKey, method, fullUrl, dateOfGmt, paramsData))
if callback != nil {
callback(req)
}
s.Unlock()
res, err := req.Execute(method, fullUrl)
if err != nil {
return nil, err
}
log.Debug(res.String())
var erron Erron
utils.Json.Unmarshal(res.Body(), &erron)
if erron.ResCode != "" {
return nil, fmt.Errorf(erron.ResMessage)
}
if erron.Code != "" && erron.Code != "SUCCESS" {
if erron.Msg == "" {
if erron.Message == "" {
return nil, fmt.Errorf(res.String())
}
return nil, fmt.Errorf(erron.Message)
}
return nil, fmt.Errorf(erron.Msg)
}
if erron.ErrorCode != "" {
return nil, fmt.Errorf(erron.ErrorMsg)
}
switch utils.Json.Get(res.Body(), "res_code").ToInt64() {
case 11, 18:
if err := s.RefreshSession(account); err != nil {
return nil, err
}
return s.Request(method, fullUrl, params, callback, account)
case 0:
if res.StatusCode() == http.StatusOK {
return res, nil
}
return nil, fmt.Errorf(res.String())
default:
return nil, fmt.Errorf(utils.Json.Get(res.Body(), "res_message").ToString())
}
}