1 use mysql; |
1 use mysql; |
2 use mysql::{error::DriverError, error::Error, params}; |
2 use mysql::{error::DriverError, error::Error, from_row_opt, params}; |
|
3 use openssl::sha::sha1; |
3 |
4 |
4 struct AccountInfo { |
5 use super::handlers::AccountInfo; |
5 is_registered: bool, |
6 use crate::server::handlers::Sha1Digest; |
6 is_admin: bool, |
7 |
7 is_contributor: bool, |
8 const GET_ACCOUNT_QUERY: &str = |
8 } |
9 r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END, |
|
10 (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 3), |
|
11 (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 13) |
|
12 FROM users WHERE users.name = :username"; |
|
13 |
|
14 const STORE_STATS_QUERY: &str = r"INSERT INTO gameserver_stats |
|
15 (players, rooms, last_update) |
|
16 VALUES |
|
17 (:players, :rooms, UNIX_TIMESTAMP())"; |
9 |
18 |
10 struct ServerStatistics { |
19 struct ServerStatistics { |
11 rooms: u32, |
20 rooms: u32, |
12 players: u32, |
21 players: u32, |
13 } |
22 } |
14 |
23 |
15 struct Achievements {} |
24 struct Achievements {} |
16 |
25 |
17 trait DatabaseInterface { |
26 pub struct Database { |
18 fn check_account(username: &str, password: &str) -> AccountInfo; |
|
19 fn store_stats(stats: &ServerStatistics) -> Result<(), ()>; |
|
20 fn store_achievements(achievements: &Achievements) -> Result<(), ()>; |
|
21 fn get_replay_name(replay_id: u32) -> Result<String, ()>; |
|
22 } |
|
23 |
|
24 struct Database { |
|
25 pool: Option<mysql::Pool>, |
27 pool: Option<mysql::Pool>, |
26 } |
28 } |
27 |
29 |
28 impl Database { |
30 impl Database { |
29 fn new() -> Self { |
31 pub fn new() -> Self { |
30 Self { pool: None } |
32 Self { pool: None } |
31 } |
33 } |
32 |
34 |
33 fn connect(&mut self, url: &str) -> Result<(), Error> { |
35 pub fn connect(&mut self, url: &str) -> Result<(), Error> { |
34 self.pool = Some(mysql::Pool::new(url)?); |
36 self.pool = Some(mysql::Pool::new(url)?); |
35 |
37 |
36 Ok(()) |
38 Ok(()) |
37 } |
39 } |
38 |
40 |
39 fn check_account(&mut self, username: &str, password: &str) -> AccountInfo { |
41 pub fn get_account( |
40 AccountInfo { |
42 &mut self, |
41 is_registered: false, |
43 nick: &str, |
42 is_admin: false, |
44 protocol: u16, |
43 is_contributor: false, |
45 password_hash: &str, |
|
46 client_salt: &str, |
|
47 server_salt: &str, |
|
48 ) -> Result<Option<AccountInfo>, Error> { |
|
49 if let Some(pool) = &self.pool { |
|
50 if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? { |
|
51 let (mut password, is_admin, is_contributor) = |
|
52 from_row_opt::<(String, i32, i32)>(row)?; |
|
53 let client_hash = get_hash(protocol, &password, &client_salt, &server_salt); |
|
54 let server_hash = get_hash(protocol, &password, &server_salt, &client_salt); |
|
55 password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔"); |
|
56 |
|
57 if server_hash == client_hash { |
|
58 Ok(Some(AccountInfo { |
|
59 is_registered: true, |
|
60 is_admin: is_admin == 1, |
|
61 is_contributor: is_contributor == 1, |
|
62 server_hash, |
|
63 })) |
|
64 } else { |
|
65 Ok(None) |
|
66 } |
|
67 } else { |
|
68 Ok(Some(AccountInfo { |
|
69 is_registered: false, |
|
70 is_admin: false, |
|
71 is_contributor: false, |
|
72 server_hash: Sha1Digest::new([0; 20]), |
|
73 })) |
|
74 } |
|
75 } else { |
|
76 Err(DriverError::SetupError.into()) |
44 } |
77 } |
45 } |
78 } |
46 |
79 |
47 fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> { |
80 pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> { |
48 if let Some(pool) = &self.pool { |
81 if let Some(pool) = &self.pool { |
49 for mut stmt in pool |
82 for mut stmt in pool.prepare(STORE_STATS_QUERY).into_iter() { |
50 .prepare( |
|
51 r"INSERT INTO gameserver_stats |
|
52 (players, rooms, last_update) |
|
53 VALUES |
|
54 (:players, :rooms, UNIX_TIMESTAMP())", |
|
55 ) |
|
56 .into_iter() |
|
57 { |
|
58 stmt.execute(params! { |
83 stmt.execute(params! { |
59 "players" => stats.players, |
84 "players" => stats.players, |
60 "rooms" => stats.rooms, |
85 "rooms" => stats.rooms, |
61 })?; |
86 })?; |
62 } |
87 } |