update to mysql_async
authoralfadur
Thu, 03 Feb 2022 01:48:31 +0300
changeset 15833 3511bacbd763
parent 15832 a4d505a32879
child 15835 f0f615dcbe7c
update to mysql_async
rust/hedgewars-server/Cargo.toml
rust/hedgewars-server/src/server/database.rs
--- a/rust/hedgewars-server/Cargo.toml	Tue Feb 01 20:58:35 2022 +0300
+++ b/rust/hedgewars-server/Cargo.toml	Thu Feb 03 01:48:31 2022 +0300
@@ -5,7 +5,7 @@
 authors = [ "Andrey Korotaev <a.korotaev@hedgewars.org>" ]
 
 [features]
-official-server = []
+official-server = ["mysql_async", "sha1"]
 default = []
 
 [dependencies]
@@ -16,11 +16,13 @@
 env_logger = "0.8"
 getopts = "0.2"
 log = "0.4"
+mysql_async = { version = "0.29.0", optional = true }
 nom = "7.1"
 rand = "0.8"
 serde = "1.0"
 serde_yaml = "0.8"
 serde_derive = "1.0"
+sha1 = { version = "0.10.0", optional = true }
 slab = "0.4"
 tokio = { version = "1.16", features = ["full"]}
 
--- a/rust/hedgewars-server/src/server/database.rs	Tue Feb 01 20:58:35 2022 +0300
+++ b/rust/hedgewars-server/src/server/database.rs	Thu Feb 03 01:48:31 2022 +0300
@@ -1,6 +1,5 @@
-use mysql;
-use mysql::{error::DriverError, error::Error, from_row_opt, params};
-use openssl::sha::sha1;
+use mysql_async::{self, from_row_opt, params, prelude::*, Pool};
+use sha1::{Digest, Sha1};
 
 use crate::handlers::{AccountInfo, Sha1Digest};
 
@@ -27,120 +26,106 @@
 pub struct Achievements {}
 
 pub struct Database {
-    pool: Option<mysql::Pool>,
+    pool: Pool,
 }
 
 impl Database {
-    pub fn new() -> Self {
-        Self { pool: None }
-    }
-
-    pub fn connect(&mut self, url: &str) -> Result<(), Error> {
-        self.pool = Some(mysql::Pool::new(url)?);
-
-        Ok(())
-    }
-
-    pub fn is_registered(&mut self, nick: &str) -> Result<bool, Error> {
-        if let Some(pool) = &self.pool {
-            let is_registered = pool
-                .first_exec(CHECK_ACCOUNT_EXISTS_QUERY, params! { "username" => nick })?
-                .is_some();
-            Ok(is_registered)
-        } else {
-            Err(DriverError::SetupError.into())
+    pub fn new(url: &str) -> Self {
+        Self {
+            pool: Pool::new(url),
         }
     }
 
-    pub fn get_account(
+    pub async fn get_is_registered(&mut self, nick: &str) -> mysql_async::Result<bool> {
+        let mut connection = self.pool.get_conn().await?;
+        let result = CHECK_ACCOUNT_EXISTS_QUERY
+            .with(params! { "username" => nick })
+            .first(&mut connection)
+            .await?;
+        Ok(!result.is_empty())
+    }
+
+    pub async 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(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
+    ) -> mysql_async::Result<Option<AccountInfo>> {
+        let mut connection = self.pool.get_conn().await?;
+        if let Some((mut password, is_admin, is_contributor)) = GET_ACCOUNT_QUERY
+            .with(params! { "username" => nick })
+            .first::<(String, i32, i32), _>(&mut connection)
+            .await?
+        {
+            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 client_hash == password_hash {
-                    Ok(Some(AccountInfo {
-                        is_registered: true,
-                        is_admin: is_admin == 1,
-                        is_contributor: is_contributor == 1,
-                        server_hash,
-                    }))
-                } else {
-                    Ok(None)
-                }
+            if client_hash == password_hash {
+                Ok(Some(AccountInfo {
+                    is_registered: true,
+                    is_admin: is_admin == 1,
+                    is_contributor: is_contributor == 1,
+                    server_hash,
+                }))
             } else {
                 Ok(None)
             }
         } else {
-            Err(DriverError::SetupError.into())
+            Ok(None)
         }
     }
 
-    pub fn get_checker_account(
+    pub async fn get_checker_account(
         &mut self,
         nick: &str,
         checker_password: &str,
-    ) -> Result<bool, Error> {
-        if let Some(pool) = &self.pool {
-            if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
-                let (mut password, _, _) = from_row_opt::<(String, i32, i32)>(row)?;
-                Ok(checker_password == password)
-            } else {
-                Ok(false)
-            }
+    ) -> mysql_async::Result<bool> {
+        let mut connection = self.pool.get_conn().await?;
+        if let Some((password, _, _)) = GET_ACCOUNT_QUERY
+            .with(params! { "username" => nick })
+            .first::<(String, i32, i32), _>(&mut connection)
+            .await?
+        {
+            Ok(checker_password == password)
         } else {
-            Err(DriverError::SetupError.into())
+            Ok(false)
         }
     }
 
-    pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
-        if let Some(pool) = &self.pool {
-            for mut stmt in pool.prepare(STORE_STATS_QUERY) {
-                stmt.execute(params! {
-                    "players" => stats.players,
-                    "rooms" => stats.rooms,
-                })?;
-            }
-            Ok(())
-        } else {
-            Err(DriverError::SetupError.into())
-        }
+    pub async fn store_stats(&mut self, stats: &ServerStatistics) -> mysql_async::Result<()> {
+        let mut connection = self.pool.get_conn().await?;
+        STORE_STATS_QUERY
+            .with(params! {
+                "players" => stats.players,
+                "rooms" => stats.rooms,
+            })
+            .ignore(&mut connection)
+            .await
     }
 
-    pub fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
+    pub async fn store_achievements(&mut self, achievements: &Achievements) -> mysql_async::Result<()> {
         Ok(())
     }
 
-    pub fn get_replay_name(&mut self, replay_id: u32) -> Result<Option<String>, Error> {
-        if let Some(pool) = &self.pool {
-            if let Some(row) =
-                pool.first_exec(GET_REPLAY_NAME_QUERY, params! { "id" => replay_id })?
-            {
-                let filename = from_row_opt::<String>(row)?;
-                Ok(Some(filename))
-            } else {
-                Ok(None)
-            }
-        } else {
-            Err(DriverError::SetupError.into())
-        }
+    pub async fn get_replay_name(&mut self, replay_id: u32) -> mysql_async::Result<Option<String>> {
+        let mut connection = self.pool.get_conn().await?;
+        GET_REPLAY_NAME_QUERY
+            .with(params! { "id" => replay_id })
+            .first::<String, _>(&mut connection)
+            .await
     }
 }
 
 fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
-    let s = format!(
+    let data = format!(
         "{}{}{}{}{}",
         salt1, salt2, web_password, protocol_number, "!hedgewars"
     );
-    Sha1Digest::new(sha1(s.as_bytes()))
+
+    let mut sha1 = Sha1::new();
+    sha1.update(&data);
+    Sha1Digest::new(sha1.finalize().try_into().unwrap())
 }