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 1 year 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
}
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 {
if req.Method == http.MethodGet {
req.Header.Set("Content-Type", "application/json; charset=utf-8")
} else {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if req.Header.Get("Content-Type") == "" {
if req.Method == http.MethodGet {
req.Header.Set("Content-Type", "application/json; charset=utf-8")
} else {
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
}
req.Header.Set("Accept", "application/json; charset=utf-8")
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))
}
if c.CsrfToken != "" {
// TODO
req.Header.Set("Csrf-Token", c.CsrfToken)
}
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)
if err3 != nil {
return err
}
}
}
}

@ -1,44 +1,81 @@
package jschan
import (
"bytes"
"context"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
"strings"
"net/textproto"
"os"
"path"
"strconv"
)
type PostMakePostOptions struct {
Board string
Thread int
Name string
Message string
Subject string
Email string
PostPassword string
//TODO: Files
type MakePostOptions struct {
Board string
Thread int
Name string
Message string
Subject string
Email string
PostPassword string
Files []string //Array of filenames
Spoiler []string
SpoilerAll bool
StripFilename []string
CustomFlag string
//Array for grid captcha, submitted as single param if len()==1
Captcha []string
Captcha []string //Array for grid captcha, submitted as single param if len()==1
}
func (c *Client) MakePost(ctx context.Context, options *PostMakePostOptions) error {
func (c *Client) MakePost(ctx context.Context, options *MakePostOptions) error {
formData := url.Values{}
//TODO: post params
endodedBody := strings.NewReader(formData.Encode())
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
if options.Files != nil && len(options.Files) > 0 {
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 {
return err
}
// req.Header.Set("content-type", "multipart/form-data")
req.Header.Add("Content-Type", writer.FormDataContentType())
req = req.WithContext(ctx)
if err := c.sendRequest(req, nil, nil); err != nil {

@ -20,10 +20,12 @@ func main() {
err := jschanClient.Login(ctx, loginOptions)
if err != nil {
fmt.Println(err)
//return
}
if jschanClient.SessionCookie != "" {
fmt.Printf("Logged in as user %s\n", loginOptions.Username)
if _, err := jschanClient.GetCSRFToken(ctx); err != nil {
fmt.Println(err)
}
}
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))
// 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
}

Loading…
Cancel
Save