The Battle for Wesnoth  1.17.0-dev
forum_user_handler.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2021
3  by Thomas Baumhauer <thomas.baumhauer@NOSPAMgmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #ifdef HAVE_MYSQLPP
17 
20 #include "hash.hpp"
21 #include "log.hpp"
22 #include "config.hpp"
23 
24 #include <boost/asio/post.hpp>
25 
26 #include <cstdlib>
27 #include <sstream>
28 
29 static lg::log_domain log_mp_user_handler("mp_user_handler");
30 #define ERR_UH LOG_STREAM(err, log_mp_user_handler)
31 #define WRN_UH LOG_STREAM(warn, log_mp_user_handler)
32 #define LOG_UH LOG_STREAM(info, log_mp_user_handler)
33 #define DBG_UH LOG_STREAM(debug, log_mp_user_handler)
34 
35 namespace {
36  const int USER_INACTIVE = 1;
37  const int USER_IGNORE = 2;
38 }
39 
40 fuh::fuh(const config& c)
41  : conn_(c)
42  , db_users_table_(c["db_users_table"].str())
43  , db_extra_table_(c["db_extra_table"].str())
44  , mp_mod_group_(0)
45 {
46  try {
47  mp_mod_group_ = std::stoi(c["mp_mod_group"].str());
48  } catch(...) {
49  ERR_UH << "Failed to convert the mp_mod_group value of '" << c["mp_mod_group"].str() << "' into an int! Defaulting to " << mp_mod_group_ << "." << std::endl;
50  }
51 }
52 
53 bool fuh::login(const std::string& name, const std::string& password) {
54  // Retrieve users' password as hash
55  try {
56  std::string hash = get_hashed_password_from_db(name);
57 
58  if(utils::md5::is_valid_hash(hash) || utils::bcrypt::is_valid_prefix(hash)) { // md5 hash
59  return password == hash;
60  } else {
61  ERR_UH << "Invalid hash for user '" << name << "'" << std::endl;
62  return false;
63  }
64  } catch (const error& e) {
65  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
66  return false;
67  }
68 }
69 
70 std::string fuh::extract_salt(const std::string& name) {
71 
72  // Some double security, this should never be needed
73  if(!(user_exists(name))) {
74  return "";
75  }
76 
77  std::string hash;
78 
79  try {
80  hash = get_hashed_password_from_db(name);
81  } catch (const error& e) {
82  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
83  return "";
84  }
85 
87  return hash.substr(0,12);
88 
90  try {
92  } catch(const utils::hash_error& err) {
93  ERR_UH << "Error getting salt from hash of user '" << name << "': " << err.what() << std::endl;
94  return "";
95  }
96  }
97 
98  return "";
99 }
100 
101 void fuh::user_logged_in(const std::string& name) {
102  conn_.write_user_int("user_lastvisit", name, static_cast<int>(std::time(nullptr)));
103 }
104 
105 bool fuh::user_exists(const std::string& name) {
106  return conn_.user_exists(name);
107 }
108 
109 long fuh::get_forum_id(const std::string& name) {
110  return conn_.get_forum_id(name);
111 }
112 
113 bool fuh::user_is_active(const std::string& name) {
114  int user_type = conn_.get_user_int(db_users_table_, "user_type", name);
115  return user_type != USER_INACTIVE && user_type != USER_IGNORE;
116 }
117 
118 bool fuh::user_is_moderator(const std::string& name) {
119  if(!user_exists(name)){
120  return false;
121  }
122  return conn_.get_user_int(db_extra_table_, "user_is_moderator", name) == 1 || (mp_mod_group_ != 0 && conn_.is_user_in_group(name, mp_mod_group_));
123 }
124 
125 void fuh::set_is_moderator(const std::string& name, const bool& is_moderator) {
126  if(!user_exists(name)){
127  return;
128  }
129  conn_.write_user_int("user_is_moderator", name, is_moderator);
130 }
131 
132 fuh::ban_info fuh::user_is_banned(const std::string& name, const std::string& addr)
133 {
134  ban_check b = conn_.get_ban_info(name, addr);
135  switch(b.get_ban_type())
136  {
137  case BAN_NONE:
138  return {};
139  case BAN_IP:
140  LOG_UH << "User '" << name << "' ip " << addr << " banned by IP address\n";
141  return { BAN_IP, b.get_ban_duration() };
142  case BAN_USER:
143  LOG_UH << "User '" << name << "' uid " << b.get_user_id() << " banned by uid\n";
144  return { BAN_USER, b.get_ban_duration() };
145  case BAN_EMAIL:
146  LOG_UH << "User '" << name << "' email " << b.get_email() << " banned by email address\n";
147  return { BAN_EMAIL, b.get_ban_duration() };
148  default:
149  ERR_UH << "Invalid ban type '" << b.get_ban_type() << "' returned for user '" << name << "'\n";
150  return {};
151  }
152 }
153 
154 std::string fuh::user_info(const std::string& name) {
155  if(!user_exists(name)) {
156  throw error("No user with the name '" + name + "' exists.");
157  }
158 
159  std::time_t reg_date = get_registrationdate(name);
160  std::time_t ll_date = get_lastlogin(name);
161 
162  std::string reg_string = ctime(&reg_date);
163  std::string ll_string;
164 
165  if(ll_date) {
166  ll_string = ctime(&ll_date);
167  } else {
168  ll_string = "Never\n";
169  }
170 
171  std::stringstream info;
172  info << "Name: " << name << "\n"
173  << "Registered: " << reg_string
174  << "Last login: " << ll_string;
175  if(!user_is_active(name)) {
176  info << "This account is currently inactive.\n";
177  }
178 
179  return info.str();
180 }
181 
182 std::string fuh::get_hashed_password_from_db(const std::string& user) {
183  return conn_.get_user_string(db_users_table_, "user_password", user);
184 }
185 
186 std::time_t fuh::get_lastlogin(const std::string& user) {
187  return std::time_t(conn_.get_user_int(db_extra_table_, "user_lastvisit", user));
188 }
189 
190 std::time_t fuh::get_registrationdate(const std::string& user) {
191  return std::time_t(conn_.get_user_int(db_users_table_, "user_regdate", user));
192 }
193 
194 std::string fuh::get_uuid(){
195  return conn_.get_uuid();
196 }
197 
198 std::string fuh::get_tournaments(){
199  return conn_.get_tournaments();
200 }
201 
202 void fuh::async_get_and_send_game_history(boost::asio::io_service& io_service, wesnothd::server& s, wesnothd::player_iterator player, int player_id, int offset) {
203  boost::asio::post([this, &s, player, player_id, offset, &io_service] {
204  boost::asio::post(io_service, [player, &s, doc = conn_.get_game_history(player_id, offset)]{
205  s.send_to_player(player, *doc);
206  });
207  });
208 }
209 
210 void fuh::db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name, int reload, int observers, int is_public, int has_password){
211  conn_.insert_game_info(uuid, game_id, version, name, reload, observers, is_public, has_password);
212 }
213 
214 void fuh::db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location){
215  conn_.update_game_end(uuid, game_id, replay_location);
216 }
217 
218 void fuh::db_insert_game_player_info(const std::string& uuid, int game_id, const std::string& username, int side_number, int is_host, const std::string& faction, const std::string& version, const std::string& source, const std::string& current_user){
219  conn_.insert_game_player_info(uuid, game_id, username, side_number, is_host, faction, version, source, current_user);
220 }
221 
222 void fuh::db_insert_game_content_info(const std::string& uuid, int game_id, const std::string& type, const std::string& name, const std::string& id, const std::string& source, const std::string& version){
223  conn_.insert_game_content_info(uuid, game_id, type, name, id, source, version);
224 }
225 
226 void fuh::db_set_oos_flag(const std::string& uuid, int game_id){
227  conn_.set_oos_flag(uuid, game_id);
228 }
229 
230 void fuh::async_test_query(boost::asio::io_service& io_service, int limit) {
231  boost::asio::post([this, limit, &io_service] {
232  ERR_UH << "async test query starts!" << std::endl;
233  int i = conn_.async_test_query(limit);
234  boost::asio::post(io_service, [i]{ ERR_UH << "async test query output: " << i << std::endl; });
235  });
236 }
237 
238 bool fuh::db_topic_id_exists(int topic_id) {
239  return conn_.topic_id_exists(topic_id);
240 }
241 
242 void fuh::db_insert_addon_info(const std::string& instance_version, const std::string& id, const std::string& name, const std::string& type, const std::string& version, bool forum_auth, int topic_id) {
243  conn_.insert_addon_info(instance_version, id, name, type, version, forum_auth, topic_id);
244 }
245 
246 unsigned long long fuh::db_insert_login(const std::string& username, const std::string& ip, const std::string& version) {
247  return conn_.insert_login(username, ip, version);
248 }
249 
250 void fuh::db_update_logout(unsigned long long login_id) {
251  conn_.update_logout(login_id);
252 }
253 
254 void fuh::get_users_for_ip(const std::string& ip, std::ostringstream* out) {
255  conn_.get_users_for_ip(ip, out);
256 }
257 
258 void fuh::get_ips_for_user(const std::string& username, std::ostringstream* out) {
259  conn_.get_ips_for_user(username, out);
260 }
261 
262 #endif //HAVE_MYSQLPP
int get_ban_duration()
unsigned long long db_insert_login(const std::string &username, const std::string &ip, const std::string &version)
Inserts into the database for when a player logs in.
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:40
logger & info()
Definition: log.cpp:89
std::string get_uuid()
static bool is_valid_prefix(const std::string &hash)
Definition: hash.cpp:188
void user_logged_in(const std::string &name)
Sets the last login time to the current time.
std::time_t get_lastlogin(const std::string &user)
std::string user_info(const std::string &name)
bool user_exists(const std::string &name)
std::string get_email()
player_connections::const_iterator player_iterator
void set_is_moderator(const std::string &name, const bool &is_moderator)
Sets or unsets whether the player should be considered a moderator in the extra table.
fuh(const config &c)
Reads wesnothd&#39;s config for the data needed to initialize this class and dbconn.
void async_get_and_send_game_history(boost::asio::io_service &io_service, wesnothd::server &s, wesnothd::player_iterator player, int player_id, int offset)
Runs an asynchronous query to fetch the user&#39;s game history data.
#define b
long get_forum_id(const std::string &name)
void db_insert_addon_info(const std::string &instance_version, const std::string &id, const std::string &name, const std::string &type, const std::string &version, bool forum_auth, int topic_id)
Inserts information about an uploaded add-on into the database.
static bool is_valid_hash(const std::string &hash)
Definition: hash.cpp:97
ban_info user_is_banned(const std::string &name, const std::string &addr)
bool db_topic_id_exists(int topic_id)
Checks whether a forum thread with topic_id exists.
const char * what() const noexcept
Definition: exceptions.hpp:36
std::string get_tournaments()
void db_set_oos_flag(const std::string &uuid, int game_id)
Sets the OOS flag in the database if wesnothd is told by a client it has detected an OOS error...
void send_to_player(player_iterator player, simple_wml::document &data)
Definition: server.hpp:72
std::size_t i
Definition: function.cpp:967
logger & err()
Definition: log.cpp:77
long get_ban_type()
bool user_exists(const std::string &name)
bool user_is_active(const std::string &name)
static map_location::DIRECTION s
Ban status description.
std::string password(const std::string &server, const std::string &login)
static bcrypt from_hash_string(const std::string &input)
Definition: hash.cpp:169
std::time_t get_registrationdate(const std::string &user)
bool user_is_moderator(const std::string &name)
void db_update_logout(unsigned long long login_id)
Updates the database for when a player logs out.
void db_insert_game_info(const std::string &uuid, int game_id, const std::string &version, const std::string &name, int reload, int observers, int is_public, int has_password)
Inserts game related information.
std::string db_users_table_
The name of the table that contains forum user information.
Definition: dbconn.hpp:188
int get_user_id()
std::string get_hashed_password_from_db(const std::string &user)
void db_insert_game_content_info(const std::string &uuid, int game_id, const std::string &type, const std::string &name, const std::string &id, const std::string &source, const std::string &version)
Inserts information about the content being played.
void get_users_for_ip(const std::string &ip, std::ostringstream *out)
Searches for all players that logged in using the ip address.
#define e
std::string get_salt() const
Definition: hash.cpp:195
int side_number
Definition: game_info.hpp:40
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
std::string extract_salt(const std::string &name)
Needed because the hashing algorithm used by phpbb requires some info from the original hash to recre...
mock_char c
void db_update_game_end(const std::string &uuid, int game_id, const std::string &replay_location)
Update the game related information when the game ends.
bool login(const std::string &name, const std::string &password)
Retrieves the player&#39;s hashed password from the phpbb forum database and checks if it matches the has...
void async_test_query(boost::asio::io_service &io_service, int limit)
A simple test query for running a query asynchronously.
void get_ips_for_user(const std::string &username, std::ostringstream *out)
Searches for all ip addresses used by the player.
void db_insert_game_player_info(const std::string &uuid, int game_id, const std::string &username, int side_number, int is_host, const std::string &faction, const std::string &version, const std::string &source, const std::string &current_user)
Inserts player information per side.
std::string db_extra_table_
The name of the table that contains additional user information.
Definition: dbconn.hpp:192