refactor: init v3

This commit is contained in:
Noah Hsu
2022-06-06 16:28:37 +08:00
parent eb15bce24b
commit b76060570e
185 changed files with 14 additions and 30438 deletions

View File

@ -1,224 +0,0 @@
package webdav
import (
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/drivers/webdav/odrvcookie"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"net/http"
"path/filepath"
)
type WebDav struct{}
func (driver WebDav) Config() base.DriverConfig {
return base.DriverConfig{
Name: "WebDav",
OnlyProxy: true,
OnlyLocal: true,
NoNeedSetLink: true,
LocalSort: true,
}
}
func (driver WebDav) Items() []base.Item {
return []base.Item{
{
Name: "site_url",
Label: "webdav root url",
Type: base.TypeString,
Required: true,
},
{
Name: "username",
Label: "username",
Type: base.TypeString,
Required: true,
},
{
Name: "password",
Label: "password",
Type: base.TypeString,
Required: true,
},
{
Name: "internal_type",
Label: "vendor",
Type: base.TypeSelect,
Required: true,
Default: "other",
Values: "sharepoint,other",
Description: "webdav vendor",
},
}
}
func (driver WebDav) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
var err error
if isSharePoint(account) {
_, err = odrvcookie.GetCookie(account.Username, account.Password, account.SiteUrl)
}
if err != nil {
account.Status = err.Error()
} else {
account.Status = "work"
}
_ = model.SaveAccount(account)
return err
}
func (driver WebDav) File(path string, account *model.Account) (*model.File, error) {
if path == "/" {
return &model.File{
Id: "/",
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver WebDav) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
cache, err := base.GetCache(path, account)
if err == nil {
files, _ := cache.([]model.File)
return files, nil
}
c := driver.NewClient(account)
rawFiles, err := c.ReadDir(driver.WebDavPath(path))
if err != nil {
return nil, err
}
files := make([]model.File, 0)
if len(rawFiles) == 0 {
return files, nil
}
for _, f := range rawFiles {
t := f.ModTime()
file := model.File{
Name: f.Name(),
Size: f.Size(),
Driver: driver.Config().Name,
UpdatedAt: &t,
}
if f.IsDir() {
file.Type = conf.FOLDER
} else {
file.Type = utils.GetFileType(filepath.Ext(f.Name()))
}
files = append(files, file)
}
_ = base.SetCache(path, files, account)
return files, nil
}
func (driver WebDav) Link(args base.Args, account *model.Account) (*base.Link, error) {
path := args.Path
c := driver.NewClient(account)
callback := func(r *http.Request) {
if args.Header.Get("Range") != "" {
r.Header.Set("Range", args.Header.Get("Range"))
}
if args.Header.Get("If-Range") != "" {
r.Header.Set("If-Range", args.Header.Get("If-Range"))
}
}
reader, header, err := c.ReadStream(driver.WebDavPath(path), callback)
if err != nil {
return nil, err
}
link := &base.Link{Data: reader}
if header.Get("Content-Range") != "" {
link.Status = 206
link.Header = http.Header{
"Content-Range": header.Values("Content-Range"),
"Content-Length": header.Values("Content-Length"),
}
}
return link, nil
}
func (driver WebDav) Path(path string, account *model.Account) (*model.File, []model.File, error) {
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
//func (driver WebDav) Proxy(r *http.Request, account *model.Account) {
//
//}
func (driver WebDav) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver WebDav) MakeDir(path string, account *model.Account) error {
c := driver.NewClient(account)
err := c.MkdirAll(driver.WebDavPath(path), 0644)
return err
}
func (driver WebDav) Move(src string, dst string, account *model.Account) error {
c := driver.NewClient(account)
err := c.Rename(driver.WebDavPath(src), driver.WebDavPath(dst), true)
return err
}
func (driver WebDav) Rename(src string, dst string, account *model.Account) error {
return driver.Move(src, dst, account)
}
func (driver WebDav) Copy(src string, dst string, account *model.Account) error {
c := driver.NewClient(account)
err := c.Copy(driver.WebDavPath(src), driver.WebDavPath(dst), true)
return err
}
func (driver WebDav) Delete(path string, account *model.Account) error {
c := driver.NewClient(account)
err := c.RemoveAll(driver.WebDavPath(path))
return err
}
func (driver WebDav) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
c := driver.NewClient(account)
path := utils.Join(file.ParentPath, file.Name)
callback := func(r *http.Request) {
r.Header.Set("Content-Type", file.GetMIMEType())
r.ContentLength = int64(file.GetSize())
}
err := c.WriteStream(driver.WebDavPath(path), file, 0644, callback)
return err
}
var _ base.Driver = (*WebDav)(nil)

View File

@ -1,47 +0,0 @@
package odrvcookie
import (
"github.com/Xhofe/alist/utils/cookie"
log "github.com/sirupsen/logrus"
"net/http"
"sync"
"time"
)
type SpCookie struct {
Cookie string
expire time.Time
}
func (sp SpCookie) IsExpire() bool {
return time.Now().After(sp.expire)
}
var cookiesMap = struct {
sync.Mutex
m map[string]*SpCookie
}{m: make(map[string]*SpCookie)}
func GetCookie(username, password, siteUrl string) (string, error) {
cookiesMap.Lock()
defer cookiesMap.Unlock()
spCookie, ok := cookiesMap.m[username]
if ok {
if !spCookie.IsExpire() {
log.Debugln("sp use old cookie.")
return spCookie.Cookie, nil
}
}
log.Debugln("fetch new cookie")
ca := New(username, password, siteUrl)
tokenConf, err := ca.Cookies()
if err != nil {
return "", err
}
spCookie = &SpCookie{
Cookie: cookie.ToString([]*http.Cookie{&tokenConf.RtFa, &tokenConf.FedAuth}),
expire: time.Now().Add(time.Hour * 12),
}
cookiesMap.m[username] = spCookie
return spCookie.Cookie, nil
}

View File

@ -1,206 +0,0 @@
// Package odrvcookie can fetch authentication cookies for a sharepoint webdav endpoint
package odrvcookie
import (
"bytes"
"encoding/xml"
"fmt"
"html/template"
"net/http"
"net/http/cookiejar"
"net/url"
"strings"
"time"
"golang.org/x/net/publicsuffix"
)
// CookieAuth hold the authentication information
// These are username and password as well as the authentication endpoint
type CookieAuth struct {
user string
pass string
endpoint string
}
// CookieResponse contains the requested cookies
type CookieResponse struct {
RtFa http.Cookie
FedAuth http.Cookie
}
// SuccessResponse hold a response from the sharepoint webdav
type SuccessResponse struct {
XMLName xml.Name `xml:"Envelope"`
Succ SuccessResponseBody `xml:"Body"`
}
// SuccessResponseBody is the body of a success response, it holds the token
type SuccessResponseBody struct {
XMLName xml.Name
Type string `xml:"RequestSecurityTokenResponse>TokenType"`
Created time.Time `xml:"RequestSecurityTokenResponse>Lifetime>Created"`
Expires time.Time `xml:"RequestSecurityTokenResponse>Lifetime>Expires"`
Token string `xml:"RequestSecurityTokenResponse>RequestedSecurityToken>BinarySecurityToken"`
}
// reqString is a template that gets populated with the user data in order to retrieve a "BinarySecurityToken"
const reqString = `<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">{{ .LoginUrl }}</a:To>
<o:Security s:mustUnderstand="1"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:UsernameToken>
<o:Username>{{ .Username }}</o:Username>
<o:Password>{{ .Password }}</o:Password>
</o:UsernameToken>
</o:Security>
</s:Header>
<s:Body>
<t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<a:EndpointReference>
<a:Address>{{ .Address }}</a:Address>
</a:EndpointReference>
</wsp:AppliesTo>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>`
// New creates a new CookieAuth struct
func New(pUser, pPass, pEndpoint string) CookieAuth {
retStruct := CookieAuth{
user: pUser,
pass: pPass,
endpoint: pEndpoint,
}
return retStruct
}
// Cookies creates a CookieResponse. It fetches the auth token and then
// retrieves the Cookies
func (ca *CookieAuth) Cookies() (CookieResponse, error) {
spToken, err := ca.getSPToken()
if err != nil {
return CookieResponse{}, err
}
return ca.getSPCookie(spToken)
}
func (ca *CookieAuth) getSPCookie(conf *SuccessResponse) (CookieResponse, error) {
spRoot, err := url.Parse(ca.endpoint)
if err != nil {
return CookieResponse{}, err
}
u, err := url.Parse("https://" + spRoot.Host + "/_forms/default.aspx?wa=wsignin1.0")
if err != nil {
return CookieResponse{}, err
}
// To authenticate with davfs or anything else we need two cookies (rtFa and FedAuth)
// In order to get them we use the token we got earlier and a cookieJar
jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
if err != nil {
return CookieResponse{}, err
}
client := &http.Client{
Jar: jar,
}
// Send the previously aquired Token as a Post parameter
if _, err = client.Post(u.String(), "text/xml", strings.NewReader(conf.Succ.Token)); err != nil {
return CookieResponse{}, err
}
cookieResponse := CookieResponse{}
for _, cookie := range jar.Cookies(u) {
if (cookie.Name == "rtFa") || (cookie.Name == "FedAuth") {
switch cookie.Name {
case "rtFa":
cookieResponse.RtFa = *cookie
case "FedAuth":
cookieResponse.FedAuth = *cookie
}
}
}
return cookieResponse, err
}
var loginUrlsMap = map[string]string{
"com": "https://login.microsoftonline.com",
"cn": "https://login.chinacloudapi.cn",
"us": "https://login.microsoftonline.us",
"de": "https://login.microsoftonline.de",
}
func getLoginUrl(endpoint string) (string, error) {
spRoot, err := url.Parse(endpoint)
if err != nil {
return "", err
}
domains := strings.Split(spRoot.Host, ".")
tld := domains[len(domains)-1]
loginUrl, ok := loginUrlsMap[tld]
if !ok {
return "", fmt.Errorf("tld %s is not supported", tld)
}
return loginUrl + "/extSTS.srf", nil
}
func (ca *CookieAuth) getSPToken() (*SuccessResponse, error) {
loginUrl, err := getLoginUrl(ca.endpoint)
if err != nil {
return nil, err
}
reqData := map[string]string{
"Username": ca.user,
"Password": ca.pass,
"Address": ca.endpoint,
"LoginUrl": loginUrl,
}
t := template.Must(template.New("authXML").Parse(reqString))
buf := &bytes.Buffer{}
if err := t.Execute(buf, reqData); err != nil {
return nil, err
}
// Execute the first request which gives us an auth token for the sharepoint service
// With this token we can authenticate on the login page and save the returned cookies
req, err := http.NewRequest("POST", loginUrl, buf)
if err != nil {
return nil, err
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBuf := bytes.Buffer{}
respBuf.ReadFrom(resp.Body)
s := respBuf.Bytes()
var conf SuccessResponse
err = xml.Unmarshal(s, &conf)
if err != nil {
return nil, err
}
return &conf, err
}

View File

@ -1,7 +0,0 @@
package webdav
import "github.com/Xhofe/alist/model"
func isSharePoint(account *model.Account) bool {
return account.InternalType == "sharepoint"
}

View File

@ -1,35 +0,0 @@
package webdav
import (
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/drivers/webdav/odrvcookie"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/pkg/gowebdav"
"github.com/Xhofe/alist/utils"
"net/http"
"strings"
)
func (driver WebDav) NewClient(account *model.Account) *gowebdav.Client {
c := gowebdav.NewClient(account.SiteUrl, account.Username, account.Password)
if isSharePoint(account) {
cookie, err := odrvcookie.GetCookie(account.Username, account.Password, account.SiteUrl)
if err == nil {
c.SetInterceptor(func(method string, rq *http.Request) {
rq.Header.Del("Authorization")
rq.Header.Set("Cookie", cookie)
})
}
}
return c
}
func (driver WebDav) WebDavPath(path string) string {
path = utils.ParsePath(path)
path = strings.TrimPrefix(path, "/")
return path
}
func init() {
base.RegisterDriver(&WebDav{})
}