mirror of
https://gitgud.io/yats/libkiwi.git
synced 2026-06-19 17:45:25 -04:00
Refactoring n shit
This commit is contained in:
@@ -2,129 +2,52 @@ package libkiwi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
"golang.org/x/net/html/atom"
|
||||
)
|
||||
|
||||
type LoginResp struct {
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
Redirect *url.URL `json:"redirect"`
|
||||
Visitor struct {
|
||||
ConversationsUnread uint32 `json:"conversations_unread"`
|
||||
AlertsUnviewed uint32 `json:"alerts_unviewed"`
|
||||
TotalUnread uint32 `json:"total_unread"`
|
||||
} `json:"visitor"`
|
||||
type Credentials struct {
|
||||
Username string
|
||||
Password string
|
||||
Remember bool
|
||||
}
|
||||
|
||||
func (lr *LoginResp) UnmarshalJSON(b []byte) error {
|
||||
var jsonMap map[string]any
|
||||
err := json.Unmarshal(b, &jsonMap)
|
||||
if err != nil {
|
||||
return err
|
||||
func loginForm(xfToken string, creds Credentials) url.Values {
|
||||
rem := []byte{'0'}
|
||||
if creds.Remember {
|
||||
rem[0] = '1'
|
||||
}
|
||||
|
||||
for k, v := range jsonMap {
|
||||
switch k {
|
||||
case "status":
|
||||
lr.Status = v.(string)
|
||||
case "message":
|
||||
lr.Message = v.(string)
|
||||
case "redirect":
|
||||
u, err := url.Parse(v.(string))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lr.Redirect = u
|
||||
case "visitor":
|
||||
for k, v := range v.(map[string]any) {
|
||||
switch k {
|
||||
case "conversations_unread":
|
||||
convos, err := strconv.Atoi(v.(string))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lr.Visitor.ConversationsUnread = uint32(convos)
|
||||
case "alerts_unviewed":
|
||||
alerts, err := strconv.Atoi(v.(string))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lr.Visitor.AlertsUnviewed = uint32(alerts)
|
||||
case "total_unread":
|
||||
unread, err := strconv.Atoi(v.(string))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lr.Visitor.ConversationsUnread = uint32(unread)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var ErrNoXFToken = errors.New("Failed to locate xfToken on page.")
|
||||
|
||||
func xfToken(page io.Reader) (string, error) {
|
||||
z := html.NewTokenizer(page)
|
||||
for i := z.Next(); i != html.ErrorToken; i = z.Next() {
|
||||
tk := z.Token()
|
||||
if tk.DataAtom == atom.Html {
|
||||
for _, a := range tk.Attr {
|
||||
switch a.Key {
|
||||
case "data-csrf":
|
||||
return a.Val, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", ErrNoXFToken
|
||||
}
|
||||
|
||||
func loginURL(host *url.URL) *url.URL {
|
||||
return &url.URL{
|
||||
Scheme: "https",
|
||||
Host: host.Hostname(),
|
||||
Path: "login",
|
||||
return url.Values{
|
||||
"_xfToken": {xfToken},
|
||||
"login": {creds.Username},
|
||||
"password": {creds.Password},
|
||||
"remember": {string(rem)},
|
||||
}
|
||||
}
|
||||
|
||||
func (kf *KF) newLoginRequest(ctx context.Context, user string, pass string) (*http.Request, error) {
|
||||
u := loginURL(kf.domain)
|
||||
func (kf *KF) newLoginRequest(ctx context.Context, creds Credentials) (*http.Request, error) {
|
||||
u := kf.urlFromPath("login")
|
||||
|
||||
resp, err := kf.GetPage(ctx, u)
|
||||
resp, err := kf.Get(ctx, u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
xfToken, err := xfToken(resp.Body)
|
||||
xfToken, err := XFToken(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
form := url.Values{
|
||||
"_xfToken": {xfToken},
|
||||
"login": {user},
|
||||
"password": {pass},
|
||||
"remember": {"1"},
|
||||
}
|
||||
form := loginForm(xfToken, creds)
|
||||
|
||||
// i.e. https://kiwifarms.net/login/login
|
||||
postURL := fmt.Sprintf("%s/login", u.String())
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", postURL, strings.NewReader(form.Encode()))
|
||||
reqURL := fmt.Sprintf("%s/login", u.String())
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -133,13 +56,13 @@ func (kf *KF) newLoginRequest(ctx context.Context, user string, pass string) (*h
|
||||
return req, nil
|
||||
}
|
||||
|
||||
func (kf *KF) Login(ctx context.Context, user string, pass string) (*http.Response, error) {
|
||||
req, err := kf.newLoginRequest(ctx, user, pass)
|
||||
func (kf *KF) Login(ctx context.Context, creds Credentials) (*http.Response, error) {
|
||||
req, err := kf.newLoginRequest(ctx, creds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := kf.client.Do(req)
|
||||
resp, err := kf.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -151,7 +74,7 @@ func (kf *KF) Login(ctx context.Context, user string, pass string) (*http.Respon
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func TwoFactorForm(xfToken string, code uint32, u *url.URL) url.Values {
|
||||
func twoFactorForm(xfToken string, code uint32, u *url.URL) url.Values {
|
||||
return url.Values{
|
||||
"code": {fmt.Sprint(code)},
|
||||
"provider": {"totp"},
|
||||
@@ -166,19 +89,19 @@ func TwoFactorForm(xfToken string, code uint32, u *url.URL) url.Values {
|
||||
}
|
||||
}
|
||||
|
||||
func (kf *KF) twoFactorAuthForm(ctx context.Context, resp *http.Response, code uint32) (*http.Request, error) {
|
||||
func (kf *KF) newTwoFactorRequest(ctx context.Context, resp *http.Response, code uint32) (*http.Request, error) {
|
||||
u := resp.Request.URL
|
||||
if !strings.Contains(u.Path, "two-step") {
|
||||
// TODO: move err to proper type with url included.
|
||||
return nil, errors.New("Did not redirect to two-step page.")
|
||||
}
|
||||
|
||||
xfToken, err := xfToken(resp.Body)
|
||||
xfToken, err := XFToken(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
form := TwoFactorForm(xfToken, code, u)
|
||||
form := twoFactorForm(xfToken, code, u)
|
||||
reqURL := fmt.Sprintf("https://%s/login/two-step", u.Hostname())
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(form.Encode()))
|
||||
@@ -191,28 +114,12 @@ func (kf *KF) twoFactorAuthForm(ctx context.Context, resp *http.Response, code u
|
||||
}
|
||||
|
||||
func (kf *KF) TwoFactorAuth(ctx context.Context, resp *http.Response, code uint32) (*http.Response, error) {
|
||||
req, err := kf.twoFactorAuthForm(ctx, resp, code)
|
||||
req, err := kf.newTwoFactorRequest(ctx, resp, code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kf.client.Do(req)
|
||||
}
|
||||
|
||||
func setCookie(u *url.URL, jar http.CookieJar, newCookie *http.Cookie) {
|
||||
cookies := jar.Cookies(u)
|
||||
|
||||
for i, c := range cookies {
|
||||
if c.Name == newCookie.Name {
|
||||
cookies[i] = newCookie
|
||||
jar.SetCookies(u, cookies)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Append if not already existing.
|
||||
cookies = append(cookies, newCookie)
|
||||
jar.SetCookies(u, cookies)
|
||||
return kf.Do(req)
|
||||
}
|
||||
|
||||
func (kf *KF) RefreshSession(ctx context.Context) error {
|
||||
@@ -222,7 +129,7 @@ func (kf *KF) RefreshSession(ctx context.Context) error {
|
||||
Value: "",
|
||||
})
|
||||
|
||||
resp, err := kf.GetPage(ctx, kf.domain)
|
||||
resp, err := kf.Get(ctx, kf.domain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user