Extended version of 1lann/messenger
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
2.9 KiB

package messenger
import (
"bytes"
"errors"
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"
"github.com/PuerkitoBio/goquery"
)
// Errors that are returned by Login.
var (
ErrLoginError = errors.New("messenger: incorrect login credentials")
ErrLoginCheckpoint = errors.New("messenger: login checkpoint")
)
var jsCookiePattern = regexp.MustCompile("\\[\"(_js_[^\"]+)\",\"([^\"]+)\",")
func (s *Session) createLoginRequest(email, password string) (*http.Request, error) {
req, _ := http.NewRequest(http.MethodGet, facebookURL, nil)
req.Header = defaultHeader()
resp, err := s.doRequest(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(data))
if err != nil {
return nil, err
}
form := make(url.Values)
doc.Find("#login_form input").Each(func(i int, s *goquery.Selection) {
name, found := s.Attr("name")
if !found {
return
}
value, found := s.Attr("value")
if !found {
return
}
form.Set(name, value)
})
cookies := s.client.Jar.Cookies(fbURL)
matches := jsCookiePattern.FindAllStringSubmatch(string(data), -1)
for _, match := range matches {
cookies = append(cookies, &http.Cookie{
Name: match[1],
Value: strings.Replace(match[2], "\\/", "/", -1),
Domain: "facebook.com",
})
}
s.client.Jar.SetCookies(fbURL, cookies)
form.Set("email", email)
form.Set("pass", password)
form.Set("default_persistent", "1")
form.Set("lgnjs", strconv.FormatInt(time.Now().Unix(), 10))
_, offset := time.Now().Zone()
form.Set("timezone", strconv.Itoa(-offset/60))
form.Set("lgndim", "eyJ3IjoxNDQwLCJoIjo5MDAsImF3IjoxNDQwLCJhaCI6OTAwLCJjIjoyNH0=")
form.Set("next", "https://www.facebook.com/")
loginReq, _ := http.NewRequest(http.MethodPost, loginURL, strings.NewReader(form.Encode()))
loginReq.Header = defaultHeader()
loginReq.Header.Set("Content-Type", formURLEncoded)
return loginReq, nil
}
// Login logs the session in to a Facebook account.
func (s *Session) Login(email, password string) error {
req, err := s.createLoginRequest(email, password)
if err != nil {
return err
}
resp, err := s.doRequest(req)
if err == nil {
resp.Body.Close()
return ErrLoginError
}
urlErr, ok := err.(*url.Error)
if !ok || urlErr.Err != errNoRedirects {
return err
}
err = handleLoginRedirect(resp)
if err != nil {
return err
}
return nil
}
func handleLoginRedirect(resp *http.Response) error {
redirURL, err := resp.Location()
if err != nil {
return err
}
if strings.Contains(redirURL.String(), "https://www.facebook.com/checkpoint") {
return ErrLoginCheckpoint
}
if strings.Contains(redirURL.String(), "https://www.facebook.com/login.php?") {
return ErrLoginError
}
if redirURL.String() == "https://www.facebook.com/" || redirURL.String() == "https://www.facebook.com" {
return nil
}
return ParseError{"unexpected redirect to " + redirURL.String()}
}