Add csrf token method, sets c.CsrfToken

Make posting handle multipart bodies, (set content type in api method)
Implement posting, multipart form with files, etc
master
Thomas Lynch 2 years ago
parent 1fc700836e
commit 8fb5f2e3a9
  1. 26
      app/auth.go
  2. 14
      app/jschan.go
  3. 79
      app/post.go
  4. 19
      example.go

@ -38,3 +38,29 @@ func (c *Client) Login(ctx context.Context, options *PostLoginOptions) error {
return nil return nil
} }
type GetCSRFTokenResponse struct {
Token string `json:"token"`
}
func (c *Client) GetCSRFToken(ctx context.Context) (*GetCSRFTokenResponse, error) {
url := fmt.Sprintf("%s/csrf.json", c.BaseURL)
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
res := &GetCSRFTokenResponse{}
if err := c.sendRequest(req, &res, nil); err != nil {
return nil, err
}
c.CsrfToken = res.Token
return res, nil
}

@ -49,10 +49,12 @@ func cookieHeader(rawCookies string) []*http.Cookie {
func (c *Client) sendRequest(req *http.Request, v interface{}, h *ReturnHeaders) error { func (c *Client) sendRequest(req *http.Request, v interface{}, h *ReturnHeaders) error {
if req.Method == http.MethodGet { if req.Header.Get("Content-Type") == "" {
req.Header.Set("Content-Type", "application/json; charset=utf-8") if req.Method == http.MethodGet {
} else { req.Header.Set("Content-Type", "application/json; charset=utf-8")
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") } else {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
} }
req.Header.Set("Accept", "application/json; charset=utf-8") req.Header.Set("Accept", "application/json; charset=utf-8")
req.Header.Set("X-Using-XHR", "true") req.Header.Set("X-Using-XHR", "true")
@ -61,7 +63,7 @@ func (c *Client) sendRequest(req *http.Request, v interface{}, h *ReturnHeaders)
req.Header.Set("Cookie", fmt.Sprintf("connect.sid=%s", c.SessionCookie)) req.Header.Set("Cookie", fmt.Sprintf("connect.sid=%s", c.SessionCookie))
} }
if c.CsrfToken != "" { if c.CsrfToken != "" {
// TODO req.Header.Set("Csrf-Token", c.CsrfToken)
} }
res, err := c.HTTPClient.Do(req) res, err := c.HTTPClient.Do(req)
@ -102,7 +104,7 @@ func (c *Client) sendRequest(req *http.Request, v interface{}, h *ReturnHeaders)
err3 := json.Unmarshal(body, &fullResponse) err3 := json.Unmarshal(body, &fullResponse)
if err3 != nil { if err3 != nil {
return err return err
} }
} }
} }

@ -1,44 +1,81 @@
package jschan package jschan
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"io"
"mime/multipart"
"net/http" "net/http"
"net/url" "net/textproto"
"strings" "os"
"path"
"strconv"
) )
type PostMakePostOptions struct { type MakePostOptions struct {
Board string Board string
Thread int Thread int
Name string Name string
Message string Message string
Subject string Subject string
Email string Email string
PostPassword string PostPassword string
//TODO: Files Files []string //Array of filenames
Spoiler []string Spoiler []string
SpoilerAll bool SpoilerAll bool
StripFilename []string StripFilename []string
CustomFlag string CustomFlag string
//Array for grid captcha, submitted as single param if len()==1 Captcha []string //Array for grid captcha, submitted as single param if len()==1
Captcha []string
} }
func (c *Client) MakePost(ctx context.Context, options *PostMakePostOptions) error { func (c *Client) MakePost(ctx context.Context, options *MakePostOptions) error {
formData := url.Values{} body := &bytes.Buffer{}
//TODO: post params writer := multipart.NewWriter(body)
if options.Files != nil && len(options.Files) > 0 {
endodedBody := strings.NewReader(formData.Encode()) for _, filepath := range options.Files {
dir, fileName := path.Split(filepath)
filePath := path.Join(dir, fileName)
file, _ := os.Open(filePath)
defer file.Close()
h := make(textproto.MIMEHeader)
h.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, fileName))
h.Set("Content-Type", "image/png")
part, _ := writer.CreatePart(h)
//part, _ := writer.CreateFormFile("file", h)
io.Copy(part, file)
}
}
_ = writer.WriteField("thread", strconv.Itoa(options.Thread))
_ = writer.WriteField("name", options.Name)
_ = writer.WriteField("message", options.Message)
_ = writer.WriteField("subject", options.Subject)
_ = writer.WriteField("email", options.Email)
_ = writer.WriteField("postpassword", options.PostPassword)
_ = writer.WriteField("customflag", options.CustomFlag)
if options.SpoilerAll == true {
_ = writer.WriteField("spoiler_all", "true")
}
for _, filename := range options.Spoiler {
_ = writer.WriteField("spoiler", filename)
}
for _, filename := range options.StripFilename {
_ = writer.WriteField("strip_filename", filename)
}
for _, answer := range options.Captcha {
_ = writer.WriteField("captcha", answer)
}
writer.Close()
url := fmt.Sprintf("%s/forms/%s/post", c.BaseURL, options.Board) url := fmt.Sprintf("%s/forms/board/%s/post", c.BaseURL, options.Board)
req, err := http.NewRequest(http.MethodPost, url, endodedBody) req, err := http.NewRequest(http.MethodPost, url, body)
if err != nil { if err != nil {
return err return err
} }
// req.Header.Set("content-type", "multipart/form-data")
req.Header.Add("Content-Type", writer.FormDataContentType())
req = req.WithContext(ctx) req = req.WithContext(ctx)
if err := c.sendRequest(req, nil, nil); err != nil { if err := c.sendRequest(req, nil, nil); err != nil {

@ -20,10 +20,12 @@ func main() {
err := jschanClient.Login(ctx, loginOptions) err := jschanClient.Login(ctx, loginOptions)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
//return
} }
if jschanClient.SessionCookie != "" { if jschanClient.SessionCookie != "" {
fmt.Printf("Logged in as user %s\n", loginOptions.Username) fmt.Printf("Logged in as user %s\n", loginOptions.Username)
if _, err := jschanClient.GetCSRFToken(ctx); err != nil {
fmt.Println(err)
}
} }
overboardOptions := &jschan.GetOverboardOptions{ overboardOptions := &jschan.GetOverboardOptions{
@ -82,5 +84,20 @@ func main() {
} }
fmt.Printf("Fetched /%s/ logs for date %s with %d entries\n", getLogsOptions.Board, getLogsOptions.Date.String(), len(res5)) fmt.Printf("Fetched /%s/ logs for date %s with %d entries\n", getLogsOptions.Board, getLogsOptions.Date.String(), len(res5))
// makePostOptions := &jschan.MakePostOptions{
// Board: "test",
// Thread: 277,
// Name: "Test",
// Message: ">test",
// Email: "sage",
// PostPassword: "123",
// Files: []string{"./image.png", "./image.png"},
// }
// err6 := jschanClient.MakePost(ctx, makePostOptions)
// if err6 != nil {
// fmt.Println(err)
// return
// }
return return
} }

Loading…
Cancel
Save