diff --git a/drivers/189/189.go b/drivers/189/189.go index 79d0bbc2..8d8fb2e4 100644 --- a/drivers/189/189.go +++ b/drivers/189/189.go @@ -132,7 +132,7 @@ func (driver Cloud189) Login(account *model.Account) error { } } if lt == "" { - return fmt.Errorf("get empty login page") + return errors.New(b) } captchaToken := regexp.MustCompile(`captchaToken' value='(.+?)'`).FindStringSubmatch(b)[1] returnUrl := regexp.MustCompile(`returnUrl = '(.+?)'`).FindStringSubmatch(b)[1] diff --git a/drivers/webdav/driver.go b/drivers/webdav/driver.go index ee11c1ef..705d3450 100644 --- a/drivers/webdav/driver.go +++ b/drivers/webdav/driver.go @@ -40,6 +40,15 @@ func (driver WebDav) Items() []base.Item { Type: base.TypeString, Required: true, }, + { + Name: "internal_type", + Label: "vendor", + Type: base.TypeSelect, + Required: true, + Default: "other", + Values: "sharepoint,other", + Description: "sharepoint temporarily unavailable", + }, } } diff --git a/drivers/webdav/odrvcookie/fetch.go b/drivers/webdav/odrvcookie/fetch.go new file mode 100644 index 00000000..20275e52 --- /dev/null +++ b/drivers/webdav/odrvcookie/fetch.go @@ -0,0 +1,176 @@ +// Package odrvcookie can fetch authentication cookies for a sharepoint webdav endpoint +package odrvcookie + +import ( + "bytes" + "encoding/xml" + "html/template" + "log" + "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 = ` + +http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue + +http://www.w3.org/2005/08/addressing/anonymous + +https://login.microsoftonline.com/extSTS.srf + + + {{ .Username }} + {{ .Password }} + + + + + + + + {{ .Address }} + + +http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey +http://schemas.xmlsoap.org/ws/2005/02/trust/Issue +urn:oasis:names:tc:SAML:1.0:assertion + + +` + +// 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) { + return ca.getSPCookie(ca.getSPToken()) +} + +func (ca *CookieAuth) getSPCookie(conf *SuccessResponse) (CookieResponse, error) { + spRoot, err := url.Parse(ca.endpoint) + if err != nil { + panic(err) + } + + u, err := url.Parse("https://" + spRoot.Host + "/_forms/default.aspx?wa=wsignin1.0") + if err != nil { + log.Fatal(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 { + log.Fatal(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 { + log.Fatal(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 +} + +func (ca *CookieAuth) getSPToken() *SuccessResponse { + reqData := map[string]interface{}{ + "Username": ca.user, + "Password": ca.pass, + "Address": ca.endpoint, + } + + t := template.Must(template.New("authXML").Parse(reqString)) + + buf := &bytes.Buffer{} + if err := t.Execute(buf, reqData); err != nil { + panic(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", "https://login.microsoftonline.com/extSTS.srf", buf) + if err != nil { + panic(err) + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err.Error()) + } + 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 { + panic(err) + } + + return &conf +} diff --git a/drivers/webdav/webdav.go b/drivers/webdav/webdav.go index 0af95c8b..9edc2e75 100644 --- a/drivers/webdav/webdav.go +++ b/drivers/webdav/webdav.go @@ -2,14 +2,36 @@ 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/utils" + "github.com/Xhofe/alist/utils/cookie" + log "github.com/sirupsen/logrus" "github.com/studio-b12/gowebdav" + "net/http" "strings" ) func (driver WebDav) NewClient(account *model.Account) *gowebdav.Client { - return gowebdav.NewClient(account.SiteUrl, account.Username, account.Password) + c := gowebdav.NewClient(account.SiteUrl, account.Username, account.Password) + if account.InternalType == "sharepoint" { + + ca := odrvcookie.New(account.Username, account.Password, account.SiteUrl) + tokenConf, err := ca.Cookies() + log.Debugln(err, tokenConf) + if err != nil { + c.SetInterceptor(func(method string, rq *http.Request) { + rq.Header.Del("Authorization") + //rq.AddCookie(&tokenConf.FedAuth) + //rq.AddCookie(&tokenConf.RtFa) + rq.Header.Set("Cookie", cookie.ToString([]*http.Cookie{&tokenConf.RtFa, &tokenConf.FedAuth})) + if method == "PROPFIND" { + rq.Header.Set("Depth", "0") + } + }) + } + } + return c } func (driver WebDav) WebDavPath(path string) string {