rust/hedgewars-server/src/server/database.rs
author Wuzzy <Wuzzy2@mail.ru>
Thu, 11 Jul 2019 16:24:09 +0200
changeset 15231 c10e9261ab9c
parent 15163 bcb98009ad39
child 15517 abd5eb807166
permissions -rw-r--r--
Make lowest line of Splash image frames transparent to work around scaling issues The Splash image is scaled. Sometimes, the lowest line is repeated on the top, which caused some weird lines to appear above big splashes (e.g. piano). This has been done fully automated with a script. Only the alpha channel was changed. The color information is preserved.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
     1
use mysql;
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
     2
use mysql::{error::DriverError, error::Error, from_row_opt, params};
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
     3
use openssl::sha::sha1;
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
     4
15075
e935b1ad23f3 normalize type names
alfadur
parents: 15074
diff changeset
     5
use crate::handlers::{AccountInfo, Sha1Digest};
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
     6
15103
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
     7
const CHECK_ACCOUNT_EXISTS_QUERY: &str =
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
     8
    r"SELECT 1 FROM users WHERE users.name = :username LIMIT 1";
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
     9
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    10
const GET_ACCOUNT_QUERY: &str =
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    11
    r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    12
     (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 3),
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    13
     (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 13)
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    14
     FROM users WHERE users.name = :username";
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    15
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    16
const STORE_STATS_QUERY: &str = r"INSERT INTO gameserver_stats
15103
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    17
      (players, rooms, last_update)
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    18
      VALUES
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    19
      (:players, :rooms, UNIX_TIMESTAMP())";
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    20
14785
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
    21
const GET_REPLAY_NAME_QUERY: &str = r"SELECT filename FROM achievements WHERE id = :id";
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
    22
15121
1a43b570cbe4 Fix build errors in certain configurations
unc0rr
parents: 15110
diff changeset
    23
pub struct ServerStatistics {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    24
    rooms: u32,
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    25
    players: u32,
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    26
}
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    27
15121
1a43b570cbe4 Fix build errors in certain configurations
unc0rr
parents: 15110
diff changeset
    28
pub struct Achievements {}
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    29
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    30
pub struct Database {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    31
    pool: Option<mysql::Pool>,
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    32
}
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    33
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    34
impl Database {
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    35
    pub fn new() -> Self {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    36
        Self { pool: None }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    37
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    38
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    39
    pub fn connect(&mut self, url: &str) -> Result<(), Error> {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    40
        self.pool = Some(mysql::Pool::new(url)?);
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    41
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    42
        Ok(())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    43
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    44
15103
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    45
    pub fn is_registered(&mut self, nick: &str) -> Result<bool, Error> {
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    46
        if let Some(pool) = &self.pool {
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    47
            let is_registered = pool
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    48
                .first_exec(CHECK_ACCOUNT_EXISTS_QUERY, params! { "username" => nick })?
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    49
                .is_some();
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    50
            Ok(is_registered)
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    51
        } else {
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    52
            Err(DriverError::SetupError.into())
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    53
        }
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    54
    }
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
    55
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    56
    pub fn get_account(
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    57
        &mut self,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    58
        nick: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    59
        protocol: u16,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    60
        password_hash: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    61
        client_salt: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    62
        server_salt: &str,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    63
    ) -> Result<Option<AccountInfo>, Error> {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    64
        if let Some(pool) = &self.pool {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    65
            if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    66
                let (mut password, is_admin, is_contributor) =
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    67
                    from_row_opt::<(String, i32, i32)>(row)?;
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    68
                let client_hash = get_hash(protocol, &password, &client_salt, &server_salt);
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    69
                let server_hash = get_hash(protocol, &password, &server_salt, &client_salt);
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    70
                password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    71
15163
bcb98009ad39 avoid allocation in hash comparison
alfadur
parents: 15121
diff changeset
    72
                if client_hash == password_hash {
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    73
                    Ok(Some(AccountInfo {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    74
                        is_registered: true,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    75
                        is_admin: is_admin == 1,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    76
                        is_contributor: is_contributor == 1,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    77
                        server_hash,
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    78
                    }))
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    79
                } else {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    80
                    Ok(None)
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    81
                }
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    82
            } else {
15110
6a8c294f49c9 fix hash comparison fix
alfadur
parents: 15109
diff changeset
    83
                Ok(None)
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    84
            }
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    85
        } else {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    86
            Err(DriverError::SetupError.into())
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    87
        }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    88
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    89
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    90
    pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    91
        if let Some(pool) = &self.pool {
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
    92
            for mut stmt in pool.prepare(STORE_STATS_QUERY).into_iter() {
14457
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
    93
                stmt.execute(params! {
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
    94
                    "players" => stats.players,
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
    95
                    "rooms" => stats.rooms,
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
    96
                })?;
98ef2913ec73 Apply rustfmt to all files
unc0rr
parents: 14456
diff changeset
    97
            }
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    98
            Ok(())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
    99
        } else {
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   100
            Err(DriverError::SetupError.into())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   101
        }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   102
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   103
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   104
    pub fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   105
        Ok(())
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   106
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   107
14785
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   108
    pub fn get_replay_name(&mut self, replay_id: u32) -> Result<Option<String>, Error> {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   109
        if let Some(pool) = &self.pool {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   110
            if let Some(row) =
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   111
                pool.first_exec(GET_REPLAY_NAME_QUERY, params! { "id" => replay_id })?
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   112
            {
15103
823052e66611 check for account existence before asking passwords
alfadur
parents: 15075
diff changeset
   113
                let filename = from_row_opt::<(String)>(row)?;
14785
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   114
                Ok(Some(filename))
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   115
            } else {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   116
                Ok(None)
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   117
            }
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   118
        } else {
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   119
            Err(DriverError::SetupError.into())
a1077e8d26f4 implement watch message apart from replay deserializing
alfadur
parents: 14779
diff changeset
   120
        }
14456
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   121
    }
a077aac9df01 Start database interaction implementation
unc0rr
parents:
diff changeset
   122
}
14779
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   123
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   124
fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   125
    let s = format!(
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   126
        "{}{}{}{}{}",
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   127
        salt1, salt2, web_password, protocol_number, "!hedgewars"
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   128
    );
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   129
    Sha1Digest::new(sha1(s.as_bytes()))
f43ab2bd76ae add a thread for internal server IO and implement account checking with it
alfadur
parents: 14457
diff changeset
   130
}