Also store where the client came from.

This commit is contained in:
pentarctagon 2019-10-12 01:28:23 -05:00 committed by Pentarctagon
parent c04ecaa8fb
commit 21f9b19120
10 changed files with 71 additions and 31 deletions

1
data/dist Normal file
View file

@ -0,0 +1 @@
Default

View file

@ -44,6 +44,8 @@
#include "utils/functional.hpp"
#include <fstream>
static lg::log_domain log_mp("mp/main");
#define DBG_MP LOG_STREAM(debug, log_mp)
#define ERR_MP LOG_STREAM(err, log_mp)
@ -147,6 +149,23 @@ std::pair<wesnothd_connection_ptr, config> open_connection(std::string host)
config cfg;
config res;
cfg["version"] = game_config::wesnoth_version.str();
// TODO: retest with below string values - dist doesn't exist
std::string info;
std::ifstream infofile("./data/dist");
if(infofile.is_open()){
infofile >> info;
infofile.close();
if(info == "Default" || info == "Steam" || info == "SourceForge" || info == "Flatpak"
|| info == "macOS App Store" || info == "Linux repository" || info == "iOS" || info == "Android") {
cfg["client_source"] = info;
} else {
cfg["client_source"] = "dist file has wrong value!";
}
} else {
cfg["client_source"] = "failed to read dist file!";
}
res.add_child("version", std::move(cfg));
sock->send_data(res);
}

View file

@ -445,10 +445,10 @@ void fuh::db_update_game_end(const std::string& uuid, int game_id, const std::st
}
}
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){
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){
try {
prepared_statement<void>("INSERT INTO `" + db_game_player_info_table_ + "`(INSTANCE_UUID, GAME_ID, USER_ID, SIDE_NUMBER, IS_HOST, FACTION, CLIENT_VERSION) VALUES(?, ?, IFNULL((SELECT user_id FROM `"+db_users_table_+"` WHERE username = ?), -1), ?, ?, ?, ?)",
uuid, game_id, username, side_number, is_host, faction, version);
prepared_statement<void>("INSERT INTO `" + db_game_player_info_table_ + "`(INSTANCE_UUID, GAME_ID, USER_ID, SIDE_NUMBER, IS_HOST, FACTION, CLIENT_VERSION, CLIENT_SOURCE) VALUES(?, ?, IFNULL((SELECT user_id FROM `"+db_users_table_+"` WHERE username = ?), -1), ?, ?, ?, ?, ?)",
uuid, game_id, username, side_number, is_host, faction, version, source);
} catch (const sql_error& e) {
ERR_UH << "Could not insert the game's player information on table `" + db_game_player_info_table_ + "`:" << e.message << std::endl;
}

View file

@ -73,7 +73,7 @@ class fuh : public user_handler {
void db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name);
void db_update_game_start(const std::string& uuid, int game_id, const std::string& map_name, const std::string& era_name, int reload);
void db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location);
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);
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);
void db_insert_modification_info(const std::string& uuid, int game_id, const std::string& modification_name);
void db_set_oos_flag(const std::string& uuid, int game_id);

View file

@ -16,11 +16,12 @@
#include "lexical_cast.hpp"
wesnothd::player::player(const std::string& n, simple_wml::node& cfg,
bool registered, const std::string& version, const std::size_t max_messages,
bool registered, const std::string& version, const std::string& source, const std::size_t max_messages,
const std::size_t time_period,
const bool moderator)
: name_(n)
, version_(version)
, source_(source)
, cfg_(cfg)
, registered_(registered)
, flood_start_(0)

View file

@ -30,7 +30,7 @@ public:
OBSERVING
};
player(const std::string& n, simple_wml::node& cfg, bool registered, const std::string& version,
player(const std::string& n, simple_wml::node& cfg, bool registered, const std::string& version, const std::string& source,
const std::size_t max_messages=4, const std::size_t time_period=10,
const bool moderator=false);
@ -46,6 +46,7 @@ public:
const std::string& name() const { return name_; }
const std::string& version() const { return version_; }
const std::string& source() const { return source_; }
const simple_wml::node* config_address() const { return &cfg_; }
bool is_message_flooding();
@ -56,6 +57,7 @@ public:
private:
const std::string name_;
std::string version_;
std::string source_;
simple_wml::node& cfg_;
bool registered_;

View file

@ -590,6 +590,9 @@ void server::read_version(socket_ptr socket, std::shared_ptr<simple_wml::documen
const simple_wml::string_span& version_str_span = (*version)["version"];
const std::string version_str(version_str_span.begin(), version_str_span.end());
const simple_wml::string_span& source_str_span = (*version)["client_source"];
const std::string source_str(source_str_span.begin(), source_str_span.end());
// Check if it is an accepted version.
auto accepted_it = std::find_if(accepted_versions_.begin(), accepted_versions_.end(),
std::bind(&utils::wildcard_string_match, version_str, _1));
@ -597,7 +600,7 @@ void server::read_version(socket_ptr socket, std::shared_ptr<simple_wml::documen
if(accepted_it != accepted_versions_.end()) {
LOG_SERVER << client_address(socket) << "\tplayer joined using accepted version " << version_str
<< ":\ttelling them to log in.\n";
async_send_doc(socket, login_response_, std::bind(&server::login, this, _1, version_str));
async_send_doc(socket, login_response_, std::bind(&server::login, this, _1, version_str, source_str));
return;
}
@ -634,23 +637,23 @@ void server::read_version(socket_ptr socket, std::shared_ptr<simple_wml::documen
}
}
void server::login(socket_ptr socket, std::string version)
void server::login(socket_ptr socket, std::string version, std::string source)
{
async_receive_doc(socket, std::bind(&server::handle_login, this, _1, _2, version));
async_receive_doc(socket, std::bind(&server::handle_login, this, _1, _2, version, source));
}
void server::handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version)
void server::handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version, std::string source)
{
if(const simple_wml::node* const login = doc->child("login")) {
if(!is_login_allowed(socket, login, version)) {
server::login(socket, version); // keep reading logins from client until we get a successful one
if(!is_login_allowed(socket, login, version, source)) {
server::login(socket, version, source); // keep reading logins from client until we get a successful one
}
} else {
async_send_error(socket, "You must login first.", MP_MUST_LOGIN);
}
}
bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version)
bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version, const std::string& source)
{
// Check if the username is valid (all alpha-numeric plus underscore and hyphen)
std::string username = (*login)["username"].to_string();
@ -687,7 +690,7 @@ bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const l
// Check for password
bool registered;
if(!authenticate(socket, username, (*login)["password"].to_string(), version, name_taken, registered))
if(!authenticate(socket, username, (*login)["password"].to_string(), version, source, name_taken, registered))
return true; // it's a failed login but we don't want to call server::login again
// because send_password_request() will handle the next network write and read instead
@ -765,13 +768,14 @@ bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const l
}
}
async_send_doc(socket, join_lobby_response_, [this, username, registered, version](socket_ptr socket) {
async_send_doc(socket, join_lobby_response_, [this, username, registered, version, source](socket_ptr socket) {
simple_wml::node& player_cfg = games_and_users_list_.root().add_child("user");
add_player(socket, wesnothd::player(
username,
player_cfg,
registered,
version,
source,
default_max_messages_,
default_time_period_,
user_handler_ && user_handler_->user_is_moderator(username)
@ -822,7 +826,7 @@ bool server::is_login_allowed(socket_ptr socket, const simple_wml::node* const l
}
bool server::authenticate(
socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, bool name_taken, bool& registered)
socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, const std::string& source, bool name_taken, bool& registered)
{
// Current login procedure for registered nicks is:
// - Client asks to log in with a particular nick
@ -852,13 +856,13 @@ bool server::authenticate(
if(password.empty()) {
if(!name_taken) {
send_password_request(socket, "The nickname '" + username + "' is registered on this server.",
username, version, MP_PASSWORD_REQUEST);
username, version, source, MP_PASSWORD_REQUEST);
} else {
send_password_request(socket,
"The nickname '" + username + "' is registered on this server."
"\n\nWARNING: There is already a client using this username, "
"logging in will cause that client to be kicked!",
username, version, MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME, true
username, version, source, MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME, true
);
}
@ -868,7 +872,7 @@ bool server::authenticate(
// A password (or hashed password) was provided, however
// there is no seed
if(seeds_[socket.get()].empty()) {
send_password_request(socket, "Please try again.", username, version, MP_NO_SEED_ERROR);
send_password_request(socket, "Please try again.", username, version, source, MP_NO_SEED_ERROR);
return false;
}
@ -909,7 +913,7 @@ bool server::authenticate(
"You have made too many failed login attempts.", MP_TOO_MANY_ATTEMPTS_ERROR);
} else {
send_password_request(socket,
"The password you provided for the nickname '" + username + "' was incorrect.", username,version,
"The password you provided for the nickname '" + username + "' was incorrect.", username,version,source,
MP_INCORRECT_PASSWORD_ERROR);
}
@ -935,6 +939,7 @@ void server::send_password_request(socket_ptr socket,
const std::string& msg,
const std::string& user,
const std::string& version,
const std::string& source,
const char* error_code,
bool force_confirmation)
{
@ -953,7 +958,7 @@ void server::send_password_request(socket_ptr socket,
"cannot log in due to an error in the hashing algorithm. "
"Logging into your forum account on https://forums.wesnoth.org "
"may fix this problem.");
login(socket, version);
login(socket, version, source);
return;
}
@ -971,7 +976,7 @@ void server::send_password_request(socket_ptr socket,
e.set_attr("error_code", error_code);
}
async_send_doc(socket, doc, std::bind(&server::login, this, _1, version));
async_send_doc(socket, doc, std::bind(&server::login, this, _1, version, source));
}
void server::add_player(socket_ptr socket, const wesnothd::player& player)
@ -1620,8 +1625,19 @@ void server::handle_player_in_game(socket_ptr socket, std::shared_ptr<simple_wml
const simple_wml::node::child_list& sides = g.get_sides_list();
for(unsigned side_index = 0; side_index < sides.size(); ++side_index) {
const simple_wml::node& side = *sides[side_index];
std::string version = player_connections_.get<name_t>().find(side["player_id"].to_string())->info().version();
user_handler_->db_insert_game_player_info(uuid_, g.id(), side["player_id"].to_string(), side["side"].to_int(), side["is_host"].to_bool(), side["faction"].to_string(), version);
const auto player = player_connections_.get<name_t>().find(side["player_id"].to_string());
std::string version;
std::string source;
// if "Nobody" is chosen for a side, for example
if(player == player_connections_.get<name_t>().end()){
version = "";
source = "";
} else {
version = player->info().version();
source = player->info().source();
}
user_handler_->db_insert_game_player_info(uuid_, g.id(), side["player_id"].to_string(), side["side"].to_int(), side["is_host"].to_bool(), side["faction"].to_string(), version, source);
}
const std::string mods = multiplayer["active_mods"].to_string();

View file

@ -42,12 +42,12 @@ private:
void handle_version(socket_ptr socket);
void read_version(socket_ptr socket, std::shared_ptr<simple_wml::document> doc);
void login(socket_ptr socket, std::string version);
void handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version);
bool is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version);
bool authenticate(socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, bool name_taken, bool& registered);
void login(socket_ptr socket, std::string version, std::string source);
void handle_login(socket_ptr socket, std::shared_ptr<simple_wml::document> doc, std::string version, std::string source);
bool is_login_allowed(socket_ptr socket, const simple_wml::node* const login, const std::string& version, const std::string& source);
bool authenticate(socket_ptr socket, const std::string& username, const std::string& password, const std::string& version, const std::string& source, bool name_taken, bool& registered);
void send_password_request(socket_ptr socket, const std::string& msg,
const std::string& user, const std::string& version, const char* error_code = "", bool force_confirmation = false);
const std::string& user, const std::string& version, const std::string& source, const char* error_code = "", bool force_confirmation = false);
bool accepting_connections() const { return !graceful_restart; }
void add_player(socket_ptr socket, const wesnothd::player&);

View file

@ -138,7 +138,7 @@ class user_handler {
virtual void db_insert_game_info(const std::string& uuid, int game_id, const std::string& version, const std::string& name) =0;
virtual void db_update_game_start(const std::string& uuid, int game_id, const std::string& map_name, const std::string& era_name, int reload) =0;
virtual void db_update_game_end(const std::string& uuid, int game_id, const std::string& replay_location) =0;
virtual 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) =0;
virtual 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) =0;
virtual void db_insert_modification_info(const std::string& uuid, int game_id, const std::string& modification_name) =0;
virtual void db_set_oos_flag(const std::string& uuid, int game_id) =0;
};

View file

@ -39,7 +39,7 @@ create table extra
(
USERNAME VARCHAR(100) NOT NULL,
USER_LASTVISIT INT(10) UNSIGNED NOT NULL DEFAULT 0,
USER_IS_MODERATOR BIT(1) NOT NULL DEFAULT 0,
USER_IS_MODERATOR TINYINT(1) NOT NULL DEFAULT 0,
PRIMARY KEY (USERNAME)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@ -88,6 +88,7 @@ create table game_player_info
IS_HOST BIT(1) NOT NULL,
FACTION VARCHAR(255) NOT NULL,
CLIENT_VERSION VARCHAR(255) NOT NULL DEFAULT '',
CLIENT_SOURCE VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (INSTANCE_UUID, GAME_ID, SIDE_NUMBER)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;