From 0d42d59ed9abbdde4b630a1a7fd008ff23b44f1a Mon Sep 17 00:00:00 2001 From: Thinkofname Date: Sun, 20 Mar 2016 23:43:31 +0000 Subject: [PATCH] Fully implement the login screen (Closes #6) --- protocol/src/protocol/mod.rs | 19 ++++++++ protocol/src/protocol/mojang.rs | 86 ++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 2 deletions(-) diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 6549828..0fcf3a1 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -16,6 +16,7 @@ use openssl; use serde_json; +use hyper; pub mod mojang; @@ -633,6 +634,8 @@ pub enum Error { Err(String), Disconnect(format::Component), IOError(io::Error), + Json(serde_json::Error), + Hyper(hyper::Error), } impl convert::From for Error { @@ -641,12 +644,26 @@ impl convert::From for Error { } } +impl convert::From for Error { + fn from(e: serde_json::Error) -> Error { + Error::Json(e) + } +} + +impl convert::From for Error { + fn from(e: hyper::Error) -> Error { + Error::Hyper(e) + } +} + impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { Error::Err(ref val) => &val[..], Error::Disconnect(_) => "Disconnect", Error::IOError(ref e) => e.description(), + Error::Json(ref e) => e.description(), + Error::Hyper(ref e) => e.description(), } } } @@ -657,6 +674,8 @@ impl ::std::fmt::Display for Error { Error::Err(ref val) => write!(f, "protocol error: {}", val), Error::Disconnect(ref val) => write!(f, "{}", val), Error::IOError(ref e) => e.fmt(f), + Error::Json(ref e) => e.fmt(f), + Error::Hyper(ref e) => e.fmt(f), } } } diff --git a/protocol/src/protocol/mojang.rs b/protocol/src/protocol/mojang.rs index e8b7efe..d3aeded 100644 --- a/protocol/src/protocol/mojang.rs +++ b/protocol/src/protocol/mojang.rs @@ -14,8 +14,10 @@ use openssl; use serde_json; +use serde_json::builder::ObjectBuilder; use hyper; +#[derive(Clone, Debug)] pub struct Profile { pub username: String, pub id: String, @@ -23,9 +25,85 @@ pub struct Profile { } const JOIN_URL: &'static str = "https://sessionserver.mojang.com/session/minecraft/join"; +const LOGIN_URL: &'static str = "https://authserver.mojang.com/authenticate"; +const REFRESH_URL: &'static str = "https://authserver.mojang.com/refresh"; +const VALIDATE_URL: &'static str = "https://authserver.mojang.com/validate"; impl Profile { - pub fn join_server(&self, server_id: &String, shared_key: &Vec, public_key: &Vec) { + pub fn login(username: &str, password: &str, token: &str) -> Result { + let req_msg = ObjectBuilder::new() + .insert("username", username) + .insert("password", password) + .insert("clientToken", token) + .insert_object("agent", |b| b + .insert("name", "Minecraft") + .insert("version", 1) + ) + .unwrap(); + let req = try!(serde_json::to_string(&req_msg)); + + let client = hyper::Client::new(); + let res = try!(client.post(LOGIN_URL) + .body(&req) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send()); + + let ret: serde_json::Value = try!(serde_json::from_reader(res)); + if let Some(error) = ret.find("error").and_then(|v| v.as_string()) { + return Err(super::Error::Err(format!( + "{}: {}", + error, + ret.find("errorMessage").and_then(|v| v.as_string()).unwrap()) + )); + } + Ok(Profile { + username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(), + id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(), + access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(), + }) + } + + pub fn refresh(self, token: &str) -> Result { + let req_msg = ObjectBuilder::new() + .insert("accessToken", self.access_token.clone()) + .insert("clientToken", token) + .unwrap(); + let req = try!(serde_json::to_string(&req_msg)); + + let client = hyper::Client::new(); + let res = try!(client.post(VALIDATE_URL) + .body(&req) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send()); + + if res.status != hyper::status::StatusCode::NoContent { + println!("Rerefeshing"); + // Refresh needed + let res = try!(client.post(REFRESH_URL) + .body(&req) + .header(hyper::header::ContentType("application/json".parse().unwrap())) + .send()); + + let ret: serde_json::Value = try!(serde_json::from_reader(res)); + if let Some(error) = ret.find("error").and_then(|v| v.as_string()) { + return Err(super::Error::Err(format!( + "{}: {}", + error, + ret.find("errorMessage").and_then(|v| v.as_string()).unwrap()) + )); + } + return Ok(Profile { + username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(), + id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(), + access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(), + }); + } + println!("Reuse"); + Ok(self) + } + + // TODO + pub fn join_server(&self, server_id: &str, shared_key: &Vec, public_key: &Vec) { let mut sha1 = openssl::SHA1::new(); sha1.update(server_id.as_bytes()); sha1.update(&shared_key[..]); @@ -46,7 +124,7 @@ impl Profile { hash_val.to_owned() }; - let join_msg = serde_json::builder::ObjectBuilder::new() + let join_msg = ObjectBuilder::new() .insert("accessToken", &self.access_token) .insert("selectedProfile", &self.id) .insert("serverId", hash_str) @@ -66,6 +144,10 @@ impl Profile { }; panic!("{:?}", ret); } + + pub fn is_complete(&self) -> bool { + !self.username.is_empty() && !self.id.is_empty() && !self.access_token.is_empty() + } } fn twos_compliment(data: &mut Vec) {