hangups-rs

Rust version of hangups (WIP) Currently Login not possible
git clone git://archive.git.mtrnord.blog/Nordgedanken/hangups-rs.git
Log | Files | Refs | README | LICENSE

commit 79a0d08b130fa6773baa4a178d576a96aaac318c
parent b54e35f960f78cd09030f573f3f6c06ae98c60b2
Author: MTRNord <mtrnord1@gmail.com>
Date:   Thu, 20 Apr 2017 10:40:36 +0200

prepare llogin and update hyper (not working yet), add sendMessage (in itself working)

Diffstat:
MCargo.toml | 11++++++++---
Alog.txt | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/hangups/mod.rs | 149++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msrc/lib.rs | 345++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
4 files changed, 485 insertions(+), 92 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -4,8 +4,13 @@ version = "0.0.1" authors = ["MTRNord"] [dependencies] -yup-oauth2 = "1.0.5" -hyper = "0.10.2" -hyper-native-tls = "0.2.2" +hyper = { git = "https://github.com/hyperium/hyper" } protobuf = "1.2.2" rand = "0.3" +sha1 = "0.2.0" +rustc-serialize = "0.3.23" +jconfig = "0.1.0" +regex = "0.2" +url = "1.4.0" +tokio-core = "0.1.6" +futures = "0.1.13" diff --git a/log.txt b/log.txt @@ -0,0 +1,72 @@ + Compiling hangups v0.0.1 (file:///source) +error[E0382]: use of moved value: `core` + --> src/lib.rs:257:9 + | +233 | let req_raw = client.get(auth_uri).and_then(|res| { + | ----- value moved (into closure) here +... +257 | core.run(req).unwrap(); + | ^^^^ value used here after move + | + = note: move occurs because `core` has type `tokio_core::reactor::Core`, which does not implement the `Copy` trait + +error: cannot borrow immutable borrowed content as mutable + --> src/lib.rs:247:115 + | +247 | do_auth_step(&mut core, &client, email_uri, &res, &body.as_str(), client_email, client_email, &mut headers); + | ^^^^^^^^^^^^ cannot borrow as mutable + +error[E0505]: cannot move out of `res` because it is borrowed + --> src/lib.rs:240:18 + | +236 | &res.headers() + | --- borrow of `res` occurs here +... +240 | &res.body().for_each(|chunk| { + | ^^^ move out of `res` occurs here + +error[E0382]: use of moved value: `core` + --> src/lib.rs:247:39 + | +246 | let core_raw = core; + | -------- value moved here +247 | do_auth_step(&mut core, &client, email_uri, &res, &body.as_str(), client_email, client_email, &mut headers); + | ^^^^ value used here after move + | + = note: move occurs because `core` has type `tokio_core::reactor::Core`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `res` + --> src/lib.rs:247:66 + | +240 | &res.body().for_each(|chunk| { + | --- value moved here +... +247 | do_auth_step(&mut core, &client, email_uri, &res, &body.as_str(), client_email, client_email, &mut headers); + | ^^^ value used here after move + | + = note: move occurs because `res` has type `hyper::client::Response`, which does not implement the `Copy` trait + +error[E0382]: use of moved value: `client_email` + --> src/lib.rs:247:101 + | +247 | do_auth_step(&mut core, &client, email_uri, &res, &body.as_str(), client_email, client_email, &mut headers); + | ------------ ^^^^^^^^^^^^ value used here after move + | | + | value moved here + | + = note: move occurs because `client_email` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0505]: cannot move out of `body` because it is borrowed + --> src/hangups/mod.rs:125:12 + | +109 | let post_req = client.request(fresh_request).map_err(|_| ()).and_then(|res| { + | ----- borrow of `body` occurs here +... +125 | Ok(body) + | ^^^^ move out of `body` occurs here + +error: aborting due to 7 previous errors + +error: Could not compile `hangups`. + +To learn more, run the command again with --verbose. diff --git a/src/hangups/mod.rs b/src/hangups/mod.rs @@ -1,44 +1,148 @@ #[path = "proto/hangouts.rs"] mod hangouts; pub mod hangups { - use hangups::hangouts::{MessageContent, Segment, SegmentType, RequestHeader, EventRequestHeader, - SendChatMessageRequest, DeliveryMediumType, OffTheRecordStatus, - EventType, SendChatMessageResponse, DeliveryMedium, ConversationId}; + use hangups::hangouts::{MessageContent, Segment, SegmentType, RequestHeader, + EventRequestHeader, SendChatMessageRequest, DeliveryMediumType, + OffTheRecordStatus, EventType, SendChatMessageResponse, + DeliveryMedium, ConversationId}; use protobuf::RepeatedField; use rand::random; - use hyper::header::Headers; use hyper::header::Cookie; - use hyper::client::Client; + use hyper::client::Request; + use hyper::{Method, Uri}; + use std::io::{Read, Write}; + use hyper::error::Error; + use protobuf::Message; + use std::collections::HashMap; + use std::time::*; + use sha1::Sha1; + use rustc_serialize::hex::ToHex; + use rustc_serialize::base64::FromBase64; + use protobuf::core::parse_from_bytes; + use auth::get_auth; + use helper::get_configs; + use std::str::FromStr; + use std; + header! { (ContentType, "content-type") => [String] } header! { (XGoogEncodeResponseIfExecutable, "X-Goog-Encode-Response-If-Executable") => [String] } - fn get_auth_headers() {} + fn get_cookies() -> Result<HashMap<String, String>, Error> { + let config = get_configs().unwrap(); + if (config.get_raw("SAPISID") == None) || (config.get_raw("HSID") == None) || (config.get_raw("SSID") == None) || (config.get_raw("APISID") == None) || (config.get_raw("SID") == None) { + get_auth(String::from(""), String::from("")); + } + + let sapisid_cookie = String::from(config.get_raw("SAPISID").unwrap().as_string().unwrap()); + let hsid_cookie = String::from(config.get_raw("HSID").unwrap().as_string().unwrap()); + let ssid_cookie = String::from(config.get_raw("SSID").unwrap().as_string().unwrap()); + let apisid_cookie = String::from(config.get_raw("APISID").unwrap().as_string().unwrap()); + let sid_cookie = String::from(config.get_raw("SID").unwrap().as_string().unwrap()); + + let mut cookies = HashMap::new(); + cookies.insert(String::from("SAPISID"), sapisid_cookie); + cookies.insert(String::from("HSID"), hsid_cookie); + cookies.insert(String::from("SSID"), ssid_cookie); + cookies.insert(String::from("APISID"), apisid_cookie); + cookies.insert(String::from("SID"), sid_cookie); + Ok(cookies) + } + + fn get_auth_headers(sapisid: String) -> Result<HashMap<String, String>, Error> { + let origin_url = String::from("https://talkgadget.google.com"); + let now = SystemTime::now(); + let dur = now.duration_since(UNIX_EPOCH).unwrap(); + let timestamp_msec = dur.as_secs(); + + + let auth_string = String::from(format!("{} {} {}", timestamp_msec, sapisid, origin_url)); + let mut hash = Sha1::new(); + hash.update(auth_string.as_bytes()); + let hash_bytes = hash.digest().bytes(); + let hex_sha1 = hash_bytes.to_hex(); + let sapisid_hash = format!("SAPISIDHASH {}_{}", timestamp_msec, hex_sha1); + let mut auth_cookies = HashMap::new(); + auth_cookies.insert(String::from("authorization"), sapisid_hash); + auth_cookies.insert(String::from("x-origin"), origin_url); + auth_cookies.insert(String::from("x-goog-authuser"), String::from("0")); + Ok(auth_cookies) + } fn api_request(endpoint_url: String, content_type: String, - cookies: String, - payload: SendChatMessageRequest) { - let mut headers = Headers::new(); - headers.set(Cookie(vec![String::from(cookies.to_owned())])); - headers.set(ContentType(String::from(content_type.to_owned()))); - headers.set(XGoogEncodeResponseIfExecutable(String::from("base64".to_owned()))); - let client = Client::new(); - let post_request = client.post(&*endpoint_url).headers(headers); + response_type: String, + payload: std::vec::Vec<u8>) + -> Result<String, Error> { + use hyper::Client; + use tokio_core::reactor::Core; + use std::str::FromStr; + use futures::{Future, Stream}; + + let mut core = Core::new().unwrap(); + let handle = core.handle(); + let client = Client::configure() + .keep_alive(true) + .build(&handle); + let url = format!("{}?key=AIzaSyAfFJCeph-euFSwtmqFZi0kaKk-cZ5wufM&alt={}", + endpoint_url, + response_type).parse::<Uri>().unwrap();; + let auth_header = get_auth_headers(String::from("PLACEHOLDER")) + .unwrap(); + let mut fresh_request = Request::new(Method::Post, url); + let cookies = get_cookies().unwrap(); + let mut cookie_vec = Vec::new(); + for (key, val) in cookies { + cookie_vec.push(format!("{}={}", key, val)) + } + fresh_request.headers_mut().set(Cookie(cookie_vec)); + fresh_request.headers_mut().set(ContentType(String::from(content_type.to_owned()))); + fresh_request.headers_mut().set(XGoogEncodeResponseIfExecutable(String::from("base64".to_owned()))); + fresh_request.set_body(payload); + for (key, val) in auth_header { + fresh_request.headers_mut().append_raw(key, val.as_bytes().to_vec()); + } + let mut body = String::new(); + + let mut post = { + let post_req = client.request(fresh_request).map_err(|_| ()).and_then(|res| { + res.body().for_each(|chunk| { + let body_chunk = String::from_utf8(chunk.to_vec()).unwrap(); + body.push_str(body_chunk.as_str()); + Ok(()) + }); + Ok(()) + }) + .map(|_| { + println!("\n\nDone."); + }); + post_req + }; + + core.run(post).unwrap(); + + Ok(body) } fn protobuf_api_request(api_endpoint: String, - request_struct: SendChatMessageRequest, - response_struct: SendChatMessageResponse) { + request_struct: SendChatMessageRequest) + -> Result<SendChatMessageResponse, Error> { let url = format!("https://clients6.google.com/chat/v1/{}", api_endpoint); + let payload = request_struct.write_to_bytes().unwrap(); let output = api_request(url, String::from("application/x-protobuf"), String::from("proto"), - request_struct); + payload); + let decoded_output = output.unwrap().as_str().from_base64().unwrap(); + let response_proto = parse_from_bytes::<SendChatMessageResponse>(&decoded_output[..]) + .unwrap(); + Ok(response_proto) } - pub fn send_message(message: String, conv_id_raw: String) { + pub fn send_message(message: String, + conv_id_raw: String) + -> Result<SendChatMessageResponse, Error> { //Prepare message let segment_type = SegmentType::SEGMENT_TYPE_TEXT; let mut segment_raw = Segment::new(); @@ -73,10 +177,9 @@ pub mod hangups { request.set_message_content(message_content); request.set_event_request_header(event_request_header); - //Prepare response - let response = SendChatMessageResponse::new(); - - let err = protobuf_api_request(String::from("conversations/sendchatmessage"), request, response); + let output = protobuf_api_request(String::from("conversations/sendchatmessage"), request) + .unwrap(); + Ok(output) } } @@ -86,7 +189,7 @@ mod tests { fn test_send_message() { use super::hangups::send_message; - let test_message = String::from("TEST!"); + let test_message = String::from("Diese Nachricht sendet der Bot!"); let test_conv = String::from("Ugxu9JRlbNPSqk5Ye1V4AaABAQ"); send_message(test_message, test_conv); } diff --git a/src/lib.rs b/src/lib.rs @@ -1,31 +1,204 @@ -#![allow(unused_variables)] #[macro_use] extern crate hyper; -extern crate hyper_native_tls; -extern crate yup_oauth2 as oauth2; extern crate protobuf; extern crate rand; +extern crate sha1; +extern crate rustc_serialize; +extern crate jconfig; +extern crate regex; +extern crate url; +extern crate tokio_core; +extern crate futures; pub mod auth { - pub fn get_auth(client_email: String) { + use hyper::{Client, Body, Uri}; + use hyper::client::{Response, HttpConnector}; + use tokio_core::reactor::Core; + use hyper::header::Headers; + fn do_auth_step(mut core: &mut Core, client: &Client<HttpConnector, Body>, url: Uri, resp: &Response, resp_body: &str, email: String, password: String, mut resp_headers_raw: &mut Headers) { + use hyper::header::{Headers, SetCookie, UserAgent}; + use hyper::client::Request; + use hyper::{Method, Uri}; + use regex::Regex; + use std::str; + use url::form_urlencoded; + use std::str::FromStr; + use futures::Future; + use futures::Stream; + + header! { (ContentType, "content-type") => [String] } + header! { (GAPS, "GAPS") => [String] } + header! { (GALX, "GALX") => [String] } + + let mut req_headers = Headers::new(); + if resp_headers_raw.has::<SetCookie>() { + let resp_headers = resp_headers_raw.get_raw("Set-Cookie").unwrap(); + let mut resp_headers_string = String::new(); + for elem in resp_headers.iter() { + let s = match str::from_utf8(elem) { + Ok(v) => v, + Err(e) => panic!("Invalid UTF-8 sequence: {}", e), + }; + resp_headers_string.push_str(s); + } + let resp_cookie_gaps_re = Regex::new("(?:GAPS=)(.*?)(?:;)").unwrap(); + let resp_cookie_gaps_captures = resp_cookie_gaps_re.captures(resp_headers_string.as_str()).unwrap(); + let resp_cookie_gaps = resp_cookie_gaps_captures.get(1).map_or("", |m| m.as_str()); + + let resp_cookie_galx_re = Regex::new("(?:GALX=)(.*?)(?:;)").unwrap(); + let resp_cookie_galx_captures = resp_cookie_galx_re.captures(resp_headers_string.as_str()).unwrap(); + let resp_cookie_galx = resp_cookie_galx_captures.get(1).map_or("", |m| m.as_str()); + + if resp_cookie_gaps != "" { + req_headers.set(GAPS(resp_cookie_gaps.to_owned())); + } + if resp_cookie_galx != "" { + req_headers.set(GALX(resp_cookie_galx.to_owned())); + } + } + + let resp_page_re = Regex::new("(?:<input.*name=\"Page\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_page_captures = resp_page_re.captures(resp_body).unwrap(); + let resp_page = resp_page_captures.get(1).map_or("", |m| m.as_str()); + + let resp_galx_re = Regex::new("(?:<input.*name=\"GALX\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_galx_captures = resp_galx_re.captures(resp_body).unwrap(); + let resp_galx = resp_galx_captures.get(1).map_or("", |m| m.as_str()); + + let resp_gxf_re = Regex::new("(?:<input.*name=\"gxf\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_gxf_captures = resp_gxf_re.captures(resp_body).unwrap(); + let resp_gxf = resp_gxf_captures.get(1).map_or("", |m| m.as_str()); + + let resp_continue_re = Regex::new("(?:<input.*name=\"continue\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_continue_captures = resp_continue_re.captures(resp_body).unwrap(); + let resp_gcontinue = resp_continue_captures.get(1).map_or("", |m| m.as_str()); + + let resp_ltmpl_re = Regex::new("(?:<input.*name=\"ltmpl\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_ltmpl_captures = resp_ltmpl_re.captures(resp_body).unwrap(); + let resp_ltmpl = resp_ltmpl_captures.get(1).map_or("", |m| m.as_str()); + + let resp_scc_re = Regex::new("(?:<input.*name=\"scc\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_scc_captures = resp_scc_re.captures(resp_body).unwrap(); + let resp_scc = resp_scc_captures.get(1).map_or("", |m| m.as_str()); + + let resp_sarp_re = Regex::new("(?:<input.*name=\"sarp\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_sarp_captures = resp_sarp_re.captures(resp_body).unwrap(); + let resp_sarp = resp_sarp_captures.get(1).map_or("", |m| m.as_str()); + + let resp_oauth_re = Regex::new("(?:<input.*name=\"oresp\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_oauth_captures = resp_oauth_re.captures(resp_body).unwrap(); + let resp_oauth = resp_oauth_captures.get(1).map_or("", |m| m.as_str()); + + let resp_profile_information_re = Regex::new("(?:<input.*name=\"ProfileInformation\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_profile_information_captures = resp_profile_information_re.captures(resp_body).unwrap(); + let resp_profile_information = resp_profile_information_captures.get(1).map_or("", |m| m.as_str()); + + let resp_session_state_re = Regex::new("(?:<input.*name=\"SessionState\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_session_state_captures = resp_session_state_re.captures(resp_body).unwrap(); + let resp_session_state = resp_session_state_captures.get(1).map_or("", |m| m.as_str()); + + let resp__utf8_re = Regex::new("(?:<input.*name=\"_utf8\".*)(?:value=\"([^\"]*))").unwrap(); + let resp__utf8_captures = resp__utf8_re.captures(resp_body).unwrap(); + let resp__utf8 = resp__utf8_captures.get(1).map_or("", |m| m.as_str()); + + let resp_bgresponse_re = Regex::new("(?:<input.*name=\"bgresponse\".*)(?:value=\"([^\"]*))").unwrap(); + let resp_bgresponse_captures = resp_bgresponse_re.captures(resp_body).unwrap(); + let resp_bgresponse = resp_bgresponse_captures.get(1).map_or("", |m| m.as_str()); + + let body_send: String = form_urlencoded::Serializer::new(String::new()) + .append_pair("Page", resp_page) + .append_pair("GALX", resp_galx) + .append_pair("gxf", resp_gxf) + .append_pair("continue", resp_gcontinue.replace("&amp;", "&").as_str()) + .append_pair("ltmpl", resp_ltmpl) + .append_pair("scc", resp_scc) + .append_pair("sarp", resp_sarp) + .append_pair("oauth", resp_oauth) + .append_pair("ProfileInformation", resp_profile_information) + .append_pair("SessionState", resp_session_state) + .append_pair("_utf8", resp__utf8) + .append_pair("bgresponse", resp_bgresponse) + .append_pair("Email", email.as_str()) + .append_pair("Passwd", password.as_str()) + .append_pair("pstMsg", "0") + .append_pair("checkConnection", "") + .append_pair("checkedDomains", "youtube") + .append_pair("signIn", "Anmelden") + .append_pair("PersistentCookie", "no") + .finish(); + + // Do Email Login + let mut body = String::new(); + + let mut fresh_request = Request::new(Method::Post, url); + fresh_request.headers_mut().set(ContentType(String::from("application/x-www-form-urlencoded".to_owned()))); + fresh_request.headers_mut().set(UserAgent::new("Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36".to_owned())); + fresh_request.set_body(body_send); + let mut post = client.request(fresh_request).map_err(|_| ()).and_then(|res| { + println!("Response: {}", &res.status()); + println!("Headers: \n{}", &res.headers()); + + &res.body().for_each(|chunk| { + let body_chunk = String::from_utf8(chunk.to_vec()).unwrap(); + body.push_str(&body_chunk.as_str()); + Ok(()) + }); + Ok(()) + }) + .map(|_| { + println!("\n\nDone."); + }); + &core.run(post).unwrap(); + // let mut email_post = client.post(url) + // .body(email_body_send.as_str()) + // .headers(req_headers) + // .unwrap(); + + // email_post.read_to_string(&mut email_body) + // .unwrap(); + } + + fn write_cookie_config() { + // let mut config = get_configs().unwrap(); + // + // config.set::<SAPISID>().unwrap(); + // config.set::<HSID>().unwrap(); + // config.set::<SSID>().unwrap(); + // config.set::<SAPISID>().unwrap(); + // config.set::<SAPISID>().unwrap(); + // config.set_raw("SAPISID", ) + // config.set_raw("HSID", ) + // config.set_raw("SSID", ) + // config.set_raw("APISID", ) + // config.set_raw("SID", ) + } + + pub fn get_auth(mut client_email: String, mut client_passwd: String) { use hyper::Client; - use hyper::net::HttpsConnector; - use hyper_native_tls::NativeTlsClient; - use oauth2::{Authenticator, DefaultAuthenticatorDelegate, MemoryStorage, GetToken, - ApplicationSecret, FlowType}; - use std::io::{stdin, stdout, Write}; - - let ssl = NativeTlsClient::new().unwrap(); - let connector = HttpsConnector::new(ssl); - let client = Client::with_connector(connector); - - let client_id = String::from("936475272427.apps.googleusercontent.com"); - let client_secret = String::from("KWsJlkaMn1jGLxQpWxMnOox-"); - let token_uri = String::from("https://accounts.google.com/o/oauth2/token"); - let auth_uri = String::from("https://accounts.google.com/o/oauth2/programmatic_auth"); - let redirect_uri = String::from("urn:ietf:wg:oauth:2.0:oob"); + use hyper::header::{Headers, SetCookie, UserAgent}; + use hyper::client::Request; + use hyper::{Method, Uri}; + use std::io::{stdin, stdout, Write, Read}; + use std::str; + use url::form_urlencoded; + use regex::Regex; + use tokio_core::reactor::Core; + use std::str::FromStr; + use futures::Future; + use futures::Stream; + + let mut core = Core::new().unwrap(); + let handle = core.handle(); + let client = Client::configure() + .keep_alive(true) + .build(&handle); + + let auth_uri = "https://accounts.google.com/o/oauth2/programmatic_auth?scope=https://www.google.com/accounts/OAuthLogin+https://www.googleapis.com/auth/userinfo.email&client_id=936475272427.apps.googleusercontent.com".parse::<Uri>().unwrap(); + let email_uri = "https://accounts.google.com/signin/v1/lookup".parse::<Uri>().unwrap(); + let passwd_uri = "https://accounts.google.com/signin/challenge/sl/password".parse::<Uri>().unwrap(); + if client_email == String::from("") { - let mut client_email = String::new(); + println!(""); println!("Please enter Google Email: "); let _ = stdout().flush(); stdin() @@ -40,61 +213,101 @@ pub mod auth { } println!("You typed: {}", client_email); - let secret = ApplicationSecret { - client_id: client_id, - client_secret: client_secret, - token_uri: token_uri, - auth_uri: auth_uri, - redirect_uris: vec![redirect_uri], - project_id: None, - client_email: Some(client_email), - auth_provider_x509_cert_url: None, - client_x509_cert_url: None, - }; - let res = Authenticator::new(&secret, - DefaultAuthenticatorDelegate, - client, - <MemoryStorage as Default>::default(), - Some(FlowType::InstalledInteractive)) - .token(&["https://www.google.com/accounts/OAuthLogin", "https://www.googleapis.com/auth/userinfo.email"]); - match res { - Ok(t) => { - println!("Acquired access_token: {}", t.access_token); - println!("Acquired refresh_token: {}", t.refresh_token); + if client_passwd == String::from("") { + println!("Please enter Google Password: "); + let _ = stdout().flush(); + stdin() + .read_line(&mut client_passwd) + .expect("Did not enter a correct Password"); + if let Some('\n') = client_passwd.chars().next_back() { + client_passwd.pop(); + } + if let Some('\r') = client_passwd.chars().next_back() { + client_passwd.pop(); } - Err(err) => println!("Failed to acquire token: {}", err), } + println!("You typed: {}", client_passwd); + + // Login Step1 + let req = { + let req_raw = client.get(auth_uri).and_then(|res| { + println!("Response: {}", &res.status()); + let mut headers = { + &res.headers() + }; + let mut body = String::new(); + + &res.body().for_each(|chunk| { + let body_chunk = String::from_utf8(chunk.to_vec()).unwrap(); + body.push_str(body_chunk.as_str()); + Ok(()) + }); + { + let core_raw = core; + do_auth_step(&mut core, &client, email_uri, &res, &body.as_str(), client_email, client_email, &mut headers); + } + Ok(()) + }) + .map(|_| { + println!("\n\nDone."); + }); + req_raw + }; + core.run(req).unwrap(); } } pub mod hangups; -#[cfg(test)] -mod tests { - #[test] - fn test_get_auth_token() { - use super::auth::get_auth; - use std::io::{stdin, stdout, Write}; +pub mod helper{ + use jconfig::config::Config; + use jconfig::error::Error; + + fn check_generate_configs() { + use std::fs::{File, create_dir_all}; use std::env; + use std::io::Write; + // TODO Add handling if there is no home dir. + // match std::env::home_dir() { + // Some(dir) => { + // //There is a home directory, do something with the dir variable + // }, + // None => { + // //There is no home directory, do something else + // } + // } - match env::var_os("CI") { - Some(env) => println!("CI's currently not supported at this test"), - None => { - let mut client_email = String::new(); - println!("Please enter Google Email: "); - let _ = stdout().flush(); - stdin() - .read_line(&mut client_email) - .expect("Did not enter a correct Email"); - if let Some('\n') = client_email.chars().next_back() { - client_email.pop(); - } - if let Some('\r') = client_email.chars().next_back() { - client_email.pop(); - } - get_auth(client_email); - } - } + //Memory File + let mut memory_path = env::home_dir().unwrap(); + memory_path.push(".local/share/hangups/"); + create_dir_all(memory_path.as_path()).unwrap(); + memory_path.push("memory.json"); + let mut f = File::create(memory_path.as_path()).unwrap(); + f.write_all(b"{}").unwrap(); + f.sync_all().unwrap(); + + //Config File + let mut config_path = env::home_dir().unwrap(); + config_path.push(".local/share/hangups/"); + create_dir_all(config_path.as_path()).unwrap(); + config_path.push("config.json"); + let mut f = File::create(config_path.as_path()).unwrap(); + f.write_all(b"{}").unwrap(); + f.sync_all().unwrap(); + } + pub fn get_configs() -> Result<Config, Error>{ + use std::env; + + check_generate_configs(); + + let mut memory_path = env::home_dir().unwrap(); + memory_path.push(".local/share/hangups/"); + memory_path.push("memory.json"); + + // Load the memory + let config = Config::load(memory_path.as_path()).unwrap(); + + Ok(config) } }