1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use error::{ErrorKind::*, *};
use regex::*;
use reqwest;
use select::{document::*, predicate::*};
use serde_json;
use std::time::SystemTime;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Token {
pub expires_in: u64,
pub scope: String,
pub token_type: String,
pub access_token: String,
pub user_id: String,
pub refresh_token: String,
pub session_id: String,
#[serde(default = "cur_date")]
pub updated_at: u64,
}
fn cur_date() -> u64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("System time is before Unix Epoch")
.as_secs()
}
impl Token {
pub fn from_response(response: &str) -> Result<Token> {
println!("{}", response);
Ok(serde_json::from_str(response)?)
}
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 is_expired(&self) -> bool {
self.updated_at + self.expires_in - cur_date() <= 0
}
pub fn refresh(&self) -> Result<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> {
let (username, password) = (username.into(), password.into());
let garegex =
Regex::new(r"var galaxyAccounts = new GalaxyAccounts\('(.+)','(.+)'\)").unwrap();
let client = reqwest::Client::new();
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");
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()?;
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
.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]", "".to_string());
form_parameters.insert("login[_token]", lid);
let mut login_response = client
.post("https://login.gog.com/login")
.form(&form_parameters)
.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")
.to_string();
Err(IncorrectCredentials.into())
} 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())
}
}
} else {
Err(MissingField("login id".to_string()).into())
}
}
} else {
Err(MissingField("auth url".to_string()).into())
}
}
}