rust/hedgewars-server/src/server/database.rs
changeset 14779 f43ab2bd76ae
parent 14457 98ef2913ec73
child 14785 a1077e8d26f4
--- a/rust/hedgewars-server/src/server/database.rs	Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/database.rs	Tue Apr 09 21:08:35 2019 +0300
@@ -1,11 +1,20 @@
 use mysql;
-use mysql::{error::DriverError, error::Error, params};
+use mysql::{error::DriverError, error::Error, from_row_opt, params};
+use openssl::sha::sha1;
+
+use super::handlers::AccountInfo;
+use crate::server::handlers::Sha1Digest;
 
-struct AccountInfo {
-    is_registered: bool,
-    is_admin: bool,
-    is_contributor: bool,
-}
+const GET_ACCOUNT_QUERY: &str =
+    r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
+     (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 3),
+     (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 13)
+     FROM users WHERE users.name = :username";
+
+const STORE_STATS_QUERY: &str = r"INSERT INTO gameserver_stats
+            (players, rooms, last_update)
+            VALUES
+            (:players, :rooms, UNIX_TIMESTAMP())";
 
 struct ServerStatistics {
     rooms: u32,
@@ -14,47 +23,63 @@
 
 struct Achievements {}
 
-trait DatabaseInterface {
-    fn check_account(username: &str, password: &str) -> AccountInfo;
-    fn store_stats(stats: &ServerStatistics) -> Result<(), ()>;
-    fn store_achievements(achievements: &Achievements) -> Result<(), ()>;
-    fn get_replay_name(replay_id: u32) -> Result<String, ()>;
-}
-
-struct Database {
+pub struct Database {
     pool: Option<mysql::Pool>,
 }
 
 impl Database {
-    fn new() -> Self {
+    pub fn new() -> Self {
         Self { pool: None }
     }
 
-    fn connect(&mut self, url: &str) -> Result<(), Error> {
+    pub fn connect(&mut self, url: &str) -> Result<(), Error> {
         self.pool = Some(mysql::Pool::new(url)?);
 
         Ok(())
     }
 
-    fn check_account(&mut self, username: &str, password: &str) -> AccountInfo {
-        AccountInfo {
-            is_registered: false,
-            is_admin: false,
-            is_contributor: false,
+    pub fn get_account(
+        &mut self,
+        nick: &str,
+        protocol: u16,
+        password_hash: &str,
+        client_salt: &str,
+        server_salt: &str,
+    ) -> Result<Option<AccountInfo>, Error> {
+        if let Some(pool) = &self.pool {
+            if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
+                let (mut password, is_admin, is_contributor) =
+                    from_row_opt::<(String, i32, i32)>(row)?;
+                let client_hash = get_hash(protocol, &password, &client_salt, &server_salt);
+                let server_hash = get_hash(protocol, &password, &server_salt, &client_salt);
+                password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
+
+                if server_hash == client_hash {
+                    Ok(Some(AccountInfo {
+                        is_registered: true,
+                        is_admin: is_admin == 1,
+                        is_contributor: is_contributor == 1,
+                        server_hash,
+                    }))
+                } else {
+                    Ok(None)
+                }
+            } else {
+                Ok(Some(AccountInfo {
+                    is_registered: false,
+                    is_admin: false,
+                    is_contributor: false,
+                    server_hash: Sha1Digest::new([0; 20]),
+                }))
+            }
+        } else {
+            Err(DriverError::SetupError.into())
         }
     }
 
-    fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
+    pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
         if let Some(pool) = &self.pool {
-            for mut stmt in pool
-                .prepare(
-                    r"INSERT INTO gameserver_stats
-            (players, rooms, last_update)
-            VALUES
-            (:players, :rooms, UNIX_TIMESTAMP())",
-                )
-                .into_iter()
-            {
+            for mut stmt in pool.prepare(STORE_STATS_QUERY).into_iter() {
                 stmt.execute(params! {
                     "players" => stats.players,
                     "rooms" => stats.rooms,
@@ -66,11 +91,19 @@
         }
     }
 
-    fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
+    pub fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
         Ok(())
     }
 
-    fn get_replay_name(&mut self, replay_id: u32) -> Result<String, ()> {
+    pub fn get_replay_name(&mut self, replay_id: u32) -> Result<String, ()> {
         Err(())
     }
 }
+
+fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
+    let s = format!(
+        "{}{}{}{}{}",
+        salt1, salt2, web_password, protocol_number, "!hedgewars"
+    );
+    Sha1Digest::new(sha1(s.as_bytes()))
+}