25 } |
24 } |
26 |
25 |
27 pub struct Achievements {} |
26 pub struct Achievements {} |
28 |
27 |
29 pub struct Database { |
28 pub struct Database { |
30 pool: Option<mysql::Pool>, |
29 pool: Pool, |
31 } |
30 } |
32 |
31 |
33 impl Database { |
32 impl Database { |
34 pub fn new() -> Self { |
33 pub fn new(url: &str) -> Self { |
35 Self { pool: None } |
34 Self { |
36 } |
35 pool: Pool::new(url), |
37 |
|
38 pub fn connect(&mut self, url: &str) -> Result<(), Error> { |
|
39 self.pool = Some(mysql::Pool::new(url)?); |
|
40 |
|
41 Ok(()) |
|
42 } |
|
43 |
|
44 pub fn is_registered(&mut self, nick: &str) -> Result<bool, Error> { |
|
45 if let Some(pool) = &self.pool { |
|
46 let is_registered = pool |
|
47 .first_exec(CHECK_ACCOUNT_EXISTS_QUERY, params! { "username" => nick })? |
|
48 .is_some(); |
|
49 Ok(is_registered) |
|
50 } else { |
|
51 Err(DriverError::SetupError.into()) |
|
52 } |
36 } |
53 } |
37 } |
54 |
38 |
55 pub fn get_account( |
39 pub async fn get_is_registered(&mut self, nick: &str) -> mysql_async::Result<bool> { |
|
40 let mut connection = self.pool.get_conn().await?; |
|
41 let result = CHECK_ACCOUNT_EXISTS_QUERY |
|
42 .with(params! { "username" => nick }) |
|
43 .first(&mut connection) |
|
44 .await?; |
|
45 Ok(!result.is_empty()) |
|
46 } |
|
47 |
|
48 pub async fn get_account( |
56 &mut self, |
49 &mut self, |
57 nick: &str, |
50 nick: &str, |
58 protocol: u16, |
51 protocol: u16, |
59 password_hash: &str, |
52 password_hash: &str, |
60 client_salt: &str, |
53 client_salt: &str, |
61 server_salt: &str, |
54 server_salt: &str, |
62 ) -> Result<Option<AccountInfo>, Error> { |
55 ) -> mysql_async::Result<Option<AccountInfo>> { |
63 if let Some(pool) = &self.pool { |
56 let mut connection = self.pool.get_conn().await?; |
64 if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? { |
57 if let Some((mut password, is_admin, is_contributor)) = GET_ACCOUNT_QUERY |
65 let (mut password, is_admin, is_contributor) = |
58 .with(params! { "username" => nick }) |
66 from_row_opt::<(String, i32, i32)>(row)?; |
59 .first::<(String, i32, i32), _>(&mut connection) |
67 let client_hash = get_hash(protocol, &password, &client_salt, &server_salt); |
60 .await? |
68 let server_hash = get_hash(protocol, &password, &server_salt, &client_salt); |
61 { |
69 password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔"); |
62 let client_hash = get_hash(protocol, &password, &client_salt, &server_salt); |
|
63 let server_hash = get_hash(protocol, &password, &server_salt, &client_salt); |
|
64 password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔"); |
70 |
65 |
71 if client_hash == password_hash { |
66 if client_hash == password_hash { |
72 Ok(Some(AccountInfo { |
67 Ok(Some(AccountInfo { |
73 is_registered: true, |
68 is_registered: true, |
74 is_admin: is_admin == 1, |
69 is_admin: is_admin == 1, |
75 is_contributor: is_contributor == 1, |
70 is_contributor: is_contributor == 1, |
76 server_hash, |
71 server_hash, |
77 })) |
72 })) |
78 } else { |
|
79 Ok(None) |
|
80 } |
|
81 } else { |
73 } else { |
82 Ok(None) |
74 Ok(None) |
83 } |
75 } |
84 } else { |
76 } else { |
85 Err(DriverError::SetupError.into()) |
77 Ok(None) |
86 } |
78 } |
87 } |
79 } |
88 |
80 |
89 pub fn get_checker_account( |
81 pub async fn get_checker_account( |
90 &mut self, |
82 &mut self, |
91 nick: &str, |
83 nick: &str, |
92 checker_password: &str, |
84 checker_password: &str, |
93 ) -> Result<bool, Error> { |
85 ) -> mysql_async::Result<bool> { |
94 if let Some(pool) = &self.pool { |
86 let mut connection = self.pool.get_conn().await?; |
95 if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? { |
87 if let Some((password, _, _)) = GET_ACCOUNT_QUERY |
96 let (mut password, _, _) = from_row_opt::<(String, i32, i32)>(row)?; |
88 .with(params! { "username" => nick }) |
97 Ok(checker_password == password) |
89 .first::<(String, i32, i32), _>(&mut connection) |
98 } else { |
90 .await? |
99 Ok(false) |
91 { |
100 } |
92 Ok(checker_password == password) |
101 } else { |
93 } else { |
102 Err(DriverError::SetupError.into()) |
94 Ok(false) |
103 } |
95 } |
104 } |
96 } |
105 |
97 |
106 pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> { |
98 pub async fn store_stats(&mut self, stats: &ServerStatistics) -> mysql_async::Result<()> { |
107 if let Some(pool) = &self.pool { |
99 let mut connection = self.pool.get_conn().await?; |
108 for mut stmt in pool.prepare(STORE_STATS_QUERY) { |
100 STORE_STATS_QUERY |
109 stmt.execute(params! { |
101 .with(params! { |
110 "players" => stats.players, |
102 "players" => stats.players, |
111 "rooms" => stats.rooms, |
103 "rooms" => stats.rooms, |
112 })?; |
104 }) |
113 } |
105 .ignore(&mut connection) |
114 Ok(()) |
106 .await |
115 } else { |
|
116 Err(DriverError::SetupError.into()) |
|
117 } |
|
118 } |
107 } |
119 |
108 |
120 pub fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> { |
109 pub async fn store_achievements(&mut self, achievements: &Achievements) -> mysql_async::Result<()> { |
121 Ok(()) |
110 Ok(()) |
122 } |
111 } |
123 |
112 |
124 pub fn get_replay_name(&mut self, replay_id: u32) -> Result<Option<String>, Error> { |
113 pub async fn get_replay_name(&mut self, replay_id: u32) -> mysql_async::Result<Option<String>> { |
125 if let Some(pool) = &self.pool { |
114 let mut connection = self.pool.get_conn().await?; |
126 if let Some(row) = |
115 GET_REPLAY_NAME_QUERY |
127 pool.first_exec(GET_REPLAY_NAME_QUERY, params! { "id" => replay_id })? |
116 .with(params! { "id" => replay_id }) |
128 { |
117 .first::<String, _>(&mut connection) |
129 let filename = from_row_opt::<String>(row)?; |
118 .await |
130 Ok(Some(filename)) |
|
131 } else { |
|
132 Ok(None) |
|
133 } |
|
134 } else { |
|
135 Err(DriverError::SetupError.into()) |
|
136 } |
|
137 } |
119 } |
138 } |
120 } |
139 |
121 |
140 fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest { |
122 fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest { |
141 let s = format!( |
123 let data = format!( |
142 "{}{}{}{}{}", |
124 "{}{}{}{}{}", |
143 salt1, salt2, web_password, protocol_number, "!hedgewars" |
125 salt1, salt2, web_password, protocol_number, "!hedgewars" |
144 ); |
126 ); |
145 Sha1Digest::new(sha1(s.as_bytes())) |
127 |
|
128 let mut sha1 = Sha1::new(); |
|
129 sha1.update(&data); |
|
130 Sha1Digest::new(sha1.finalize().try_into().unwrap()) |
146 } |
131 } |