From 74099b7ac217620439c129eaa0cc072ec9eaba0c Mon Sep 17 00:00:00 2001 From: webb Date: Sun, 19 Jul 2020 07:36:27 -0400 Subject: [PATCH] First commit --- account.go | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++ client.go | 30 ++++++++++++++ http.go | 40 +++++++++++++++++++ upload.go | 43 ++++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 account.go create mode 100644 client.go create mode 100644 http.go create mode 100644 upload.go diff --git a/account.go b/account.go new file mode 100644 index 0000000..1d8f3d8 --- /dev/null +++ b/account.go @@ -0,0 +1,113 @@ +package teal + +import ( + "bytes" + "encoding/json" + "errors" + "io/ioutil" + "log" + "net/http" +) + +// User holds all of the information about a user that is accessible from the profile page +type User struct { + ID int `json:"id"` + Username string `json:"username"` + Email string `json:"Email"` + RegistrationDate string `json:"registrationDate"` + LastLogin string `json:"lastLogin"` + UploadKey string `json:"uploadKey"` + RegistrationIP string `json:"registrationIp"` + Moderator bool `json:"moderator"` + Admin bool `json:"admin"` + Banned bool `json:"banned"` + BanReason string `json:"banReason"` + ImageCount int `json:"imageCount"` +} + +// UpdateAccountInfo is a wrapper for GetAccountInfo() that replaces user info automatically +func (c Client) UpdateAccountInfo() error { + u, err := GetAccountInfo(c.Session) + if err != nil { + return err + } + c.User = u + return nil +} + +// GetAccountInfo gets info of a user of a given authenticated session +func GetAccountInfo(session string) (User, error) { + httpClient := &http.Client{} + req, err := http.NewRequest("GET", "https://api.pxl.blue/users/@me", nil) + req.Header.Set("Authorization", session) + + if err != nil { + log.Println(err) + return User{}, err + } + + resp, err := httpClient.Do(req) + if err != nil { + log.Println(err) + return User{}, err + } + if resp != nil { + defer resp.Body.Close() + } + + bodyReader, err := ioutil.ReadAll(resp.Body) + if err != nil { + return User{}, err + } + + var uR userResponse + + err = json.Unmarshal(bodyReader, &uR) + if err != nil { + { + return User{}, err + } + } + return uR.User, nil + +} + +// Gets session from given username and password +func getSession(u string, p string) (string, error) { + rb, err := json.Marshal(map[string]string{ + "username": u, + "password": p, + }) + if err != nil { + return "", err + } + rs, err := http.Post("https://api.pxl.blue/auth/login", "application/json", bytes.NewBuffer(rb)) + if err != nil { + return "", err + } + + body, err := ioutil.ReadAll(rs.Body) + if err != nil { + return "", err + } + defer rs.Body.Close() + var lr loginResponse + err = json.Unmarshal(body, &lr) + if err != nil { + log.Println(err) + return "", err + } + + if !lr.Success { + log.Println(err) + return "", errors.New(lr.Message) + } + + if err != nil { + log.Println(err) + return "", err + } + + defer rs.Body.Close() + return lr.Session, nil +} diff --git a/client.go b/client.go new file mode 100644 index 0000000..3ff3984 --- /dev/null +++ b/client.go @@ -0,0 +1,30 @@ +package teal + +// Client represents the client used for making http requests, including the session if used. +type Client struct { + UploadKey string + Session string + UserAgent string + User User +} + +// Login will login to an account with the provided credentials and give you a Client with user info back +func Login(username string, password string) (Client, error) { + s, err := getSession(username, password) + if err != nil { + return Client{}, err + } + + u, err := GetAccountInfo(s) + if err != nil { + return Client{}, err + } + var client Client + + client.User = u + client.Session = s + client.UploadKey = u.UploadKey + client.UserAgent = "teal" + + return client, nil +} diff --git a/http.go b/http.go new file mode 100644 index 0000000..56d304b --- /dev/null +++ b/http.go @@ -0,0 +1,40 @@ +/* HTTP related structs and helpers */ + +package teal + +import ( + "fmt" + "io" + "mime/multipart" + "net/textproto" + "strings" +) + +var quoteEscaper = strings.NewReplacer("\\", "\\\\", `"`, "\\\"") + +type loginResponse struct { + Success bool `json:"success"` + Message string `json:"message"` + Session string `json:"session"` +} + +type userResponse struct { + Success bool `json:"success"` + Message string `json:"message"` + User User `json:"user"` +} + +/* createFormFileWithMIME is a modification of multipart.CreateFormFile() that + allows arbitrary mime-types. */ +func createFormFileWithMIME(w *multipart.Writer, fieldname, filename string, mimeType string) (io.Writer, error) { + h := make(textproto.MIMEHeader) + h.Set("Content-Disposition", + fmt.Sprintf(`form-data; name="%s"; filename="%s"`, + escapeQuotes(fieldname), escapeQuotes(filename))) + h.Set("Content-Type", mimeType) + return w.CreatePart(h) +} + +func escapeQuotes(s string) string { + return quoteEscaper.Replace(s) +} diff --git a/upload.go b/upload.go new file mode 100644 index 0000000..e3a82b3 --- /dev/null +++ b/upload.go @@ -0,0 +1,43 @@ +package teal + +import ( + "io" + "io/ioutil" + "mime" + "mime/multipart" + "net/http" + "path/filepath" +) + +// UploadFile attempts to upload file from io.Readerm and returns the response +func (c Client) UploadFile(rd io.Reader, n string) (response string, error error) { + r, w := io.Pipe() + m := multipart.NewWriter(w) + go func() { + defer w.Close() + defer m.Close() + part, err := createFormFileWithMIME(m, "file", n, mime.TypeByExtension(filepath.Ext(n))) + if err != nil { + return + } + if err != nil { + return + } + if _, err = io.Copy(part, rd); err != nil { + return + } + m.WriteField("key", c.UploadKey) + }() + + rs, err := http.Post("https://api.pxl.blue/upload/sharex", m.FormDataContentType(), r) + if err != nil { + return "", err + } + rb, err := ioutil.ReadAll(rs.Body) + if err != nil { + return "", err + } + + return string(rb), err + +}