@@ -10,12 +10,17 @@ import (
1010 "fmt"
1111 "io"
1212 "net/http"
13+ "net/http/cookiejar"
1314 "net/url"
15+ "strings"
16+
17+ "golang.org/x/net/publicsuffix"
1418)
1519
1620// APIer is used by the sub packages to allow mocking the http methods in tests.
1721// This also allows consuming packages to override methods.
1822type APIer interface {
23+ Login () error // Only needed for non-API paths, like backup downloads. Requires Username and Password being set.
1924 Get (path string , params url.Values ) (respBody []byte , err error )
2025 Post (path string , params url.Values , postBody []byte ) (respBody []byte , err error )
2126 Put (path string , params url.Values , putBody []byte ) (respBody []byte , err error )
@@ -63,6 +68,39 @@ func (c *Config) log(code int, data, body []byte, header http.Header, path, meth
6368 }
6469}
6570
71+ // Login POSTs to the login form in a Starr app and saves the authentication cookie for future use.
72+ func (c * Config ) Login () error {
73+ if c .Client .Jar == nil {
74+ jar , err := cookiejar .New (& cookiejar.Options {PublicSuffixList : publicsuffix .List })
75+ if err != nil {
76+ return fmt .Errorf ("cookiejar.New(publicsuffix): %w" , err )
77+ }
78+
79+ c .Client .Jar = jar
80+ }
81+
82+ post := []byte ("username=" + c .Username + "&password=" + c .Password )
83+
84+ code , resp , header , err := c .body (context .Background (), "/login" , http .MethodPost , nil , bytes .NewBuffer (post ))
85+ c .log (code , nil , post , header , c .URL + "/login" , http .MethodPost , err )
86+
87+ if err != nil {
88+ return fmt .Errorf ("authenticating as user '%s' failed: %w" , c .Username , err )
89+ }
90+ defer resp .Close ()
91+
92+ _ , _ = io .Copy (io .Discard , resp )
93+
94+ if u , _ := url .Parse (c .URL ); strings .Contains (header .Get ("location" ), "loginFailed" ) ||
95+ len (c .Client .Jar .Cookies (u )) == 0 {
96+ return fmt .Errorf ("%w: authenticating as user '%s' failed" , ErrRequestError , c .Username )
97+ }
98+
99+ c .cookie = true
100+
101+ return nil
102+ }
103+
66104// Get makes a GET http request and returns the body.
67105func (c * Config ) Get (path string , params url.Values ) ([]byte , error ) {
68106 code , data , header , err := c .req (path , http .MethodGet , params , nil )
@@ -74,7 +112,7 @@ func (c *Config) Get(path string, params url.Values) ([]byte, error) {
74112// Post makes a POST http request and returns the body.
75113func (c * Config ) Post (path string , params url.Values , postBody []byte ) ([]byte , error ) {
76114 code , data , header , err := c .req (path , http .MethodPost , params , bytes .NewBuffer (postBody ))
77- c .log (code , data , postBody , header , c .setPathParams (path , params ), http .MethodPut , err )
115+ c .log (code , data , postBody , header , c .setPathParams (path , params ), http .MethodPost , err )
78116
79117 return data , err
80118}
@@ -130,7 +168,7 @@ func (c *Config) DeleteInto(path string, params url.Values, v interface{}) error
130168// If it's not 200, it's possible the request had an error or was not authenticated.
131169func (c * Config ) GetBody (ctx context.Context , path string , params url.Values ) (io.ReadCloser , int , error ) {
132170 code , data , header , err := c .body (ctx , path , http .MethodGet , params , nil )
133- c .log (code , nil , nil , header , c .setPathParams ( path , params ) , http .MethodGet , err )
171+ c .log (code , nil , nil , header , c .URL + path , http .MethodGet , err )
134172
135173 return data , code , err
136174}
@@ -142,7 +180,7 @@ func (c *Config) GetBody(ctx context.Context, path string, params url.Values) (i
142180func (c * Config ) PostBody (ctx context.Context , path string , params url.Values ,
143181 postBody []byte ) (io.ReadCloser , int , error ) {
144182 code , data , header , err := c .body (ctx , path , http .MethodPost , params , bytes .NewBuffer (postBody ))
145- c .log (code , nil , postBody , header , c .setPathParams ( path , params ), http .MethodPut , err )
183+ c .log (code , nil , postBody , header , c .URL + path , http .MethodPost , err )
146184
147185 return data , code , err
148186}
@@ -153,7 +191,7 @@ func (c *Config) PostBody(ctx context.Context, path string, params url.Values,
153191func (c * Config ) PutBody (ctx context.Context , path string , params url.Values ,
154192 putBody []byte ) (io.ReadCloser , int , error ) {
155193 code , data , header , err := c .body (ctx , path , http .MethodPut , params , bytes .NewBuffer (putBody ))
156- c .log (code , nil , putBody , header , c .setPathParams ( path , params ) , http .MethodPut , err )
194+ c .log (code , nil , putBody , header , c .URL + path , http .MethodPut , err )
157195
158196 return data , code , err
159197}
@@ -164,7 +202,7 @@ func (c *Config) PutBody(ctx context.Context, path string, params url.Values,
164202// If it's not 200, it's possible the request had an error or was not authenticated.
165203func (c * Config ) DeleteBody (ctx context.Context , path string , params url.Values ) (io.ReadCloser , int , error ) {
166204 code , data , header , err := c .body (ctx , path , http .MethodDelete , params , nil )
167- c .log (code , nil , nil , header , c .setPathParams ( path , params ) , http .MethodDelete , err )
205+ c .log (code , nil , nil , header , c .URL + path , http .MethodDelete , err )
168206
169207 return data , code , err
170208}
0 commit comments