The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
forum_user_handler.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008 - 2016 by Thomas Baumhauer <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #ifdef HAVE_MYSQLPP
16 
17 #include "forum_user_handler.hpp"
18 #include "hash.hpp"
19 #include "log.hpp"
20 #include "config.hpp"
21 
22 #include <stdlib.h>
23 #include <sstream>
24 
25 static lg::log_domain log_mp_user_handler("mp_user_handler");
26 #define ERR_UH LOG_STREAM(err, log_mp_user_handler)
27 #define WRN_UH LOG_STREAM(warn, log_mp_user_handler)
28 #define LOG_UH LOG_STREAM(info, log_mp_user_handler)
29 #define DBG_UH LOG_STREAM(debug, log_mp_user_handler)
30 
31 namespace {
32  const int USER_INACTIVE = 1;
33  const int USER_IGNORE = 2;
34 }
35 
36 fuh::fuh(const config& c)
37  : db_name_(c["db_name"].str())
38  , db_host_(c["db_host"].str())
39  , db_user_(c["db_user"].str())
40  , db_password_(c["db_password"].str())
41  , db_users_table_(c["db_users_table"].str())
42  , db_extra_table_(c["db_extra_table"].str())
43  , conn(mysql_init(nullptr))
44 {
45  if(!conn || !mysql_real_connect(conn, db_host_.c_str(), db_user_.c_str(), db_password_.c_str(), db_name_.c_str(), 0, nullptr, 0)) {
46  ERR_UH << "Could not connect to database: " << mysql_errno(conn) << ": " << mysql_error(conn) << std::endl;
47  }
48 }
49 
50 fuh::~fuh() {
51  mysql_close(conn);
52 }
53 
54 void fuh::add_user(const std::string& /*name*/, const std::string& /*mail*/, const std::string& /*password*/) {
55  throw error("For now please register at http://forum.wesnoth.org");
56 }
57 
58 void fuh::remove_user(const std::string& /*name*/) {
59  throw error("'Dropping your nickname is currently impossible");
60 }
61 
62 // The hashing code is basically taken from forum_auth.cpp
63 bool fuh::login(const std::string& name, const std::string& password, const std::string& seed) {
64 
65  // Retrieve users' password as hash
66 
67  std::string hash;
68 
69  try {
70  hash = get_hash(name);
71  } catch (error& e) {
72  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
73  return false;
74  }
75 
76  // Check hash prefix, if different than $H$ hash is invalid
77  if(!util::is_valid_hash(hash)) {
78  ERR_UH << "Invalid hash for user '" << name << "'" << std::endl;
79  return false;
80  }
81 
82  std::string valid_hash = util::create_hash(hash.substr(12,34), seed);
83 
84  if(password == valid_hash) return true;
85 
86  return false;
87 }
88 
90 
91  // Some double security, this should never be needed
92  if(!(user_exists(name))) {
93  return "";
94  }
95 
96  std::string hash;
97 
98  try {
99  hash = get_hash(name);
100  } catch (error& e) {
101  ERR_UH << "Could not retrieve hash for user '" << name << "' :" << e.message << std::endl;
102  return "";
103  }
104 
105  if(!util::is_valid_hash(hash)) return "";
106 
107  return hash.substr(0,12);
108 }
109 
110 void fuh::user_logged_in(const std::string& name) {
111  set_lastlogin(name, time(nullptr));
112 }
113 
114 bool fuh::user_exists(const std::string& name) {
115 
116  // Make a test query for this username
117  try {
118  mysql_result res = db_query("SELECT username FROM " + db_users_table_ + " WHERE UPPER(username)=UPPER('" + name + "')");
119  return mysql_fetch_row(res.get());
120  } catch (error& e) {
121  ERR_UH << "Could not execute test query for user '" << name << "' :" << e.message << std::endl;
122  // If the database is down just let all usernames log in
123  return false;
124  }
125 }
126 
127 bool fuh::user_is_active(const std::string& name) {
128  try {
129  int user_type = std::stoi(get_detail_for_user(name, "user_type"));
130  return user_type != USER_INACTIVE && user_type != USER_IGNORE;
131  } catch (error& e) {
132  ERR_UH << "Could not retrieve user type for user '" << name << "' :" << e.message << std::endl;
133  return false;
134  }
135 }
136 
137 bool fuh::user_is_moderator(const std::string& name) {
138 
139  if(!user_exists(name)) return false;
140 
141  try {
142  return get_writable_detail_for_user(name, "user_is_moderator") == "1";
143  } catch (error& e) {
144  ERR_UH << "Could not query user_is_moderator for user '" << name << "' :" << e.message << std::endl;
145  // If the database is down mark nobody as a mod
146  return false;
147  }
148 }
149 
150 void fuh::set_is_moderator(const std::string& name, const bool& is_moderator) {
151 
152  if(!user_exists(name)) return;
153 
154  try {
155  write_detail(name, "user_is_moderator", is_moderator ? "1" : "0");
156  } catch (error& e) {
157  ERR_UH << "Could not set is_moderator for user '" << name << "' :" << e.message << std::endl;
158  }
159 }
160 
161 void fuh::password_reminder(const std::string& /*name*/) {
162  throw error("For now please use the password recovery "
163  "function provided at http://forum.wesnoth.org");
164 }
165 
167  if(!user_exists(name)) {
168  throw error("No user with the name '" + name + "' exists.");
169  }
170 
171  time_t reg_date = get_registrationdate(name);
172  time_t ll_date = get_lastlogin(name);
173 
174  std::string reg_string = ctime(&reg_date);
175  std::string ll_string;
176 
177  if(ll_date) {
178  ll_string = ctime(&ll_date);
179  } else {
180  ll_string = "Never\n";
181  }
182 
183  std::stringstream info;
184  info << "Name: " << name << "\n"
185  << "Registered: " << reg_string
186  << "Last login: " << ll_string;
187  if(!user_is_active(name)) {
188  info << "This account is currently inactive.\n";
189  }
190 
191  return info.str();
192 }
193 
194 void fuh::set_user_detail(const std::string& /*user*/, const std::string& /*detail*/, const std::string& /*value*/) {
195  throw error("For now this is a 'read-only' user_handler");
196 }
197 
199  return "For now this is a 'read-only' user_handler";
200 }
201 
203  try {
204  return get_detail_for_user(user, "user_password");
205  } catch (error& e) {
206  ERR_UH << "Could not retrieve password for user '" << user << "' :" << e.message << std::endl;
207  return "";
208  }
209 }
210 
212  try {
213  return get_detail_for_user(user, "user_email");
214  } catch (error& e) {
215  ERR_UH << "Could not retrieve email for user '" << user << "' :" << e.message << std::endl;
216  return "";
217  }
218 }
219 
220 time_t fuh::get_lastlogin(const std::string& user) {
221  try {
222  int time_int = std::stoi(get_writable_detail_for_user(user, "user_lastvisit"));
223  return time_t(time_int);
224  } catch (error& e) {
225  ERR_UH << "Could not retrieve last visit for user '" << user << "' :" << e.message << std::endl;
226  return time_t(0);
227  }
228 }
229 
230 time_t fuh::get_registrationdate(const std::string& user) {
231  try {
232  int time_int = std::stoi(get_detail_for_user(user, "user_regdate"));
233  return time_t(time_int);
234  } catch (error& e) {
235  ERR_UH << "Could not retrieve registration date for user '" << user << "' :" << e.message << std::endl;
236  return time_t(0);
237  }
238 }
239 
240 void fuh::set_lastlogin(const std::string& user, const time_t& lastlogin) {
241 
242  std::stringstream ss;
243  ss << lastlogin;
244 
245  try {
246  write_detail(user, "user_lastvisit", ss.str());
247  } catch (error& e) {
248  ERR_UH << "Could not set last visit for user '" << user << "' :" << e.message << std::endl;
249  }
250 }
251 
253  if(mysql_query(conn, sql.c_str())) {
254  WRN_UH << "not connected to database, reconnecting..." << std::endl;
255  //Try to reconnect and execute query again
256  if(!mysql_real_connect(conn, db_host_.c_str(), db_user_.c_str(), db_password_.c_str(), db_name_.c_str(), 0, nullptr, 0)
257  || mysql_query(conn, sql.c_str())) {
258  ERR_UH << "Could not connect to database: " << mysql_errno(conn) << ": " << mysql_error(conn) << std::endl;
259  throw error("Error querying database.");
260  }
261  }
262  return mysql_result(mysql_store_result(conn), mysql_free_result);
263 }
264 
266  mysql_result res = db_query(sql);
267  return std::string(mysql_fetch_row(res.get())[0]);
268 }
269 
270 
272  return db_query_to_string("SELECT " + detail + " FROM " + db_users_table_ + " WHERE UPPER(username)=UPPER('" + name + "')");
273 }
274 
276  if(!extra_row_exists(name)) return "";
277  return db_query_to_string("SELECT " + detail + " FROM " + db_extra_table_ + " WHERE UPPER(username)=UPPER('" + name + "')");
278 }
279 
280 void fuh::write_detail(const std::string& name, const std::string& detail, const std::string& value) {
281  try {
282  // Check if we do already have a row for this user in the extra table
283  if(!extra_row_exists(name)) {
284  // If not create the row
285  db_query("INSERT INTO " + db_extra_table_ + " VALUES('" + name + "','" + value + "','0')");
286  }
287  db_query("UPDATE " + db_extra_table_ + " SET " + detail + "='" + value + "' WHERE UPPER(username)=UPPER('" + name + "')");
288  } catch (error& e) {
289  ERR_UH << "Could not set detail for user '" << name << "': " << e.message << std::endl;
290  }
291 }
292 
293 bool fuh::extra_row_exists(const std::string& name) {
294 
295  // Make a test query for this username
296  try {
297  mysql_result res = db_query("SELECT username FROM " + db_extra_table_ + " WHERE UPPER(username)=UPPER('" + name + "')");
298  return mysql_fetch_row(res.get());
299  } catch (error& e) {
300  ERR_UH << "Could not execute test query for user '" << name << "' :" << e.message << std::endl;
301  return false;
302  }
303 }
304 
305 #endif //HAVE_MYSQLPP
std::string get_hash(const std::string &user)
std::string db_query_to_string(const std::string &query)
void password_reminder(const std::string &name)
Send a password reminder email to the given user.
void set_user_detail(const std::string &user, const std::string &detail, const std::string &value)
Set data for a given user name.
std::string create_pepper(const std::string &name)
Needed because the hashing algorithm used by phpbb requires some info from the original hash to recre...
const GLfloat * c
Definition: glew.h:12741
std::unique_ptr< MYSQL_RES, decltype(&mysql_free_result)> mysql_result
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
logger & info()
Definition: log.cpp:91
void user_logged_in(const std::string &name)
Executed when the user with the given name logged in.
void set_lastlogin(const std::string &user, const time_t &lastlogin)
void add_user(const std::string &name, const std::string &mail, const std::string &password)
Adds a user.
std::string user_info(const std::string &name)
Returns a string containing info like the last login of this user.
bool user_exists(const std::string &name)
Returns true if a user with the given name exists.
void set_is_moderator(const std::string &name, const bool &is_moderator)
Mark this user as a moderator.
fuh(const config &c)
mysql_result db_query(const std::string &query)
std::string create_hash(const std::string &password, const std::string &salt, int iteration_count)
Definition: hash.cpp:74
bool extra_row_exists(const std::string &name)
std::string get_writable_detail_for_user(const std::string &name, const std::string &detail)
GLsizei const GLfloat * value
Definition: glew.h:1817
time_t get_lastlogin(const std::string &user)
bool is_valid_hash(const std::string &hash)
Definition: hash.cpp:41
bool login(const std::string &name, const std::string &password, const std::string &seed)
Return true if the given password matches the password for the given user.
GLuint res
Definition: glew.h:9258
bool user_is_active(const std::string &name)
Returns true if the specified user account is usable for logins.
time_t get_registrationdate(const std::string &user)
~fuh()
bool user_is_moderator(const std::string &name)
Returns true if this user is a moderator on this server.
GLuint const GLchar * name
Definition: glew.h:1782
void write_detail(const std::string &name, const std::string &detail, const std::string &value)
#define e
std::string get_mail(const std::string &user)
Used in send_mail().
void remove_user(const std::string &name)
Removes a user.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
std::string get_valid_details()
List of details that can be set for this user_handler.
GLsizei const GLcharARB ** string
Definition: glew.h:4503
std::string get_detail_for_user(const std::string &name, const std::string &detail)