diff --git a/Cargo.toml b/Cargo.toml index 95e27e0..5bda36a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,12 @@ byteorder = "0.3" bincode = "0.4.0" docopt = "0.6" rustc-serialize = "0.3" +nickel = "*" +plugin = "*" +typemap = "*" +hyper = "*" +cookie = "0.1" +url = "*" [dependencies.server] path = "server" - -[dependencies.iron] -version = "*" diff --git a/src/webclient/main.rs b/src/webclient/main.rs index c0a0e20..b6137c9 100644 --- a/src/webclient/main.rs +++ b/src/webclient/main.rs @@ -1,88 +1,208 @@ -extern crate iron; +#[macro_use] +extern crate nickel; +extern crate plugin; +extern crate typemap; +extern crate hyper; extern crate uosql; +extern crate rustc_serialize; +extern crate cookie; +extern crate url; -use iron::prelude::*; -use iron::status; use uosql::Connection; +use std::io::Read; use uosql::Error; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::ops::DerefMut; use std::sync::{Arc, Mutex}; +use plugin::Extensible; +use hyper::header::{Cookie, SetCookie}; +use nickel::{Nickel, HttpRouter}; +use cookie::Cookie as CookiePair; +use hyper::method::Method; +use url::form_urlencoded as urlencode; + +// Dummy key for typemap +struct ConnKey; +impl typemap::Key for ConnKey { + type Value = Arc>; +} + +#[derive(Debug)] +struct Login { + user : String, + password: String +} + /// Web based client fn main() { - // Key Value pairs: User + connections - // Every user has one connection - let map: HashMap = HashMap::new(); - let ptr = Arc::new(Mutex::new(map)); + let mut server = Nickel::new(); + let map: HashMap>>= HashMap::new(); + let map = Arc::new(Mutex::new(map)); + let map2 = map.clone(); + + // Cookie managing + server.utilize(middleware! { |req, res| + + // If login data has been posted, continue + if req.origin.method == Method::Post { + return Ok(nickel::Action::Continue(res)); + } + + // Look for session string in Cookies + let sess = match req.origin.headers.get::() { + // If no Cookie found, go to Login + None => { + let m = HashMap::::new(); + return res.render("src/webclient/templates/login.tpl", &m); + } + // If there is a Cookie, eat it + // (or find the matching UosqlDB-Cookie and extract session string) + Some(cs) => { + if let Some(sess) = cs.to_cookie_jar(&[1u8]).find("UosqlDB") { + sess.value + // There is a cookie, but it is not ours :'( + // Return to Login + } else { + let m = HashMap::::new(); + return res.render("src/webclient/templates/login.tpl", &m); + } + }, + }; + + // We have a session string and look for the matching connection in + // our Session-Connection map + let guard = map.lock().unwrap(); + match guard.get(&sess) { + // No matching session: Old cookie + None => { + let mut data = HashMap::new(); + data.insert("err_msg", "Invalid Session"); + return res.render("src/webclient/templates/login.tpl", &data); + } + // There is a connection, we are logged in, we can enter the site! + Some(con) => { + req.extensions_mut().insert::(con.clone()); + return Ok(nickel::Action::Continue(res)); + } + } + }); + + // Login managing + server.post("/login", middleware! { |req, mut res| + + // Read the post data + let mut login_data = String::new(); + let read = req.origin.read_to_string(&mut login_data).unwrap(); + + // Not sufficiently filled in, return to Login with error msg + if read < 15 { + let mut data = HashMap::new(); + data.insert("err_msg", "No data given"); + return res.render("src/webclient/templates/login.tpl", &data); + } + + // Extract login data from Post string + let pairs = urlencode::parse(login_data.as_bytes()); + let username = pairs.iter().find(|e| e.0 == "user").map(|e| e.1.clone()); + let password = pairs.iter().find(|e| e.0 == "password").map(|e| e.1.clone()); + + // If eihter username or password are empty, return to Login page + if username.is_none() || password.is_none() { + let mut data = HashMap::new(); + data.insert("err_msg", "Not all required fields given"); + return res.render("src/webclient/templates/login.tpl", &data); + } + + // build Login struct + let login = Login { + user: username.unwrap(), + password: password.unwrap() + }; + + // Generate new session string + let sess_str = login.user.clone(); // Dummy - let mut chain = Chain::new(move |_: &mut Request| { - let own = ptr.clone(); - // Locked until guard dies - let mut guard = own.lock().unwrap(); + // Try connect to db server + // Insert connection and session string into hashmap + let mut guard = map2.lock().unwrap(); -//==================== TODO Read user data input somehow ==========================// - // Get login data - let username = "Peter".to_string(); + // DUMMY DATA let connection = "127.0.0.1".to_string(); let port = 4242; - let password = "Bob".to_string(); - // Get connection of user or build new connection and insert it into - // hashmap - let con = match guard.deref_mut().entry(username.clone()) { - Entry::Occupied(o) => o.into_mut(), + // create new connections + match guard.deref_mut().entry(sess_str.clone()) { + Entry::Occupied(_) => {}, Entry::Vacant(v) => { let cres = Connection::connect(connection, port, - username, password); + login.user.clone(), login.password.clone()); match cres { Err(e) => { - match e { + let errstr = match e { + // Connection error handling + // TO DO: Wait for Display/Debug Error::AddrParse(_) => { - return Ok(Response::with((iron::status::Ok, - format!("Could not connect to specified server.")))) + "Could not connect to specified server." }, Error::Io(_) => { - return Ok(Response::with((iron::status::Ok, - format!("Connection failure. Try again later.")))) + "Connection failure. Try again later." }, Error::Decode(_) => { - return Ok(Response::with((iron::status::Ok, - format!("Could not read data from server.")))) + "Could not readfsdfd data from server." }, Error::Encode(_) => { - return Ok(Response::with((iron::status::Ok, - format!("Could not send data to server.")))) + "Could not send data to server." }, - Error::UnexpectedPkg(e) => { - return Ok(Response::with((iron::status::Ok, - format!("{}", e.to_string())))) + Error::UnexpectedPkg(_) => { + "Unexpected Package." }, - Error::Auth(e) => { - return Ok(Response::with((iron::status::Ok, - format!("{}", e.to_string())))) + Error::Auth(_) => { + "Authentication failed." }, - } + }; + let mut data = HashMap::new(); + data.insert("err", errstr); + return res.render("src/webclient/templates/error.tpl", &data); } - Ok(c) => v.insert(c), + Ok(c) => { + v.insert(Arc::new(Mutex::new(c))); + }, } } }; - // Msg to print to web - let msg = format!("Connected (version {}) to {}:{}\n{}\n", - con.get_version(), con.get_ip(), - con.get_port(), con.get_message()); + // Set a Cookie with the session string as its value + // sess_str is set to a value here, so we can safely unwrap + let keks = CookiePair::new("UosqlDB".to_owned(), sess_str.clone()); + res.headers_mut().set(SetCookie(vec![keks])); - Ok(Response::with((iron::status::Ok, msg))) + // Redirect to the greeting page + *res.status_mut() = nickel::status::StatusCode::Found; + res.headers_mut().set_raw("location", vec![b"/".to_vec()]); + return res.send(""); }); - // Build webclient on localhost 3000 - Iron::new(chain).http("localhost:3000").unwrap(); -} -/* - Get data input from user via webclient and - process it + // Greeting page + server.get("/", middleware! { |req, response| + + // Look for connection + let con = req.extensions().get::().unwrap().lock().unwrap(); -*/ + // Current display with short welcome message + let version = con.get_version().to_string(); + let port = con.get_port().to_string(); + let mut data = HashMap::new(); + + data.insert("name", con.get_username()); + data.insert("version", &version); + data.insert("bind", con.get_ip()); + data.insert("port", &port); + data.insert("msg", con.get_message()); + return response.render("src/webclient/templates/hello.tpl", &data); + }); + + + server.listen("127.0.0.1:6767"); +} diff --git a/src/webclient/templates/error.tpl b/src/webclient/templates/error.tpl new file mode 100644 index 0000000..8e17557 --- /dev/null +++ b/src/webclient/templates/error.tpl @@ -0,0 +1,7 @@ + + +

+ Error: {{ err }} +

+ + diff --git a/src/webclient/templates/hello.tpl b/src/webclient/templates/hello.tpl new file mode 100644 index 0000000..cfdcbcb --- /dev/null +++ b/src/webclient/templates/hello.tpl @@ -0,0 +1,15 @@ + + +
+ +
+

+ Hello {{ name }}! +

+

+ Connected (version : {{ version }}) to {{ bind }} : {{ port }}
+ {{ msg }} +

+ + + diff --git a/src/webclient/templates/login.tpl b/src/webclient/templates/login.tpl new file mode 100644 index 0000000..2e52b3d --- /dev/null +++ b/src/webclient/templates/login.tpl @@ -0,0 +1,19 @@ + + +

+ Login +

+

+ {{ err_msg }} +

+

+

+ +
+ +
+ +
+

+ + diff --git a/src/webclient/templates/success.tpl b/src/webclient/templates/success.tpl new file mode 100644 index 0000000..2869a76 --- /dev/null +++ b/src/webclient/templates/success.tpl @@ -0,0 +1,7 @@ + + +

+ Success! +

+ +