Fully implement the login screen (Closes #6)

This commit is contained in:
Thinkofname 2016-03-20 23:43:31 +00:00
parent 3d6f5ba904
commit 0d42d59ed9
2 changed files with 103 additions and 2 deletions

View File

@ -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<io::Error> for Error {
@ -641,12 +644,26 @@ impl convert::From<io::Error> for Error {
}
}
impl convert::From<serde_json::Error> for Error {
fn from(e: serde_json::Error) -> Error {
Error::Json(e)
}
}
impl convert::From<hyper::Error> 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),
}
}
}

View File

@ -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<u8>, public_key: &Vec<u8>) {
pub fn login(username: &str, password: &str, token: &str) -> Result<Profile, super::Error> {
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<Profile, super::Error> {
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<u8>, public_key: &Vec<u8>) {
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<u8>) {