mirror of
https://gitgud.io/yats/libkiwi.git
synced 2026-06-29 15:42:03 -04:00
Compare commits
4 Commits
1e9bd059f0
..
login
| Author | SHA1 | Date | |
|---|---|---|---|
| c7ccfc65bf | |||
| 74b923f06f | |||
| 92438691f1 | |||
| 72840af90e |
@@ -29,6 +29,16 @@ func loginForm(xfToken string, creds Credentials) url.Values {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newFormRequest(ctx context.Context, reqURL string, form url.Values) (*http.Request, error) {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(form.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (kf *KF) newLoginRequest(ctx context.Context, creds Credentials) (*http.Request, error) {
|
func (kf *KF) newLoginRequest(ctx context.Context, creds Credentials) (*http.Request, error) {
|
||||||
u := kf.urlFromPath("login")
|
u := kf.urlFromPath("login")
|
||||||
|
|
||||||
@@ -43,17 +53,8 @@ func (kf *KF) newLoginRequest(ctx context.Context, creds Credentials) (*http.Req
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
form := loginForm(xfToken, creds)
|
// e.g. https://kiwifarms.net/login/login
|
||||||
|
return newFormRequest(ctx, fmt.Sprintf("%s/login", u.String()), loginForm(xfToken, creds))
|
||||||
// i.e. https://kiwifarms.net/login/login
|
|
||||||
reqURL := fmt.Sprintf("%s/login", u.String())
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(form.Encode()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
|
|
||||||
return req, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kf *KF) Login(ctx context.Context, creds Credentials) (*http.Response, error) {
|
func (kf *KF) Login(ctx context.Context, creds Credentials) (*http.Response, error) {
|
||||||
@@ -101,16 +102,7 @@ func (kf *KF) newTwoFactorRequest(ctx context.Context, resp *http.Response, code
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
form := twoFactorForm(xfToken, code, u)
|
return newFormRequest(ctx, fmt.Sprintf("https://%s/login/two-step", u.Hostname()), 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()))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
||||||
|
|
||||||
return req, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kf *KF) TwoFactorAuth(ctx context.Context, resp *http.Response, code uint32) (*http.Response, error) {
|
func (kf *KF) TwoFactorAuth(ctx context.Context, resp *http.Response, code uint32) (*http.Response, error) {
|
||||||
@@ -122,18 +114,30 @@ func (kf *KF) TwoFactorAuth(ctx context.Context, resp *http.Response, code uint3
|
|||||||
return kf.Do(req)
|
return kf.Do(req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kf *KF) RefreshSession(ctx context.Context) error {
|
func (kf *KF) IsLoggedIn() bool {
|
||||||
|
cookie := kf.Cookies.GetCookie(kf.domain, "xf_user")
|
||||||
|
return !(cookie == nil || cookie.Value == "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kf *KF) RefreshSession(ctx context.Context) (string, error) {
|
||||||
|
const COOKIE_NAME = "xf_session"
|
||||||
|
|
||||||
// Clear any existing session token to request a new one.
|
// Clear any existing session token to request a new one.
|
||||||
setCookie(kf.domain, kf.client.Jar, &http.Cookie{
|
kf.Cookies.SetCookie(kf.domain, &http.Cookie{
|
||||||
Name: "xf_session",
|
Name: COOKIE_NAME,
|
||||||
Value: "",
|
Value: "",
|
||||||
})
|
})
|
||||||
|
|
||||||
resp, err := kf.Get(ctx, kf.domain)
|
resp, err := kf.Get(ctx, kf.domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
|
|
||||||
return nil
|
session := kf.Cookies.GetCookie(kf.domain, COOKIE_NAME)
|
||||||
|
if session == nil || session.Value == "" {
|
||||||
|
return "", fmt.Errorf("Failed to get new %s cookie", COOKIE_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
return session.Value, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,6 +63,20 @@ func TestLogin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
t.Logf("2FA response: %+v\n", lr)
|
t.Logf("2FA response: %+v\n", lr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Logf("Checking RefreshSession()")
|
||||||
|
session, err := kf.RefreshSession(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Logf("xf_session: %s\n", session)
|
||||||
|
|
||||||
|
t.Logf("Checking IsLoggedIn()\n")
|
||||||
|
if !kf.IsLoggedIn() {
|
||||||
|
t.Error("IsLoggedIn() test returned false.")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func genTest2FACode() (uint32, error) {
|
func genTest2FACode() (uint32, error) {
|
||||||
|
|||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
package libkiwi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// http.CookieJar interface wrapper to add convenience functions.
|
||||||
|
type cookieJar struct {
|
||||||
|
jar *http.CookieJar
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *cookieJar) GetCookie(u *url.URL, name string) *http.Cookie {
|
||||||
|
jar := (*j.jar)
|
||||||
|
cookies := jar.Cookies(u)
|
||||||
|
for _, c := range cookies {
|
||||||
|
if c.Name == name {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *cookieJar) SetCookie(u *url.URL, newCookie *http.Cookie) {
|
||||||
|
jar := (*j.jar)
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
module gitgud.io/yats/libkiwi
|
module gitgud.io/yats/libkiwi
|
||||||
|
|
||||||
go 1.26.1
|
go 1.26.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
gitgud.io/yats/cerberus v0.0.0-20260214165307-66e6f74a4be9
|
gitgud.io/yats/cerberus v0.0.0-20260214165307-66e6f74a4be9
|
||||||
|
|||||||
@@ -19,12 +19,14 @@ func XFToken(page io.Reader) (string, error) {
|
|||||||
z := html.NewTokenizer(page)
|
z := html.NewTokenizer(page)
|
||||||
for i := z.Next(); i != html.ErrorToken; i = z.Next() {
|
for i := z.Next(); i != html.ErrorToken; i = z.Next() {
|
||||||
tk := z.Token()
|
tk := z.Token()
|
||||||
if tk.DataAtom == atom.Html {
|
if tk.DataAtom != atom.Html {
|
||||||
for _, a := range tk.Attr {
|
continue
|
||||||
switch a.Key {
|
}
|
||||||
case "data-csrf":
|
|
||||||
return a.Val, nil
|
for _, a := range tk.Attr {
|
||||||
}
|
switch a.Key {
|
||||||
|
case "data-csrf":
|
||||||
|
return a.Val, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,22 +34,6 @@ func XFToken(page io.Reader) (string, error) {
|
|||||||
return "", ErrNoXFToken
|
return "", ErrNoXFToken
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (kf *KF) Do(req *http.Request) (*http.Response, error) {
|
func (kf *KF) Do(req *http.Request) (*http.Response, error) {
|
||||||
var (
|
var (
|
||||||
ctx = req.Context()
|
ctx = req.Context()
|
||||||
|
|||||||
+16
-15
@@ -13,33 +13,34 @@ import (
|
|||||||
gq "github.com/PuerkitoBio/goquery"
|
gq "github.com/PuerkitoBio/goquery"
|
||||||
)
|
)
|
||||||
|
|
||||||
const _USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; rv:60.0) Gecko/20100101 Firefox/60.0"
|
|
||||||
|
|
||||||
type KF struct {
|
type KF struct {
|
||||||
client http.Client
|
client http.Client
|
||||||
domain *url.URL
|
domain *url.URL
|
||||||
|
|
||||||
|
Cookies cookieJar
|
||||||
}
|
}
|
||||||
|
|
||||||
// Supply your own http.Client to route through any proxies.
|
// Supply your own http.Client to route through any proxies.
|
||||||
func NewKF(hc http.Client, host *url.URL) (*KF, error) {
|
func NewKF(hc http.Client, host *url.URL) (KF, error) {
|
||||||
u, err := url.Parse(fmt.Sprintf("https://%s", host.Hostname()))
|
u, err := url.Parse(fmt.Sprintf("https://%s", host.Hostname()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return KF{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
jar, err := cookiejar.New(nil)
|
if hc.Jar == nil {
|
||||||
if err != nil {
|
jar, err := cookiejar.New(nil)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return KF{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hc.Jar = jar
|
||||||
}
|
}
|
||||||
|
|
||||||
hc.Jar = jar
|
return KF{
|
||||||
|
client: hc,
|
||||||
kf := &KF{
|
domain: u,
|
||||||
client: hc,
|
Cookies: cookieJar{&hc.Jar},
|
||||||
domain: u,
|
}, nil
|
||||||
}
|
|
||||||
|
|
||||||
return kf, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
|
|||||||
+3
-3
@@ -58,7 +58,7 @@ func TestGetPost(t *testing.T) {
|
|||||||
t.Logf("Post text: %s\n", post.TextContent())
|
t.Logf("Post text: %s\n", post.TextContent())
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestKF() (*KF, error) {
|
func newTestKF() (KF, error) {
|
||||||
host := os.Getenv("TEST_HOST")
|
host := os.Getenv("TEST_HOST")
|
||||||
if host == "" {
|
if host == "" {
|
||||||
host = _TEST_HOST
|
host = _TEST_HOST
|
||||||
@@ -66,7 +66,7 @@ func newTestKF() (*KF, error) {
|
|||||||
|
|
||||||
u, err := url.Parse("https://" + host)
|
u, err := url.Parse("https://" + host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return KF{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
p := proxy.FromEnvironment()
|
p := proxy.FromEnvironment()
|
||||||
@@ -76,7 +76,7 @@ func newTestKF() (*KF, error) {
|
|||||||
|
|
||||||
jar, err := cookiejar.New(nil)
|
jar, err := cookiejar.New(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return KF{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hc := http.Client{
|
hc := http.Client{
|
||||||
|
|||||||
Reference in New Issue
Block a user