diff --git a/src/account.rs b/src/account.rs index d331930..d2a9058 100644 --- a/src/account.rs +++ b/src/account.rs @@ -6,9 +6,10 @@ use rand::Rng; use rand::RngCore; use regex::Regex; use std::time::Duration; +use std::collections::HashMap; +use percent_encoding::percent_decode_str; -use crate::config::PROXY_TIMEOUT; -use crate::config::{ADJ_LIST, NAME_LIST}; +use crate::config::{PROXY_TIMEOUT, USER_AGENT, ADJ_LIST, NAME_LIST}; use crate::debug; use crate::errors::{crash, TrainCrash}; use crate::templates::get_lang; @@ -142,11 +143,11 @@ pub async fn login( ) -> Result { debug(&format!("Sending forged login for user {}", user)); - // 1. GET /login + // 1. GET /csrftoken let mut login_get = client - .get(format!("{}/{}", CONFIG.nextcloud_url, "login")) + .get(format!("{}/{}", CONFIG.nextcloud_url, "csrftoken")) .timeout(Duration::new(PROXY_TIMEOUT, 0)) - .header("User-Agent", "Actix-web") + .header("User-Agent", USER_AGENT) .send() .await .map_err(|e| { @@ -156,22 +157,63 @@ pub async fn login( // rewrite cookie headers from GET to POST let mut str_cookiepair = String::new(); - for h_value in login_get.headers().get_all("set-cookie") { - str_cookiepair = format!( - "{}; {}", - str_cookiepair, - h_value.clone().to_str().map_err(|e| { - eprintln!("error_login_cookiepair: {}", e); - crash(get_lang(&req), "error_login_cookiepair") - })? - ); + + // remove duplicate oc cookie (nextcloud bug) + // leading to sncf being unable to forge logins + let cookie_set = login_get.headers().get_all("set-cookie"); + let mut cookie_map: HashMap = HashMap::new(); + for c in cookie_set { + // get str version of cookie header + let c_str = c.to_str().map_err(|e| { + eprintln!("error_login_cookiepair (1): {}", e); + crash(get_lang(&req), "error_login_cookiepair") + })?; + + // percent decode + let c_str = percent_decode_str(c_str).decode_utf8_lossy(); + + //then remove values after ';' + let c_str_arr = c_str.split(';').collect::>(); + + let c_str = c_str_arr.first() + .expect("error: cookiepair split does not have a first value. shouldn't happen."); + + // split cookie key and cookie value + // split_once would work best but it's nightly-only for now + let c_str_arr = c_str.split('=').collect::>(); + + let c_key = c_str_arr.first() + .expect("error: cookie key split does not have a first value, shouldn't happen."); + + let c_value = c_str.replace(&format!("{}=", c_key), ""); + + if c_key != c_str { + // if the key already exists in hashmap, replace its value + // else, insert it + if let Some(c_sel) = cookie_map.get_mut(*c_key) { + *c_sel = c_value; + } + else { + cookie_map.insert(c_key.to_string(), c_value); + } + } + else { + eprintln!("error_login_cookiepair (2)"); + return Err(crash(get_lang(&req), "error_login_cookiepair")); + } } + for (cookie_k, cookie_v) in cookie_map { + str_cookiepair.push_str(&format!("{}={}; ", cookie_k, cookie_v)); + } + + println!("SET-COOKIE: {}", str_cookiepair); + // load requesttoken regex lazy_static! { - static ref RE: Regex = Regex::new(r#"requesttoken="(?P.*)""#) + static ref RE: Regex = Regex::new(r#"\{"token":"(?P[^"]*)"\}"#) .expect("Error while parsing the requesttoken regex"); - } + } let post_body = login_get.body().await.map_err(|e| { eprintln!("error_login_get_body: {}", e); @@ -186,18 +228,18 @@ pub async fn login( eprintln!("error_login_regex (no capture)"); crash(get_lang(&req), "error_login_regex") })? - .name("token") + .name("token") .ok_or_else(|| { eprintln!("error_login_regex (no capture named token)"); crash(get_lang(&req), "error_login_regex") })? - .as_str(); + .as_str(); // 2. POST /login let mut login_post = client .post(format!("{}/{}", CONFIG.nextcloud_url, "login")) .timeout(Duration::new(PROXY_TIMEOUT, 0)) - .header("User-Agent", "Actix-web"); + .header("User-Agent", USER_AGENT); // include all NC cookies in one cookie (cookie pair) login_post = login_post.header("Cookie", str_cookiepair); @@ -211,7 +253,7 @@ pub async fn login( timezone_offset: "2", requesttoken, }) - .await + .await .map_err(|e| { eprintln!("error_login_post: {}", e); crash(get_lang(&req), "error_login_post") @@ -219,6 +261,7 @@ pub async fn login( // 3. set the same cookies in the user's browser let mut user_response = HttpResponse::SeeOther(); + for item in response_post.headers().clone().get_all("set-cookie") { user_response.header( "Set-Cookie",