@@ 4,6 4,10 @@ use reqwest;
use select::{document::*, predicate::*};
use serde_json;
use std::time::SystemTime;
+use user_agent::*;
+fn convert_rsession(err: ::user_agent::ReqwestSessionError) -> crate::error::Error {
+ ErrorKind::SessionNetwork(err).into()
+}
/// An OAuth token. Will usually expire after an hour.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Token {
@@ 28,14 32,24 @@ fn cur_date() -> u64 {
}
impl Token {
/// Creates a token from a response from /token
- pub fn from_response(response: &str) -> Result<Token> {
- println!("{}", response);
- Ok(serde_json::from_str(response)?)
+ pub fn from_response(response: impl Into<String>) -> Result<Token> {
+ Ok(serde_json::from_str(response.into().as_str())?)
}
/// Fetches a token using a login code
- pub fn from_login_code(code: &str) -> Result<Token> {
- let mut res = reqwest::get(&("https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fembed.gog.com%2Fon_login_success%3Forigin%3Dclient&layout=client2&code=".to_string()+&code+""))?;
- Token::from_response(&res.text()?)
+ pub fn from_login_code(code: impl Into<String>) -> Result<Token> {
+ let mut res = reqwest::get(&("https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fembed.gog.com%2Fon_login_success%3Forigin%3Dclient&layout=client2&code=".to_string()+&code.into()+""))?;
+ let text = res.text()?;
+ println!("{:?}", text);
+ Token::from_response(text)
+ }
+ pub fn from_home_code(code: impl Into<String>) -> Result<Token> {
+ let url = format!("https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fwww.gog.com%2Fon_login_success&layout=client2&code={}", code.into());
+ println!("URL:{}", url);
+ let mut res = reqwest::get(&url)?;
+ println!("{:?}", res);
+ let text = res.text()?;
+ println!("{:?}", text);
+ Token::from_response(text)
}
/// Checks if token has expired
pub fn is_expired(&self) -> bool {
@@ 46,105 60,204 @@ impl Token {
let mut res = reqwest::get(&("https://auth.gog.com/token?client_id=46899977096215655&client_secret=9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9&grant_type=refresh_token&redirect_uri=https://embed.gog.com/on_login_success?origin=client&refresh_token=".to_string()+&self.refresh_token))?;
Ok(serde_json::from_str(&res.text()?)?)
}
- pub fn login(username: impl Into<String>, password: impl Into<String>) -> Result<Token> {
+ pub fn login<F>(
+ username: impl Into<String>,
+ password: impl Into<String>,
+ two_step_token_fn: F,
+ ) -> Result<Token>
+ where
+ F: Fn() -> String,
+ {
let (username, password) = (username.into(), password.into());
let garegex =
Regex::new(r"var galaxyAccounts = new GalaxyAccounts\('(.+)','(.+)'\)").unwrap();
- let client = reqwest::Client::new();
+ let gsregex = Regex::new(r"(galaxy-login-s=.+;) expires").unwrap();
+ let mut client = ReqwestSession::new(
+ reqwest::ClientBuilder::new()
+ .redirect(reqwest::RedirectPolicy::none())
+ .build()
+ .unwrap(),
+ );
+ let mut normal_client = ReqwestSession::new(reqwest::ClientBuilder::new().build().unwrap());
info!("Fetching GOG home page to get auth url");
- let mut result = client.get("https://gog.com").send()?;
-
- let text = result.text().expect("Couldn't get home page text");
+ let mut result = normal_client
+ .get("https://gog.com")
+ .map_err(convert_rsession)?;
+ println!("{:?}", result);
+ let text = result
+ .text()
+ .expect("Couldn't get home page text")
+ .to_owned()
+ .to_string();
if let Some(captures) = garegex.captures(&text) {
let auth_url = captures[1].to_string();
println!("Auth URl: {}", auth_url);
info!("Got URL, requesting auth page");
- let mut aresult = client.get(&auth_url).send()?;
+ let mut aresult = client.get(&auth_url).map_err(convert_rsession)?;
+ while aresult.status().is_redirection() {
+ println!("Redirect!");
+ let mut next_url = aresult
+ .headers()
+ .get("Location")
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string();
+ println!("{:?}", aresult);
+ aresult = client
+ .get(reqwest::Url::parse(&next_url).unwrap())
+ .map_err(convert_rsession)?
+ }
+ println!("{:?}", aresult);
info!("Auth page request successful");
let atext = aresult.text().expect("Couldn't get auth page text");
let document = Document::from(atext.as_str());
info!("Checking for captchas");
- let gcaptcha = document.find(Class("g-recaptcha"));
- if gcaptcha.count() > 0 {
- error!("Captcha detected. Wait and try again.");
- Err(NotAvailable.into())
- } else {
- let mut login_id = document.find(Attr("id", "login__token"));
- if let Some(input) = login_id.next() {
- info!("Got login ID");
- let lid = input
+ let gcaptcha = document.find(Attr("defer", ""));
+ for poss in gcaptcha {
+ if poss.html().contains("recaptcha") {
+ error!("Captcha detected. Wait and try again.");
+ println!("Captcha");
+ return Err(NotAvailable.into());
+ }
+ }
+ let mut login_id = document.find(Attr("id", "login__token"));
+ if let Some(input) = login_id.next() {
+ info!("Got login ID");
+ let lid = input
+ .attr("value")
+ .expect("Login id field has no value.")
+ .to_string();
+ info!("Searching home page text with regex for url");
+ let mut form_parameters = std::collections::HashMap::new();
+ form_parameters.insert("login[username]", username);
+ form_parameters.insert("login[password]", password);
+ form_parameters.insert("login[login]", String::default());
+ form_parameters.insert("login[login_flow]", "default".to_string());
+ form_parameters.insert("login[_token]", lid);
+ println!("{:?}", form_parameters);
+ let check_url = reqwest::Url::parse("https://login.gog.com/login_check").unwrap();
+ let mut request = client
+ .client
+ .post_request(&check_url)
+ .form(&form_parameters);
+ let mut cookies_processed: Vec<cookie::Cookie> = client
+ .store
+ .get_request_cookies(&check_url)
+ .cloned()
+ .collect();
+ request = request.add_cookies(cookies_processed.iter().collect());
+ println!("{:?}", request);
+ let mut login_response = request.send()?;
+ println!("{:?}", login_response);
+ while login_response.status().is_redirection() {
+ println!("Redirect!");
+ let mut next_url = login_response
+ .headers()
+ .get("Location")
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string();
+ println!("{:?}", login_response);
+ login_response = client
+ .client
+ .get_request(&reqwest::Url::parse(&next_url).unwrap())
+ .add_cookies(cookies_processed.iter().collect())
+ .send()?;
+ }
+ let login_text = login_response.text().expect("Couldn't fetch login text");
+ let login_doc = Document::from(login_text.as_str());
+ let mut two_step_search =
+ login_doc.find(Attr("id", "second_step_authentication__token"));
+ let url = login_response.url().clone();
+ if let Some(two_node) = two_step_search.next() {
+ warn!("Two step authentication token needed.");
+ println!("Two step token required. This is not implmented yet.");
+ let two_token_secret = two_node
.attr("value")
- .expect("Login id field has no value.")
+ .expect("No two step token found")
.to_string();
- info!("Searching home page text with regex for url");
- let mut form_parameters = std::collections::HashMap::new();
- form_parameters.insert("login[username]", username);
- form_parameters.insert("login[password]", password);
- form_parameters.insert("login[login]", "".to_string());
- form_parameters.insert("login[_token]", lid);
+ let two_token = two_step_token_fn();
+ if two_token.len() < 4 {
+ return Err(MissingField("Token too short".to_string()).into());
+ }
+ let mut token_iter = two_token.chars().map(|x| x.to_string());
+ let mut token_parameters = std::collections::HashMap::new();
+ token_parameters.insert(
+ "second_step_authentication[token][letter_1]",
+ token_iter.next().unwrap(),
+ );
+ token_parameters.insert(
+ "second_step_authentication[token][letter_2]",
+ token_iter.next().unwrap(),
+ );
+ token_parameters.insert(
+ "second_step_authentication[token][letter_3]",
+ token_iter.next().unwrap(),
+ );
+ token_parameters.insert(
+ "second_step_authentication[token][letter_4]",
+ token_iter.next().unwrap(),
+ );
+ token_parameters.insert("second_step_authentication[send]", String::default());
+ token_parameters.insert("second_step_authentication[_token]", two_token_secret);
let mut login_response = client
- .post("https://login.gog.com/login")
- .form(&form_parameters)
+ .client
+ .post_request(&url)
+ .add_cookies(cookies_processed.iter().collect())
.send()?;
- let mut cookie_headers = reqwest::header::HeaderMap::new();
- let mut url;
- loop {
- if login_response.status().is_redirection() {
- {
- let login_headers = login_response.headers();
- for cookie in login_headers.get_all("set-cookie") {
- cookie_headers.append(
- "Cookie",
- reqwest::header::HeaderValue::from_str(&format!(
- "{};",
- cookie.to_str().unwrap()
- ))
- .unwrap(),
- );
- }
- url = login_headers
- .get("location")
- .unwrap()
- .to_str()
- .unwrap()
- .to_string();
- }
- login_response =
- client.get(&url).headers(cookie_headers.clone()).send()?;
- } else {
- break;
- }
- }
- let login_text = login_response.text().expect("Couldn't fetch login text");
- let login_doc = Document::from(login_text.as_str());
- let mut two_step_search =
- login_doc.find(Attr("id", "second_step_authentication__token"));
- let url = login_response.url();
- if let Some(two_node) = two_step_search.next() {
- info!("Two step authentication token needed.");
- let two_token = two_node
- .attr("value")
- .expect("No two step token found")
+ while login_response.status().is_redirection() {
+ println!("Redirect!");
+ let mut next_url = login_response
+ .headers()
+ .get("Location")
+ .unwrap()
+ .to_str()
+ .unwrap()
.to_string();
- Err(IncorrectCredentials.into())
+ println!("{:?}", login_response);
+ login_response = client
+ .client
+ .get_request(&reqwest::Url::parse(&next_url).unwrap())
+ .add_cookies(cookies_processed.iter().collect())
+ .send()?;
+ }
+ if url.as_str().contains("on_login_success") {
+ println!("{:?}", url);
+ let code = url
+ .query_pairs()
+ .filter(|(k, _v)| k == "code")
+ .map(|x| x.1)
+ .next()
+ .unwrap();
+ Token::from_home_code(code)
} else {
- if url.as_str().contains("on_login_success") {
- println!("{:?}", url);
- let code = url
- .query_pairs()
- .filter(|(k, _v)| k == "code")
- .map(|x| x.1)
- .next()
- .unwrap();
- Token::from_login_code(&code)
- } else {
- error!("Login failed.");
- Err(IncorrectCredentials.into())
- }
+ println!("{:?}", url);
+ println!("{:?}", login_response);
+ error!("Login failed.");
+ Err(IncorrectCredentials.into())
}
} else {
- Err(MissingField("login id".to_string()).into())
+ println!("{:?}", cookies_processed);
+ if url.as_str().contains("on_login_success") {
+ println!("{:?}", url);
+ let code = url
+ .query_pairs()
+ .filter(|(k, _v)| k == "code")
+ .map(|x| x.1)
+ .next()
+ .unwrap();
+ Token::from_home_code(code)
+ } else {
+ println!("{:?}", url);
+ println!("{:?}", login_response);
+ error!("Login failed.");
+ Err(IncorrectCredentials.into())
+ }
}
+ } else {
+ Err(MissingField("login id".to_string()).into())
}
} else {
Err(MissingField("auth url".to_string()).into())