server/server.cpp

Go to the documentation of this file.
00001 /* $Id: server.cpp 53113 2012-02-19 07:20:50Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Wesnoth-Server, for multiplayer-games.
00019  */
00020 
00021 #include "server.hpp"
00022 
00023 #include "../global.hpp"
00024 
00025 #include "../config.hpp"
00026 #include "../foreach.hpp"
00027 #include "../game_config.hpp"
00028 #include "../log.hpp"
00029 #include "../map.hpp" // gamemap::MAX_PLAYERS
00030 #include "../network.hpp"
00031 #include "../filesystem.hpp"
00032 #include "../multiplayer_error_codes.hpp"
00033 #include "../serialization/parser.hpp"
00034 #include "../serialization/preprocessor.hpp"
00035 #include "../serialization/string_utils.hpp"
00036 #include "../util.hpp"
00037 
00038 #include "game.hpp"
00039 #include "input_stream.hpp"
00040 #include "metrics.hpp"
00041 #include "player.hpp"
00042 #include "proxy.hpp"
00043 #include "simple_wml.hpp"
00044 #include "ban.hpp"
00045 
00046 
00047 #include "user_handler.hpp"
00048 #include "sample_user_handler.hpp"
00049 
00050 #ifdef HAVE_MYSQLPP
00051 #include "forum_user_handler.hpp"
00052 #endif
00053 
00054 #include <boost/bind.hpp>
00055 #include <boost/scoped_ptr.hpp>
00056 #include <boost/scoped_array.hpp>
00057 #include <boost/utility.hpp>
00058 #include <algorithm>
00059 #include <cassert>
00060 #include <cerrno>
00061 #include <cstdlib>
00062 #include <iomanip>
00063 #include <iostream>
00064 #include <map>
00065 #include <set>
00066 #include <sstream>
00067 #include <vector>
00068 #include <queue>
00069 
00070 #include <csignal>
00071 
00072 #ifndef _WIN32
00073 #include <sys/times.h>
00074 
00075 
00076 namespace {
00077 
00078 clock_t get_cpu_time(bool active) {
00079     if(!active) {
00080         return 0;
00081     }
00082     struct tms buf;
00083     times(&buf);
00084     return buf.tms_utime + buf.tms_stime;
00085 }
00086 
00087 }
00088 
00089 #else
00090 
00091 // on Windows we don't calculate CPU time
00092 clock_t get_cpu_time(bool /*active*/) {
00093     return 0;
00094 }
00095 
00096 #endif
00097 
00098 namespace {
00099 
00100 bool match_user(std::pair<network::connection, wesnothd::player> pl, const std::string& username, const std::string& ip) {
00101     return pl.second.name() == username && network::ip_address(pl.first) == ip;
00102 }
00103 }
00104 
00105 static lg::log_domain log_server("server");
00106 /**
00107  * fatal and directly server related errors/warnings,
00108  * ie not caused by erroneous client data
00109  */
00110 #define ERR_SERVER LOG_STREAM(err, log_server)
00111 
00112 /** clients send wrong/unexpected data */
00113 #define WRN_SERVER LOG_STREAM(warn, log_server)
00114 
00115 /** normal events */
00116 #define LOG_SERVER LOG_STREAM(info, log_server)
00117 #define DBG_SERVER LOG_STREAM(debug, log_server)
00118 
00119 static lg::log_domain log_config("config");
00120 #define ERR_CONFIG LOG_STREAM(err, log_config)
00121 #define WRN_CONFIG LOG_STREAM(warn, log_config)
00122 
00123 //compatibility code for MS compilers
00124 #ifndef SIGHUP
00125 #define SIGHUP 20
00126 #endif
00127 /** @todo FIXME: should define SIGINT here too, but to what? */
00128 
00129 sig_atomic_t config_reload = 0;
00130 
00131 #ifndef _MSC_VER
00132 static void reload_config(int signal) {
00133     assert(signal == SIGHUP);
00134     config_reload = 1;
00135 }
00136 #endif
00137 
00138 static void exit_sigint(int signal) {
00139     assert(signal == SIGINT);
00140     LOG_SERVER << "SIGINT caught, exiting without cleanup immediately.\n";
00141     exit(128 + SIGINT);
00142 }
00143 
00144 static void exit_sigterm(int signal) {
00145     assert(signal == SIGTERM);
00146     LOG_SERVER << "SIGTERM caught, exiting without cleanup immediately.\n";
00147     exit(128 + SIGTERM);
00148 }
00149 
00150 namespace {
00151 
00152 // we take profiling info on every n requests
00153 int request_sample_frequency = 1;
00154 
00155 void send_doc(simple_wml::document& doc, network::connection connection, std::string type = "")
00156 {
00157     if (type.empty())
00158         type = doc.root().first_child().to_string();
00159     try {
00160         simple_wml::string_span s = doc.output_compressed();
00161         network::send_raw_data(s.begin(), s.size(), connection, type);
00162     } catch (simple_wml::error& e) {
00163         WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
00164     }
00165 }
00166 
00167 void make_add_diff(const simple_wml::node& src, const char* gamelist,
00168                    const char* type,
00169                    simple_wml::document& out, int index=-1)
00170 {
00171     if (!out.child("gamelist_diff")) {
00172         out.root().add_child("gamelist_diff");
00173     }
00174 
00175     simple_wml::node* top = out.child("gamelist_diff");
00176     if(gamelist) {
00177         top = &top->add_child("change_child");
00178         top->set_attr_int("index", 0);
00179         top = &top->add_child("gamelist");
00180     }
00181 
00182     simple_wml::node& insert = top->add_child("insert_child");
00183     const simple_wml::node::child_list& children = src.children(type);
00184     assert(!children.empty());
00185     if(index < 0) {
00186         index = children.size() - 1;
00187     }
00188 
00189     assert(index < static_cast<int>(children.size()));
00190     insert.set_attr_int("index", index);
00191     children[index]->copy_into(insert.add_child(type));
00192 }
00193 
00194 bool make_delete_diff(const simple_wml::node& src,
00195                       const char* gamelist,
00196                       const char* type,
00197                       const simple_wml::node* remove,
00198                       simple_wml::document& out)
00199 {
00200     if (!out.child("gamelist_diff")) {
00201         out.root().add_child("gamelist_diff");
00202     }
00203 
00204     simple_wml::node* top = out.child("gamelist_diff");
00205     if(gamelist) {
00206         top = &top->add_child("change_child");
00207         top->set_attr_int("index", 0);
00208         top = &top->add_child("gamelist");
00209     }
00210 
00211     const simple_wml::node::child_list& children = src.children(type);
00212     const simple_wml::node::child_list::const_iterator itor =
00213         std::find(children.begin(), children.end(), remove);
00214     if(itor == children.end()) {
00215         return false;
00216     }
00217     const int index = itor - children.begin();
00218     simple_wml::node& del = top->add_child("delete_child");
00219     del.set_attr_int("index", index);
00220     del.add_child(type);
00221     return true;
00222 }
00223 
00224 bool make_change_diff(const simple_wml::node& src,
00225                       const char* gamelist,
00226                       const char* type,
00227                       const simple_wml::node* item,
00228                       simple_wml::document& out)
00229 {
00230     if (!out.child("gamelist_diff")) {
00231         out.root().add_child("gamelist_diff");
00232     }
00233 
00234     simple_wml::node* top = out.child("gamelist_diff");
00235     if(gamelist) {
00236         top = &top->add_child("change_child");
00237         top->set_attr_int("index", 0);
00238         top = &top->add_child("gamelist");
00239     }
00240     const simple_wml::node::child_list& children = src.children(type);
00241     const simple_wml::node::child_list::const_iterator itor =
00242         std::find(children.begin(), children.end(), item);
00243     if(itor == children.end()) {
00244         return false;
00245     }
00246 
00247     simple_wml::node& diff = *top;
00248     simple_wml::node& del = diff.add_child("delete_child");
00249     const int index = itor - children.begin();
00250     del.set_attr_int("index", index);
00251     del.add_child(type);
00252 
00253     //inserts will be processed first by the client, so insert at index+1,
00254     //and then when the delete is processed we'll slide into the right position
00255     simple_wml::node& insert = diff.add_child("insert_child");
00256     insert.set_attr_int("index", index + 1);
00257     children[index]->copy_into(insert.add_child(type));
00258     return true;
00259 }
00260 
00261 std::string player_status(wesnothd::player_map::const_iterator pl) {
00262     std::ostringstream out;
00263     const network::connection_stats& stats = network::get_connection_stats(pl->first);
00264     const int time_connected = stats.time_connected / 1000;
00265     const int seconds = time_connected % 60;
00266     const int minutes = (time_connected / 60) % 60;
00267     const int hours = time_connected / (60 * 60);
00268     out << "'" << pl->second.name() << "' @ " << network::ip_address(pl->first)
00269         << " connected for " << std::setw(2) << hours << ":" << std::setw(2) << minutes << ":" << std::setw(2) << seconds
00270         << " sent " << stats.bytes_sent << " bytes, received "
00271         << stats.bytes_received << " bytes";
00272     return out.str();
00273 }
00274 
00275 } // namespace
00276 
00277 class fps_limiter {
00278     size_t start_ticks_;
00279     size_t ms_per_frame_;
00280 public:
00281     fps_limiter(size_t ms_per_frame = 20) : start_ticks_(0), ms_per_frame_(ms_per_frame)
00282     {}
00283 
00284     void limit() {
00285         size_t current_ticks = SDL_GetTicks();
00286         if (current_ticks - start_ticks_ < ms_per_frame_) {
00287             SDL_Delay(ms_per_frame_ - (current_ticks - start_ticks_));
00288             start_ticks_ += ms_per_frame_;
00289         } else {
00290             start_ticks_ = current_ticks;
00291         }
00292     }
00293 
00294     void set_ms_per_frame(size_t ms_per_frame)
00295     {
00296         ms_per_frame_ = ms_per_frame;
00297     }
00298 
00299     void set_fps(size_t fps)
00300     {
00301         ms_per_frame_ = 1000 / fps;
00302     }
00303 };
00304 
00305 static fps_limiter fps_limit_;
00306 
00307 namespace {
00308     const std::string denied_msg = "You're not allowed to execute this command.";
00309     const std::string help_msg = "Available commands are: adminmsg <msg>,"
00310         " ban <mask> <time> <reason>, bans [deleted] [<ipmask>], clones,"
00311         " dul|deny_unregistered_login [yes|no], kick <mask> [<reason>],"
00312         " k[ick]ban <mask> <time> <reason>, help, games, metrics,"
00313         " netstats [all], [lobby]msg <message>, motd [<message>],"
00314         " pm|privatemsg <nickname> <message>, requests, sample, searchlog <mask>,"
00315         " signout, stats, status [<mask>], unban <ipmask>\n"
00316         "Specific strings (those not inbetween <> like the command names)"
00317         " are case insensitive.";
00318 }
00319 
00320 
00321 server::server(int port, const std::string& config_file, size_t min_threads,
00322         size_t max_threads) :
00323     net_manager_(min_threads, max_threads),
00324     server_(port),
00325     ban_manager_(),
00326     ip_log_(),
00327     failed_logins_(),
00328     user_handler_(NULL),
00329     seeds_(),
00330     players_(),
00331     ghost_players_(),
00332     games_(),
00333     not_logged_in_(),
00334     rooms_(players_),
00335     input_(),
00336     config_file_(config_file),
00337     cfg_(read_config()),
00338     accepted_versions_(),
00339     redirected_versions_(),
00340     proxy_versions_(),
00341     disallowed_names_(),
00342     admin_passwd_(),
00343     admins_(),
00344     motd_(),
00345     default_max_messages_(0),
00346     default_time_period_(0),
00347     concurrent_connections_(0),
00348     graceful_restart(false),
00349     lan_server_(time(NULL)),
00350     last_user_seen_time_(time(NULL)),
00351     restart_command(),
00352     max_ip_log_size_(0),
00353     uh_name_(),
00354     deny_unregistered_login_(false),
00355     save_replays_(false),
00356     replay_save_path_(),
00357     allow_remote_shutdown_(false),
00358     tor_ip_list_(),
00359     failed_login_limit_(),
00360     failed_login_ban_(),
00361     failed_login_buffer_size_(),
00362     version_query_response_("[version]\n[/version]\n", simple_wml::INIT_COMPRESSED),
00363     login_response_("[mustlogin]\n[/mustlogin]\n", simple_wml::INIT_COMPRESSED),
00364     join_lobby_response_("[join_lobby]\n[/join_lobby]\n", simple_wml::INIT_COMPRESSED),
00365     games_and_users_list_("[gamelist]\n[/gamelist]\n", simple_wml::INIT_STATIC),
00366     metrics_(),
00367     last_ping_(time(NULL)),
00368     last_stats_(last_ping_),
00369     last_uh_clean_(last_ping_),
00370     cmd_handlers_()
00371 {
00372     setup_handlers();
00373     load_config();
00374     ban_manager_.read();
00375     rooms_.read_rooms();
00376 
00377 #ifndef _MSC_VER
00378     signal(SIGHUP, reload_config);
00379 #endif
00380 
00381     signal(SIGINT, exit_sigint);
00382     signal(SIGTERM, exit_sigterm);
00383 }
00384 
00385 void server::setup_handlers()
00386 {
00387     cmd_handlers_["shut_down"] = &server::shut_down_handler;
00388     cmd_handlers_["restart"] = &server::restart_handler;
00389     cmd_handlers_["sample"] = &server::sample_handler;
00390     cmd_handlers_["help"] = &server::help_handler;
00391     cmd_handlers_["stats"] = &server::stats_handler;
00392     cmd_handlers_["metrics"] = &server::metrics_handler;
00393     cmd_handlers_["requests"] = &server::requests_handler;
00394     cmd_handlers_["games"] = &server::games_handler;
00395     cmd_handlers_["wml"] = &server::wml_handler;
00396     cmd_handlers_["netstats"] = &server::netstats_handler;
00397     cmd_handlers_["report"]   = &server::adminmsg_handler;
00398     cmd_handlers_["adminmsg"] = &server::adminmsg_handler;
00399     cmd_handlers_["pm"] = &server::pm_handler;
00400     cmd_handlers_["privatemsg"] = &server::pm_handler;
00401     cmd_handlers_["msg"] = &server::msg_handler;
00402     cmd_handlers_["lobbymsg"] = &server::msg_handler;
00403     cmd_handlers_["status"] = &server::status_handler;
00404     cmd_handlers_["clones"] = &server::clones_handler;
00405     cmd_handlers_["bans"] = &server::bans_handler;
00406     cmd_handlers_["ban"] = &server::ban_handler;
00407     cmd_handlers_["unban"] = &server::unban_handler;
00408     cmd_handlers_["ungban"] = &server::ungban_handler;
00409     cmd_handlers_["kick"] = &server::kick_handler;
00410     cmd_handlers_["kickban"] = &server::kickban_handler;
00411     cmd_handlers_["kban"] = &server::kickban_handler;
00412     cmd_handlers_["gban"] = &server::gban_handler;
00413     cmd_handlers_["motd"] = &server::motd_handler;
00414     cmd_handlers_["searchlog"] = &server::searchlog_handler;
00415     cmd_handlers_["sl"] = &server::searchlog_handler;
00416     cmd_handlers_["dul"] = &server::dul_handler;
00417     cmd_handlers_["deny_unregistered_login"] = &server::dul_handler;
00418 }
00419 
00420 void server::send_error(network::connection sock, const char* msg, const char* error_code) const
00421 {
00422     simple_wml::document doc;
00423     doc.root().add_child("error").set_attr("message", msg);
00424     if(*error_code != '\0') {
00425         doc.child("error")->set_attr("error_code", error_code);
00426     }
00427 
00428     send_doc(doc, sock, "error");
00429 }
00430 
00431 void server::send_warning(network::connection sock, const char* msg, const char* warning_code) const
00432 {
00433     simple_wml::document doc;
00434     doc.root().add_child("warning").set_attr("message", msg);
00435     if(*warning_code != '\0') {
00436         doc.child("warning")->set_attr("warning_code", warning_code);
00437     }
00438 
00439     send_doc(doc, sock, "warning");
00440 }
00441 
00442 void server::send_password_request(network::connection sock, const std::string& msg,
00443     const std::string& user, const char* error_code, bool force_confirmation)
00444 {
00445     std::string salt = user_handler_->create_salt();
00446     std::string pepper = user_handler_->create_pepper(user);
00447     std::string spices = pepper + salt;
00448     if(user_handler_->use_phpbb_encryption() && pepper.empty()) {
00449         send_error(sock, "Even though your nickname is registered on this server you "
00450                     "cannot log in due to an error in the hashing algorithm. "
00451                     "Logging into your forum account on http://forum.wesnoth.org "
00452                     "may fix this problem.");
00453         return;
00454     }
00455 
00456     seeds_.insert(std::pair<network::connection, std::string>(sock, salt));
00457 
00458     simple_wml::document doc;
00459     simple_wml::node& e = doc.root().add_child("error");
00460     e.set_attr("message", msg.c_str());
00461     e.set_attr("password_request", "yes");
00462     e.set_attr("phpbb_encryption", user_handler_->use_phpbb_encryption() ? "yes" : "no");
00463     e.set_attr("salt", spices.c_str());
00464     e.set_attr("force_confirmation", force_confirmation ? "yes" : "no");
00465     if(*error_code != '\0') {
00466         e.set_attr("error_code", error_code);
00467     }
00468 
00469     send_doc(doc, sock, "error");
00470 }
00471 
00472 config server::read_config() const {
00473     config configuration;
00474     if (config_file_ == "") return configuration;
00475     try {
00476         scoped_istream stream = preprocess_file(config_file_);
00477         read(configuration, *stream);
00478         LOG_SERVER << "Server configuration from file: '" << config_file_
00479             << "' read.\n";
00480     } catch(config::error& e) {
00481         ERR_CONFIG << "ERROR: Could not read configuration file: '"
00482             << config_file_ << "': '" << e.message << "'.\n";
00483     }
00484     return configuration;
00485 }
00486 
00487 void server::load_config() {
00488 #ifndef FIFODIR
00489 # ifdef _MSC_VER
00490 #  pragma message ("No FIFODIR set")
00491 #  define FIFODIR "d:/"
00492 # else
00493 #  warning "No FIFODIR set"
00494 #  ifdef _WIN32
00495 #    define FIFODIR "d:/"
00496 #  else
00497 #    define FIFODIR "/var/run/wesnothd"
00498 #   endif
00499 # endif
00500 #endif
00501     const std::string fifo_path = (cfg_["fifo_path"].empty() ? std::string(FIFODIR) + "/socket" : std::string(cfg_["fifo_path"]));
00502     input_.reset();
00503     input_.reset(new input_stream(fifo_path));
00504 
00505     save_replays_ = cfg_["save_replays"].to_bool();
00506     replay_save_path_ = cfg_["replay_save_path"].str();
00507 
00508     tor_ip_list_ = utils::split(cfg_["tor_ip_list_path"].empty() ? "" : read_file(cfg_["tor_ip_list_path"]), '\n');
00509 
00510     admin_passwd_ = cfg_["passwd"].str();
00511     motd_ = cfg_["motd"].str();
00512     lan_server_ = lexical_cast_default<time_t>(cfg_["lan_server"], 0);
00513     uh_name_ = cfg_["user_handler"].str();
00514 
00515     deny_unregistered_login_ = cfg_["deny_unregistered_login"].to_bool();
00516 
00517     allow_remote_shutdown_ = cfg_["allow_remote_shutdown"].to_bool();
00518 
00519     disallowed_names_.clear();
00520     if (cfg_["disallow_names"] == "") {
00521         disallowed_names_.push_back("*admin*");
00522         disallowed_names_.push_back("*admln*");
00523         disallowed_names_.push_back("*server*");
00524         disallowed_names_.push_back("player");
00525         disallowed_names_.push_back("network");
00526         disallowed_names_.push_back("human");
00527         disallowed_names_.push_back("computer");
00528         disallowed_names_.push_back("ai");
00529         disallowed_names_.push_back("ai?");
00530     } else {
00531         disallowed_names_ = utils::split(cfg_["disallow_names"]);
00532     }
00533     default_max_messages_ = cfg_["max_messages"].to_int(4);
00534     default_time_period_ = cfg_["messages_time_period"].to_int(10);
00535     concurrent_connections_ = cfg_["connections_allowed"].to_int(5);
00536     max_ip_log_size_ = cfg_["max_ip_log_size"].to_int(500);
00537 
00538     failed_login_limit_ = cfg_["failed_logins_limit"].to_int(10);
00539     failed_login_ban_ = cfg_["failed_logins_ban"].to_int(3600);
00540     failed_login_buffer_size_ = cfg_["failed_logins_buffer_size"].to_int(500);
00541 
00542     // Example config line:
00543     // restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
00544     // remember to make new one as a daemon or it will block old one
00545     restart_command = cfg_["restart_command"].str();
00546 
00547     fps_limit_.set_ms_per_frame(cfg_["ms_per_frame"].to_int(20));
00548 
00549     accepted_versions_.clear();
00550     const std::string& versions = cfg_["versions_accepted"];
00551     if (versions.empty() == false) {
00552         accepted_versions_ = utils::split(versions);
00553     } else {
00554         accepted_versions_.push_back(game_config::version);
00555         accepted_versions_.push_back("test");
00556     }
00557 
00558     redirected_versions_.clear();
00559     foreach (const config &redirect, cfg_.child_range("redirect")) {
00560         foreach (const std::string &version, utils::split(redirect["version"])) {
00561             redirected_versions_[version] = redirect;
00562         }
00563     }
00564 
00565     proxy_versions_.clear();
00566     foreach (const config &proxy, cfg_.child_range("proxy")) {
00567         foreach (const std::string &version, utils::split(proxy["version"])) {
00568             proxy_versions_[version] = proxy;
00569         }
00570     }
00571     ban_manager_.load_config(cfg_);
00572     rooms_.load_config(cfg_);
00573 
00574     // If there is a [user_handler] tag in the config file
00575     // allow nick registration, otherwise we set user_handler_
00576     // to NULL. Thus we must check user_handler_ for not being
00577     // NULL everytime we want to use it.
00578     user_handler_.reset();
00579 
00580     if (const config &user_handler = cfg_.child("user_handler")) {
00581         if(uh_name_ == "sample") {
00582             user_handler_.reset(new suh(user_handler));
00583         }
00584 #ifdef HAVE_MYSQLPP
00585         else if(uh_name_ == "forum" || uh_name_.empty()) {
00586             user_handler_.reset(new fuh(user_handler));
00587         }
00588 #endif
00589         // Initiate the mailer class with the [mail] tag
00590         // from the config file
00591         if (user_handler_) user_handler_->init_mailer(cfg_.child("mail"));
00592     }
00593 }
00594 
00595 bool server::ip_exceeds_connection_limit(const std::string& ip) const {
00596     if (concurrent_connections_ == 0) return false;
00597     size_t connections = 0;
00598     for (wesnothd::player_map::const_iterator i = players_.begin(); i != players_.end(); ++i) {
00599         if (network::ip_address(i->first) == ip) {
00600             ++connections;
00601         }
00602     }
00603 
00604     return connections >= concurrent_connections_;
00605 }
00606 
00607 std::string server::is_ip_banned(const std::string& ip) const {
00608     if (!tor_ip_list_.empty()) {
00609         if (find(tor_ip_list_.begin(), tor_ip_list_.end(), ip) != tor_ip_list_.end()) return "TOR IP";
00610     }
00611     return ban_manager_.is_ip_banned(ip);
00612 }
00613 
00614 void server::dump_stats(const time_t& now) {
00615     last_stats_ = now;
00616     LOG_SERVER << "Statistics:"
00617         << "\tnumber_of_games = " << games_.size()
00618         << "\tnumber_of_users = " << players_.size()
00619         << "\tlobby_users = " << rooms_.lobby().size() << "\n";
00620 }
00621 
00622 void server::clean_user_handler(const time_t& now) {
00623     if(!user_handler_) {
00624         return;
00625     }
00626     last_uh_clean_ = now;
00627     user_handler_->clean_up();
00628 }
00629 
00630 void server::run() {
00631     int graceful_counter = 0;
00632 
00633     for (int loop = 0;; ++loop) {
00634         // Try to run with 50 FPS all the time
00635         // Server will respond a bit faster under heavy load
00636         fps_limit_.limit();
00637         try {
00638             // We are going to waith 10 seconds before shutting down so users can get out of game.
00639             if (graceful_restart && games_.empty() && ++graceful_counter > 500 )
00640             {
00641                 // TODO: We should implement client side autoreconnect.
00642                 // Idea:
00643                 // server should send [reconnect]host=host,port=number[/reconnect]
00644                 // Then client would reconnect to new server automatically.
00645                 // This would also allow server to move to new port or address if there is need
00646 
00647                 process_command("msg All games ended. Shutting down now. Reconnect to the new server instance.", "system");
00648                 throw network::error("shut down");
00649             }
00650 
00651             if (config_reload == 1) {
00652                 cfg_ = read_config();
00653                 load_config();
00654                 config_reload = 0;
00655             }
00656 
00657             // Process commands from the server socket/fifo
00658             std::string admin_cmd;
00659             if (input_ && input_->read_line(admin_cmd)) {
00660                 LOG_SERVER << "Admin Command: type: " << admin_cmd << "\n";
00661                 const std::string res = process_command(admin_cmd, "*socket*");
00662                 // Only mark the response if we fake the issuer (i.e. command comes from IRC or so)
00663                 if (admin_cmd.at(0) == '+') {
00664                     LOG_SERVER << "[admin_command_response]\n" << res << "\n" << "[/admin_command_response]\n";
00665                 } else {
00666                     LOG_SERVER << res << "\n";
00667                 }
00668             }
00669 
00670             time_t now = time(NULL);
00671             if (last_ping_ + network::ping_interval <= now) {
00672                 if (lan_server_ && players_.empty() && last_user_seen_time_ + lan_server_ < now)
00673                 {
00674                     LOG_SERVER << "Lan server has been empty for  " << (now - last_user_seen_time_) << " seconds. Shutting down!\n";
00675                     // We have to shutdown
00676                     graceful_restart = true;
00677                 }
00678                 // and check if bans have expired
00679                 ban_manager_.check_ban_times(now);
00680                 // Make sure we log stats every 5 minutes
00681                 if (last_stats_ + 5 * 60 <= now) {
00682                     dump_stats(now);
00683                     if (rooms_.dirty()) rooms_.write_rooms();
00684                 }
00685 
00686                 // Cleaning the user_handler once a day should be more than enough
00687                 if (last_uh_clean_ + 60 * 60 * 24 <= now) {
00688                     clean_user_handler(now);
00689                 }
00690 
00691                 // Send network stats every hour
00692                 static int prev_hour = localtime(&now)->tm_hour;
00693                 if (prev_hour != localtime(&now)->tm_hour)
00694                 {
00695                     prev_hour = localtime(&now)->tm_hour;
00696                     LOG_SERVER << network::get_bandwidth_stats();
00697 
00698                 }
00699 
00700                 // send a 'ping' to all players to detect ghosts
00701                 DBG_SERVER << "Pinging inactive players.\n" ;
00702                 std::ostringstream strstr ;
00703                 strstr << "ping=\"" << now << "\"" ;
00704                 simple_wml::document ping( strstr.str().c_str(),
00705                                simple_wml::INIT_COMPRESSED );
00706                 simple_wml::string_span s = ping.output_compressed();
00707                 foreach (network::connection sock, ghost_players_) {
00708                     if (!lg::debug.dont_log(log_server)) {
00709                         wesnothd::player_map::const_iterator i = players_.find(sock);
00710                         if (i != players_.end()) {
00711                             DBG_SERVER << "Pinging " << i->second.name() << "(" << i->first << ").\n";
00712                         } else {
00713                             ERR_SERVER << "Player " << sock << " is in ghost_players_ but not in players_.\n";
00714                         }
00715                     }
00716                     network::send_raw_data(s.begin(), s.size(), sock, "ping") ;
00717                 }
00718 
00719                 // Copy new player list on top of ghost_players_ list.
00720                 // Only a single thread should be accessing this
00721                 // Erase before we copy - speeds inserts
00722                 ghost_players_.clear();
00723                 foreach (const wesnothd::player_map::value_type v, players_) {
00724                     ghost_players_.insert(v.first);
00725                 }
00726                 last_ping_ = now;
00727             }
00728 
00729             network::process_send_queue();
00730 
00731             network::connection sock = network::accept_connection();
00732             if (sock) {
00733                 const std::string ip = network::ip_address(sock);
00734                 const std::string reason = is_ip_banned(ip);
00735                 if (!reason.empty()) {
00736                     LOG_SERVER << ip << "\trejected banned user. Reason: " << reason << "\n";
00737                     send_error(sock, "You are banned. Reason: " + reason);
00738                     network::disconnect(sock);
00739                 } else if (ip_exceeds_connection_limit(ip)) {
00740                     LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
00741                     send_error(sock, "Too many connections from your IP.");
00742                     network::disconnect(sock);
00743                 } else {
00744                     DBG_SERVER << ip << "\tnew connection accepted. (socket: "
00745                         << sock << ")\n";
00746                     send_doc(version_query_response_, sock);
00747                     not_logged_in_.insert(sock);
00748                 }
00749             }
00750 
00751             static int sample_counter = 0;
00752 
00753             std::vector<char> buf;
00754             network::bandwidth_in_ptr bandwidth_type;
00755             while ((sock = network::receive_data(buf, &bandwidth_type)) != network::null_connection) {
00756                 metrics_.service_request();
00757 
00758                 if(buf.empty()) {
00759                     WRN_SERVER << "received empty packet\n";
00760                     continue;
00761                 }
00762 
00763                 const bool sample = request_sample_frequency >= 1 && (sample_counter++ % request_sample_frequency) == 0;
00764 
00765                 const clock_t before_parsing = get_cpu_time(sample);
00766 
00767                 char* buf_ptr = new char [buf.size()];
00768                 memcpy(buf_ptr, &buf[0], buf.size());
00769                 simple_wml::string_span compressed_buf(buf_ptr, buf.size());
00770                 boost::scoped_ptr<simple_wml::document> data_ptr;
00771                 try {
00772                     data_ptr.reset(new simple_wml::document(compressed_buf)); // might throw a simple_wml::error
00773                     data_ptr->take_ownership_of_buffer(buf_ptr);
00774 
00775                 } catch (simple_wml::error& e) {
00776                     WRN_CONFIG << "simple_wml error in received data: " << e.message << std::endl;
00777                     send_error(sock, "Invalid WML received: " + e.message);
00778                     delete [] buf_ptr;
00779                     continue;
00780                 } catch(...) {
00781                     delete [] buf_ptr;
00782                     throw;
00783                 }
00784 
00785                 simple_wml::document& data = *data_ptr;
00786                 std::vector<char>().swap(buf);
00787 
00788                 const clock_t after_parsing = get_cpu_time(sample);
00789 
00790                 process_data(sock, data);
00791 
00792                 bandwidth_type->set_type(data.root().first_child().to_string());
00793                 if(sample) {
00794                     const clock_t after_processing = get_cpu_time(sample);
00795                     metrics_.record_sample(data.root().first_child(),
00796                               after_parsing - before_parsing,
00797                               after_processing - after_parsing);
00798                 }
00799 
00800             }
00801 
00802             metrics_.no_requests();
00803 
00804         } catch(simple_wml::error& e) {
00805             WRN_CONFIG << "Warning: error in received data: " << e.message << "\n";
00806         } catch(network::error& e) {
00807             if (e.message == "shut down") {
00808                 LOG_SERVER << "Try to disconnect all users...\n";
00809                 for (wesnothd::player_map::const_iterator pl = players_.begin();
00810                     pl != players_.end(); ++pl)
00811                 {
00812                     network::disconnect(pl->first);
00813                 }
00814                 LOG_SERVER << "Shutting server down.\n";
00815                 break;
00816             }
00817             if (!e.socket) {
00818                 ERR_SERVER << "network error: " << e.message << "\n";
00819                 e.disconnect();
00820                 continue;
00821             }
00822             DBG_SERVER << "socket closed: " << e.message << "\n";
00823             const std::string ip = network::ip_address(e.socket);
00824             if (proxy::is_proxy(e.socket)) {
00825                 LOG_SERVER << ip << "\tProxy user disconnected.\n";
00826                 proxy::disconnect(e.socket);
00827                 e.disconnect();
00828                 DBG_SERVER << "done closing socket...\n";
00829                 continue;
00830             }
00831             // Was the user already logged in?
00832             const wesnothd::player_map::iterator pl_it = players_.find(e.socket);
00833             if (pl_it == players_.end()) {
00834                 std::set<network::connection>::iterator i = not_logged_in_.find(e.socket);
00835                 if (i != not_logged_in_.end()) {
00836                     DBG_SERVER << ip << "\tNot logged in user disconnected.\n";
00837                     not_logged_in_.erase(i);
00838                 } else {
00839                     WRN_SERVER << ip << "\tWarning: User disconnected right after the connection was accepted.\n";
00840                 }
00841                 e.disconnect();
00842                 DBG_SERVER << "done closing socket...\n";
00843                 continue;
00844             }
00845             const simple_wml::node::child_list& users = games_and_users_list_.root().children("user");
00846             const size_t index = std::find(users.begin(), users.end(), pl_it->second.config_address()) - users.begin();
00847             if (index < users.size()) {
00848                 simple_wml::document diff;
00849                 if(make_delete_diff(games_and_users_list_.root(), NULL, "user",
00850                                     pl_it->second.config_address(), diff)) {
00851                     rooms_.lobby().send_data(diff, e.socket);
00852                 }
00853 
00854                 games_and_users_list_.root().remove_child("user", index);
00855             } else {
00856                 ERR_SERVER << ip << "ERROR: Could not find user to remove: "
00857                     << pl_it->second.name() << " in games_and_users_list_.\n";
00858             }
00859             // Was the player in the lobby or a game?
00860             if (rooms_.in_lobby(e.socket)) {
00861                 rooms_.remove_player(e.socket);
00862                 LOG_SERVER << ip << "\t" << pl_it->second.name()
00863                     << "\thas logged off. (socket: " << e.socket << ")\n";
00864 
00865             } else {
00866                 for (std::vector<wesnothd::game*>::iterator g = games_.begin();
00867                     g != games_.end(); ++g)
00868                 {
00869                     if (!(*g)->is_member(e.socket)) {
00870                         continue;
00871                     }
00872                     // Did the last player leave?
00873                     if ((*g)->remove_player(e.socket, true)) {
00874                         delete_game(g);
00875                         break;
00876                     } else {
00877                         (*g)->describe_slots();
00878 
00879                         update_game_in_lobby(*g, e.socket);
00880                     }
00881                     break;
00882                 }
00883             }
00884 
00885             // Find the matching nick-ip pair in the log and update the sign off time
00886             connection_log ip_name = connection_log(pl_it->second.name(), ip, 0);
00887             std::deque<connection_log>::iterator i = std::find(ip_log_.begin(), ip_log_.end(), ip_name);
00888             if(i != ip_log_.end()) {
00889                 i->log_off = time(NULL);
00890             }
00891 
00892             players_.erase(pl_it);
00893             ghost_players_.erase(e.socket);
00894             if (lan_server_)
00895             {
00896                 last_user_seen_time_ = time(0);
00897             }
00898             e.disconnect();
00899             DBG_SERVER << "done closing socket...\n";
00900 
00901         // Catch user_handler exceptions here, to prevent the
00902         // server from going down completely. Once we are sure
00903         // all user_handler exceptions are caught correctly
00904         // this can removed.
00905         } catch (user_handler::error& e) {
00906             ERR_SERVER << "Uncaught user_handler exception: " << e.message << "\n";
00907         }
00908     }
00909 }
00910 
00911 void server::process_data(const network::connection sock,
00912                           simple_wml::document& data) {
00913     if (proxy::is_proxy(sock)) {
00914         proxy::received_data(sock, data);
00915         return;
00916     }
00917 
00918     // We know the client is alive for this interval
00919     // Remove player from ghost_players map if selective_ping
00920     // is enabled for the player.
00921 
00922     if (ghost_players_.find(sock) != ghost_players_.end()) {
00923         const wesnothd::player_map::const_iterator pl = players_.find(sock);
00924         if (pl != players_.end()) {
00925             if (pl->second.selective_ping() ) {
00926                 ghost_players_.erase(sock);
00927             }
00928         }
00929     }
00930 
00931     // Process the message
00932     simple_wml::node& root = data.root();
00933     if(root.has_attr("ping")) {
00934         // Ignore client side pings for now.
00935         return;
00936     } else if(not_logged_in_.find(sock) != not_logged_in_.end()) {
00937         // Someone who is not yet logged in is sending login details.
00938         process_login(sock, data);
00939     } else if (simple_wml::node* query = root.child("query")) {
00940         process_query(sock, *query);
00941     } else if (simple_wml::node* nickserv = root.child("nickserv")) {
00942         process_nickserv(sock, *nickserv);
00943     } else if (simple_wml::node* whisper = root.child("whisper")) {
00944         process_whisper(sock, *whisper);
00945     } else if (rooms_.in_lobby(sock)) {
00946         process_data_lobby(sock, data);
00947     } else {
00948         process_data_game(sock, data);
00949     }
00950 }
00951 
00952 
00953 
00954 void server::process_login(const network::connection sock,
00955                            simple_wml::document& data) {
00956 
00957     // See if the client is sending their version number.
00958     if (const simple_wml::node* const version = data.child("version")) {
00959         const simple_wml::string_span& version_str_span = (*version)["version"];
00960         const std::string version_str(version_str_span.begin(),
00961                                       version_str_span.end());
00962         std::vector<std::string>::const_iterator accepted_it;
00963         // Check if it is an accepted version.
00964         for (accepted_it = accepted_versions_.begin();
00965             accepted_it != accepted_versions_.end(); ++accepted_it) {
00966             if (utils::wildcard_string_match(version_str, *accepted_it)) break;
00967         }
00968         if (accepted_it != accepted_versions_.end()) {
00969             LOG_SERVER << network::ip_address(sock)
00970                 << "\tplayer joined using accepted version " << version_str
00971                 << ":\ttelling them to log in.\n";
00972             send_doc(login_response_, sock);
00973             return;
00974         }
00975         std::map<std::string, config>::const_iterator config_it;
00976         // Check if it is a redirected version
00977         for (config_it = redirected_versions_.begin();
00978             config_it != redirected_versions_.end(); ++config_it)
00979         {
00980             if (utils::wildcard_string_match(version_str, config_it->first))
00981                 break;
00982         }
00983         if (config_it != redirected_versions_.end()) {
00984             LOG_SERVER << network::ip_address(sock)
00985                 << "\tplayer joined using version " << version_str
00986                 << ":\tredirecting them to " << config_it->second["host"]
00987                 << ":" << config_it->second["port"] << "\n";
00988             config response;
00989             response.add_child("redirect", config_it->second);
00990             network::send_data(response, sock, "redirect");
00991             return;
00992         }
00993         // Check if it's a version we should start a proxy for.
00994         for (config_it = proxy_versions_.begin();
00995             config_it != proxy_versions_.end(); ++config_it)
00996         {
00997             if (utils::wildcard_string_match(version_str, config_it->first))
00998                 break;
00999         }
01000         if (config_it != proxy_versions_.end()) {
01001             LOG_SERVER << network::ip_address(sock)
01002                 << "\tplayer joined using version " << version_str
01003                 << ":\tconnecting them by proxy to " << config_it->second["host"]
01004                 << ":" << config_it->second["port"] << "\n";
01005             proxy::create_proxy(sock, config_it->second["host"],
01006                 config_it->second["port"].to_int(15000));
01007             return;
01008         }
01009         // No match, send a response and reject them.
01010         LOG_SERVER << network::ip_address(sock)
01011             << "\tplayer joined using unknown version " << version_str
01012             << ":\trejecting them\n";
01013         config response;
01014         if (!accepted_versions_.empty()) {
01015             response["version"] = *accepted_versions_.begin();
01016         } else if (redirected_versions_.empty() == false) {
01017             response["version"] = redirected_versions_.begin()->first;
01018         } else {
01019             ERR_SERVER << "ERROR: This server doesn't accept any versions at all.\n";
01020             response["version"] = "null";
01021         }
01022         network::send_data(response, sock, "error");
01023         return;
01024     }
01025 
01026     const simple_wml::node* const login = data.child("login");
01027     // Client must send a login first.
01028     if (login == NULL) {
01029         send_error(sock, "You must login first.", MP_MUST_LOGIN);
01030         return;
01031     }
01032 
01033     // Check if the username is valid (all alpha-numeric plus underscore and hyphen)
01034     std::string username = (*login)["username"].to_string();
01035     if (!utils::isvalid_username(username)) {
01036         send_error(sock, "The nickname '" + username + "' contains invalid "
01037             "characters. Only alpha-numeric characters, underscores and hyphens"
01038             "are allowed.", MP_INVALID_CHARS_IN_NAME_ERROR);
01039         return;
01040     }
01041     if (username.size() > 20) {
01042         send_error(sock, "The nickname '" + username + "' is too long. Nicks must be 20 characters or less.",
01043             MP_NAME_TOO_LONG_ERROR);
01044         return;
01045     }
01046     // Check if the username is allowed.
01047     for (std::vector<std::string>::const_iterator d_it = disallowed_names_.begin();
01048         d_it != disallowed_names_.end(); ++d_it)
01049     {
01050         if (utils::wildcard_string_match(utils::lowercase(username),
01051             utils::lowercase(*d_it)))
01052         {
01053             send_error(sock, "The nickname '" + username + "' is reserved and cannot be used by players",
01054                 MP_NAME_RESERVED_ERROR);
01055             return;
01056         }
01057     }
01058 
01059     // If this is a request for password reminder
01060     if(user_handler_) {
01061         std::string password_reminder = (*login)["password_reminder"].to_string();
01062         if(password_reminder == "yes") {
01063             try {
01064                 user_handler_->password_reminder(username);
01065                 send_error(sock, "Your password reminder email has been sent.");
01066             } catch (user_handler::error& e) {
01067                 send_error(sock, "There was an error sending your password reminder email. The error message was: " +
01068                 e.message);
01069             }
01070             return;
01071         }
01072     }
01073 
01074     // Check the username isn't already taken
01075     bool name_taken = false;
01076     wesnothd::player_map::const_iterator p;
01077     for (p = players_.begin(); p != players_.end(); ++p) {
01078         if (p->second.name() == username) {
01079             name_taken = true;
01080             break;
01081         }
01082     }
01083 
01084     // unregistered users may not get auto-kicked to prevent abuse
01085     if (name_taken && !p->second.registered()) {
01086         send_error(sock, "The nickname '" + username + "' is already taken.", MP_NAME_TAKEN_ERROR);
01087         return;
01088     }
01089 
01090     // Check for password
01091 
01092     // Current login procedure  for registered nicks is:
01093     // - Client asks to log in with a particular nick
01094     // - Server sends client random salt plus some info
01095     //  generated from the original hash that is required to
01096     //  regenerate the hash
01097     // - Client generates hash for the user provided password
01098     //  and mixes it with the received random salt
01099     // - Server received salted hash, salts the valid hash with
01100     //  the same salt it sent to the client and compares the results
01101 
01102     bool registered = false;
01103     if(user_handler_) {
01104         std::string password = (*login)["password"].to_string();
01105         const bool exists = user_handler_->user_exists(username);
01106         // This name is registered but the account is not active
01107         if(exists && !user_handler_->user_is_active(username)) {
01108             send_warning(sock, "The nickname '" + username + "' is inactive. You cannot claim ownership of this "
01109                 "nickname until you activate your account via email or ask an administrator to do it for you.", MP_NAME_INACTIVE_WARNING);
01110             //registered = false;
01111         }
01112         else if(exists) {
01113             // This name is registered and no password provided
01114             if(password.empty()) {
01115                 if (!name_taken) {
01116                     send_password_request(sock, "The nickname '" + username +"' is registered on this server.",
01117                             username, MP_PASSWORD_REQUEST);
01118                 } else {
01119                     send_password_request(sock, "The nickname '" + username + "' is registered on this server."
01120                             "\n\nWARNING: There is already a client using this username, "
01121                             "logging in will cause that client to be kicked!",
01122                             username, MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME, true);
01123                 }
01124                 return;
01125             }
01126 
01127             // A password (or hashed password) was provided, however
01128             // there is no seed
01129             if(seeds_[sock].empty()) {
01130                 send_password_request(sock, "Please try again.", username, MP_NO_SEED_ERROR);
01131             }
01132             // This name is registered and an incorrect password provided
01133             else if(!(user_handler_->login(username, password, seeds_[sock]))) {
01134                 const time_t now = time(NULL);
01135 
01136                 // Reset the random seed
01137                 seeds_.erase(sock);
01138 
01139                 login_log login_ip = login_log(network::ip_address(sock), 0, now);
01140                 std::deque<login_log>::iterator i = std::find(failed_logins_.begin(), failed_logins_.end(), login_ip);
01141                 if(i == failed_logins_.end()) {
01142                     failed_logins_.push_back(login_ip);
01143                     i = --failed_logins_.end();
01144 
01145                     // Remove oldest entry if maximum size is exceeded
01146                     if(failed_logins_.size() > failed_login_buffer_size_)
01147                         failed_logins_.pop_front();
01148 
01149                 }
01150 
01151                 if (i->first_attempt + failed_login_ban_ < now) {
01152                     // Clear and move to the beginning
01153                     failed_logins_.erase(i);
01154                     failed_logins_.push_back(login_ip);
01155                     i = --failed_logins_.end();
01156                 }
01157 
01158                 i->attempts++;
01159 
01160                 if (i->attempts > failed_login_limit_) {
01161                     LOG_SERVER << ban_manager_.ban(login_ip.ip, now + failed_login_ban_, "Maximum login attempts exceeded", "automatic", "", username);
01162                     send_error(sock, "You have made too many failed login attempts.", MP_TOO_MANY_ATTEMPTS_ERROR);
01163                     network::queue_disconnect(sock);
01164                 } else {
01165                     send_password_request(sock, "The password you provided for the nickname '" + username +
01166                         "' was incorrect.", username, MP_INCORRECT_PASSWORD_ERROR);
01167                 }
01168 
01169                 // Log the failure
01170                 LOG_SERVER << network::ip_address(sock) << "\t"
01171                         << "Login attempt with incorrect password for nickname '" << username << "'.\n";
01172                 return;
01173             }
01174             // This name exists and the password was neither empty nor incorrect
01175             registered = true;
01176             // Reset the random seed
01177             seeds_.erase(sock);
01178             user_handler_->user_logged_in(username);
01179 
01180             if (name_taken) {
01181                 // If there is already a client using this username kick it
01182                 process_command("kick " + username + " autokick by registered user", username);
01183             }
01184         }
01185     }
01186 
01187     // If we disallow unregistered users and this user is not registered send an error
01188     if(user_handler_ && !registered && deny_unregistered_login_) {
01189         send_error(sock, "The nickname '" + username + "' is not registered. "
01190                 "This server disallows unregistered nicknames.", MP_NAME_UNREGISTERED_ERROR);
01191         return;
01192     }
01193 
01194     // if registered, the old client got kicked
01195     if (name_taken && !registered) {
01196         send_error(sock, "The nickname '" + username + "' is already taken.", MP_NAME_TAKEN_ERROR);
01197         return;
01198     }
01199 
01200     // Check if the version is now available. If it is not, this player must
01201     // always be pinged.
01202     bool selective_ping = false ;
01203     if( (*login)["selective_ping"].to_bool() ) {
01204       selective_ping = true ;
01205       DBG_SERVER << "selective ping is ENABLED for " << sock << "\n" ;
01206     } else {
01207       DBG_SERVER << "selective ping is DISABLED for  " << sock << "\n" ;
01208     }
01209 
01210     send_doc(join_lobby_response_, sock);
01211 
01212     simple_wml::node& player_cfg = games_and_users_list_.root().add_child("user");
01213     const wesnothd::player new_player(username, player_cfg, registered,
01214         default_max_messages_, default_time_period_, selective_ping,
01215         user_handler_ && user_handler_->user_is_moderator(username));
01216 
01217     // If the new player does not have selective ping enabled, immediately
01218     // add the player to the ghost player's list. This ensures a client won't
01219     // have to wait as long as x2 the current ping delay; which could cause
01220     // a client-side disconnection.
01221     players_.insert(std::make_pair(sock, new_player));
01222     if( !selective_ping )
01223       ghost_players_.insert(sock) ;
01224 
01225     not_logged_in_.erase(sock);
01226     rooms_.enter_lobby(sock);
01227     // Send the new player the entire list of games and players
01228     send_doc(games_and_users_list_, sock);
01229 
01230     if (motd_ != "") {
01231         rooms_.lobby().send_server_message(motd_, sock);
01232     }
01233 
01234     // Send other players in the lobby the update that the player has joined
01235     simple_wml::document diff;
01236     make_add_diff(games_and_users_list_.root(), NULL, "user", diff);
01237     rooms_.lobby().send_data(diff, sock);
01238 
01239     LOG_SERVER << network::ip_address(sock) << "\t" << username
01240         << "\thas logged on" << (registered ? " to a registered account" : "")
01241         << ". (socket: " << sock << ")\n";
01242 
01243     for (std::vector<wesnothd::game*>::const_iterator g = games_.begin(); g != games_.end(); ++g) {
01244         // Note: This string is parsed by the client to identify lobby join messages!
01245         (*g)->send_server_message_to_all(username + " has logged into the lobby");
01246     }
01247 
01248     if(user_handler_ && user_handler_->user_is_moderator(username)) {
01249         LOG_SERVER << "Admin automatically recognized: IP: "
01250             << network::ip_address(sock) << "\tnick: "
01251             << username << std::endl;
01252         // This string is parsed by the client!
01253         rooms_.lobby().send_server_message("You are now recognized as an administrator. "
01254                 "If you no longer want to be automatically authenticated use '/query signout'.", sock);
01255     }
01256 
01257     // Log the IP
01258     connection_log ip_name = connection_log(username, network::ip_address(sock), 0);
01259     if (std::find(ip_log_.begin(), ip_log_.end(), ip_name) == ip_log_.end()) {
01260         ip_log_.push_back(ip_name);
01261         // Remove the oldest entry if the size of the IP log exceeds the maximum size
01262         if(ip_log_.size() > max_ip_log_size_) ip_log_.pop_front();
01263     }
01264 }
01265 
01266 void server::process_query(const network::connection sock,
01267                            simple_wml::node& query) {
01268     const wesnothd::player_map::iterator pl = players_.find(sock);
01269     if (pl == players_.end()) {
01270         DBG_SERVER << "ERROR: process_query(): Could not find player with socket: " << sock << "\n";
01271         return;
01272     }
01273     const std::string command(query["type"].to_string());
01274     std::ostringstream response;
01275     const std::string& help_msg = "Available commands are: adminmsg <msg>, help, games, metrics,"
01276             " motd, netstats [all], requests, sample, stats, status, wml.";
01277     // Commands a player may issue.
01278     if (command == "status") {
01279         response << process_command(command + " " + pl->second.name(), pl->second.name());
01280     } else if (command.find("adminmsg") == 0
01281             || command == "games"
01282             || command == "metrics"
01283             || command == "motd"
01284             || command == "netstats"
01285             || command == "netstats all"
01286             || command == "requests"
01287             || command == "sample"
01288             || command == "stats"
01289             || command == "status " + pl->second.name()
01290             || command == "wml")
01291     {
01292         response << process_command(command, pl->second.name());
01293     } else if (pl->second.is_moderator()) {
01294         if (command == "signout") {
01295             LOG_SERVER << "Admin signed out: IP: "
01296                 << network::ip_address(sock) << "\tnick: "
01297                 << pl->second.name() << std::endl;
01298             pl->second.set_moderator(false);
01299             // This string is parsed by the client!
01300             response << "You are no longer recognized as an administrator.";
01301             if(user_handler_) {
01302                 user_handler_->set_is_moderator(pl->second.name(), false);
01303             }
01304         } else {
01305             LOG_SERVER << "Admin Command: type: " << command
01306                 << "\tIP: "<< network::ip_address(sock)
01307                 << "\tnick: "<< pl->second.name() << std::endl;
01308             response << process_command(command, pl->second.name());
01309             LOG_SERVER << response.str() << std::endl;
01310         }
01311     } else if (command == "help" || command.empty()) {
01312         response << help_msg;
01313     } else if (command == "admin" || command.find("admin ") == 0) {
01314         if (admin_passwd_.empty()) {
01315             rooms_.lobby().send_server_message("No password set.", sock);
01316             return;
01317         }
01318         std::string passwd;
01319         if (command.size() >= 6) passwd = command.substr(6);
01320         if (passwd == admin_passwd_) {
01321             LOG_SERVER << "New Admin recognized: IP: "
01322                 << network::ip_address(sock) << "\tnick: "
01323                 << pl->second.name() << std::endl;
01324             pl->second.set_moderator(true);
01325             // This string is parsed by the client!
01326             response << "You are now recognized as an administrator.";
01327             if (user_handler_) {
01328                 user_handler_->set_is_moderator(pl->second.name(), true);
01329             }
01330         } else {
01331             WRN_SERVER << "FAILED Admin attempt with password: '" << passwd << "'\tIP: "
01332                 << network::ip_address(sock) << "\tnick: "
01333                 << pl->second.name() << std::endl;
01334             response << "Error: wrong password";
01335         }
01336     } else {
01337         response << "Error: unrecognized query: '" << command << "'\n" << help_msg;
01338     }
01339     rooms_.lobby().send_server_message(response.str(), sock);
01340 }
01341 
01342 void server::start_new_server() {
01343     if (restart_command.empty())
01344         return;
01345 
01346     // Example config line:
01347     // restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
01348     // remember to make new one as a daemon or it will block old one
01349     if (std::system(restart_command.c_str())) {
01350         ERR_SERVER << "Failed to start new server with command: " << restart_command << "\n";
01351     } else {
01352         LOG_SERVER << "New server started with command: " << restart_command << "\n";
01353     }
01354 }
01355 
01356 std::string server::process_command(std::string query, std::string issuer_name) {
01357     utils::strip(query);
01358 
01359     if (issuer_name == "*socket*" && query.at(0) == '+') {
01360         // The first argument might be "+<issuer>: ".
01361         // In that case we use +<issuer>+ as the issuer_name.
01362         // (Mostly used for communication with IRC.)
01363         std::string::iterator issuer_end =
01364                 std::find(query.begin(), query.end(), ':');
01365         std::string issuer(query.begin() + 1, issuer_end);
01366         if (!issuer.empty()) {
01367             issuer_name = "+" + issuer + "+";
01368             query = std::string(issuer_end + 1, query.end());
01369             utils::strip(query);
01370         }
01371     }
01372 
01373     const std::string::iterator i = std::find(query.begin(), query.end(), ' ');
01374     const std::string command = utils::lowercase(std::string(query.begin(), i));
01375     std::string parameters = (i == query.end() ? "" : std::string(i + 1, query.end()));
01376     utils::strip(parameters);
01377 
01378     std::ostringstream out;
01379     std::map<std::string, server::cmd_handler>::iterator handler_itor = cmd_handlers_.find(command);
01380     if(handler_itor == cmd_handlers_.end()) {
01381         out << "Command '" << command << "' is not recognized.\n" << help_msg;
01382     } else {
01383         const cmd_handler &handler = handler_itor->second;
01384         handler(this, issuer_name, query, parameters, &out);
01385     }
01386 
01387     return out.str();
01388 }
01389 
01390 // Shutdown, restart and sample commands can only be issued via the socket.
01391 void server::shut_down_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01392     assert(out != NULL);
01393 
01394     if (issuer_name != "*socket*" && !allow_remote_shutdown_) {
01395         *out << denied_msg;
01396         return;
01397     }
01398     if (parameters == "now") {
01399         throw network::error("shut down");
01400     } else {
01401         // Graceful shut down.
01402         server_.stop();
01403         input_.reset();
01404         graceful_restart = true;
01405         process_command("msg The server is shutting down. You may finish your games but can't start new ones. Once all games have ended the server will exit.", issuer_name);
01406         *out << "Server is doing graceful shut down.";
01407     }
01408 }
01409 
01410 void server::restart_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01411     assert(out != NULL);
01412 
01413     if (issuer_name != "*socket*" && !allow_remote_shutdown_) {
01414         *out << denied_msg;
01415         return;
01416     }
01417 
01418     if (restart_command.empty()) {
01419         *out << "No restart_command configured! Not restarting.";
01420     } else {
01421         graceful_restart = true;
01422         // stop listening socket
01423         server_.stop();
01424         input_.reset();
01425         // start new server
01426         start_new_server();
01427         process_command("msg The server has been restarted. You may finish current games but can't start new ones and new players can't join this (old) server instance. (So if a player of your game disconnects you have to save, reconnect and reload the game on the new server instance. It is actually recommended to do that right away.)", issuer_name);
01428         *out << "New server started.";
01429     }
01430 }
01431 
01432 void server::sample_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01433     assert(out != NULL);
01434 
01435     if (parameters.empty()) {
01436         *out << "Current sample frequency: " << request_sample_frequency;
01437         return;
01438     } else if (issuer_name != "*socket*") {
01439         *out << denied_msg;
01440         return;
01441     }
01442     request_sample_frequency = atoi(parameters.c_str());
01443     if (request_sample_frequency <= 0) {
01444         *out << "Sampling turned off.";
01445     } else {
01446         *out << "Sampling every " << request_sample_frequency << " requests.";
01447     }
01448 }
01449 
01450 void server::help_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01451     assert(out != NULL);
01452     *out << help_msg;
01453 }
01454 
01455 void server::stats_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01456     assert(out != NULL);
01457 
01458     *out << "Number of games = " << games_.size()
01459         << "\nTotal number of users = " << players_.size()
01460         << "\nNumber of users in the lobby = " << rooms_.lobby().size();
01461 }
01462 
01463 void server::metrics_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01464     assert(out != NULL);
01465     *out << metrics_;
01466 }
01467 
01468 void server::requests_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01469     assert(out != NULL);
01470     metrics_.requests(*out);
01471 }
01472 
01473 void server::games_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01474     assert(out != NULL);
01475     metrics_.games(*out);
01476 }
01477 
01478 void server::wml_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01479     assert(out != NULL);
01480     *out << simple_wml::document::stats();
01481 }
01482 
01483 void server::netstats_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01484     assert(out != NULL);
01485 
01486     network::pending_statistics stats = network::get_pending_stats();
01487     *out << "Network stats:\nPending send buffers: "
01488         << stats.npending_sends << "\nBytes in buffers: "
01489         << stats.nbytes_pending_sends << "\n";
01490 
01491     if (utils::lowercase(parameters) == "all") {
01492         *out << network::get_bandwidth_stats_all();
01493     } else {
01494         *out << network::get_bandwidth_stats(); // stats from previuos hour
01495     }
01496 }
01497 
01498 void server::adminmsg_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01499     assert(out != NULL);
01500 
01501     if (parameters == "") {
01502         *out << "You must type a message.";
01503         return;
01504     }
01505 
01506     const std::string& sender = issuer_name;
01507     const std::string& message = parameters;
01508     LOG_SERVER << "Admin message: <" << sender << (message.find("/me ") == 0
01509             ? std::string(message.begin() + 3, message.end()) + ">"
01510             : "> " + message) << "\n";
01511 
01512     simple_wml::document data;
01513     simple_wml::node& msg = data.root().add_child("whisper");
01514     msg.set_attr_dup("sender", ("admin message from " + sender).c_str());
01515     msg.set_attr_dup("message", message.c_str());
01516     int n = 0;
01517     for (wesnothd::player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
01518         if (pl->second.is_moderator()) {
01519             ++n;
01520             send_doc(data, pl->first);
01521         }
01522     }
01523 
01524     if (n == 0) {
01525         *out << "Sorry, no admin available right now. But your message got logged.";
01526         return;
01527     }
01528 
01529     *out << "Message sent to " << n << " admins.";
01530 }
01531 
01532 void server::pm_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01533     assert(out != NULL);
01534 
01535     std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
01536     if (first_space == parameters.end()) {
01537         *out << "You must name a receiver.";
01538         return;
01539     }
01540 
01541     const std::string& sender = issuer_name;
01542     const std::string receiver(parameters.begin(), first_space);
01543     std::string message(first_space + 1, parameters.end());
01544     utils::strip(message);
01545     if (message.empty()) {
01546         *out << "You must type a message.";
01547         return;
01548     }
01549 
01550     simple_wml::document data;
01551     simple_wml::node& msg = data.root().add_child("whisper");
01552     // This string is parsed by the client!
01553     msg.set_attr_dup("sender", ("server message from " + sender).c_str());
01554     msg.set_attr_dup("message", message.c_str());
01555     for (wesnothd::player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
01556         if (receiver != pl->second.name().c_str()) {
01557             continue;
01558         }
01559         send_doc(data, pl->first);
01560         *out << "Message to " << receiver << " successfully sent.";
01561         return;
01562     }
01563 
01564     *out << "No such nick: " << receiver;
01565 }
01566 
01567 void server::msg_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01568     assert(out != NULL);
01569 
01570     if (parameters == "") {
01571         *out << "You must type a message.";
01572         return;
01573     }
01574 
01575     rooms_.lobby().send_server_message_to_all(parameters);
01576     for (std::vector<wesnothd::game*>::const_iterator g = games_.begin(); g != games_.end(); ++g) {
01577         (*g)->send_server_message_to_all(parameters);
01578     }
01579 
01580     LOG_SERVER << "<server" << (parameters.find("/me ") == 0
01581             ? std::string(parameters.begin() + 3, parameters.end()) + ">"
01582             : "> " + parameters) << "\n";
01583 
01584     *out << "message '" << parameters << "' relayed to players";
01585 }
01586 
01587 void server::lobbymsg_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01588     assert(out != NULL);
01589 
01590     if (parameters == "") {
01591         *out << "You must type a message.";
01592         return;
01593     }
01594 
01595     rooms_.lobby().send_server_message_to_all(parameters);
01596     LOG_SERVER << "<server" << (parameters.find("/me ") == 0
01597             ? std::string(parameters.begin() + 3, parameters.end()) + ">"
01598             : "> " + parameters) << "\n";
01599 
01600     *out << "message '" << parameters << "' relayed to players";
01601 }
01602 
01603 void server::status_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01604     assert(out != NULL);
01605 
01606     *out << "STATUS REPORT for '" << parameters << "'";
01607     bool found_something = false;
01608     // If a simple username is given we'll check for its IP instead.
01609     if (utils::isvalid_username(parameters)) {
01610         for (wesnothd::player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
01611             if (parameters == pl->second.name()) {
01612                 parameters = network::ip_address(pl->first);
01613                 found_something = true;
01614                 break;
01615             }
01616         }
01617         if (!found_something) {
01618             //out << "\nNo match found. You may want to check with 'searchlog'.";
01619             //return out.str();
01620             *out << process_command("searchlog " + parameters, issuer_name);
01621             return;
01622         }
01623     }
01624     const bool match_ip = (std::count(parameters.begin(), parameters.end(), '.') >= 1);
01625     for (wesnothd::player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
01626         if (parameters == "" || parameters == "*"
01627         || (match_ip && utils::wildcard_string_match(network::ip_address(pl->first), parameters))
01628         || (!match_ip && utils::wildcard_string_match(pl->second.name(), parameters))) {
01629             found_something = true;
01630             *out << std::endl << player_status(pl);
01631         }
01632     }
01633     if (!found_something) *out << "\nNo match found. You may want to check with 'searchlog'.";
01634 }
01635 
01636 void server::clones_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
01637     assert(out != NULL);
01638 
01639     *out << "CLONES STATUS REPORT";
01640     std::set<std::string> clones;
01641     for (wesnothd::player_map::const_iterator pl = players_.begin(); pl != players_.end(); ++pl) {
01642         if (clones.find(network::ip_address(pl->first)) != clones.end()) continue;
01643         bool found = false;
01644         for (wesnothd::player_map::const_iterator clone = boost::next(pl); clone != players_.end(); ++clone) {
01645             if (network::ip_address(pl->first) == network::ip_address(clone->first)) {
01646                 if (!found) {
01647                     found = true;
01648                     clones.insert(network::ip_address(pl->first));
01649                     *out << std::endl << player_status(pl);
01650                 }
01651                 *out << std::endl << player_status(clone);
01652             }
01653         }
01654     }
01655     if (clones.empty()) {
01656         *out << "No clones found.";
01657     }
01658 }
01659 
01660 void server::bans_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01661     assert(out != NULL);
01662 
01663     if (parameters.empty()) {
01664         ban_manager_.list_bans(*out);
01665     } else if (utils::lowercase(parameters) == "deleted") {
01666         ban_manager_.list_deleted_bans(*out);
01667     } else if (utils::lowercase(parameters).find("deleted") == 0) {
01668         std::string mask = parameters.substr(7);
01669         ban_manager_.list_deleted_bans(*out, utils::strip(mask));
01670     } else {
01671         ban_manager_.list_bans(*out, utils::strip(parameters));
01672     }
01673 }
01674 
01675 void server::ban_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01676     assert(out != NULL);
01677 
01678     bool banned = false;
01679     std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
01680 
01681     if (first_space == parameters.end()) {
01682         *out << ban_manager_.get_ban_help();
01683         return;
01684     }
01685 
01686     std::string::iterator second_space = std::find(first_space + 1, parameters.end(), ' ');
01687     const std::string target(parameters.begin(), first_space);
01688 
01689     const std::string duration(first_space + 1, second_space);
01690     time_t parsed_time = time(NULL);
01691     if (ban_manager_.parse_time(duration, &parsed_time) == false) {
01692         *out << "Failed to parse the ban duration: '" << duration << "'\n"
01693             << ban_manager_.get_ban_help();
01694         return;
01695     }
01696 
01697     if (second_space == parameters.end()) {
01698         --second_space;
01699     }
01700     std::string reason(second_space + 1, parameters.end());
01701     utils::strip(reason);
01702     if (reason.empty()) {
01703         *out << "You need to give a reason for the ban.";
01704         return;
01705     }
01706 
01707     std::string dummy_group;
01708 
01709     // if we find a '.' consider it an ip mask
01710     /** @todo  FIXME: make a proper check for valid IPs. */
01711     if (std::count(target.begin(), target.end(), '.') >= 1) {
01712         banned = true;
01713 
01714         *out << ban_manager_.ban(target, parsed_time, reason, issuer_name, dummy_group);
01715     } else {
01716         for (wesnothd::player_map::const_iterator pl = players_.begin();
01717                 pl != players_.end(); ++pl)
01718         {
01719             if (utils::wildcard_string_match(pl->second.name(), target)) {
01720                 if (banned) *out << "\n";
01721                 else banned = true;
01722                 const std::string ip = network::ip_address(pl->first);
01723                 *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, dummy_group, target);
01724             }
01725         }
01726         if (!banned) {
01727             // If nobody was banned yet check the ip_log but only if a
01728             // simple username was used to prevent accidental bans.
01729             // @todo FIXME: since we can have several entries now we should only ban the latest or so
01730             /*if (utils::isvalid_username(target)) {
01731                 for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
01732                         i != ip_log_.end(); ++i) {
01733                     if (i->nick == target) {
01734                         if (banned) out << "\n";
01735                         else banned = true;
01736                         out << ban_manager_.ban(i->ip, parsed_time, reason, issuer_name, group, target);
01737                     }
01738                 }
01739             }*/
01740             if(!banned) {
01741                 *out << "Nickname mask '" << target << "' did not match, no bans set.";
01742             }
01743         }
01744     }
01745 }
01746 
01747 void server::kickban_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01748         assert(out != NULL);
01749 
01750         bool banned = false;
01751         std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
01752         if (first_space == parameters.end()) {
01753             *out << ban_manager_.get_ban_help();
01754             return;
01755         }
01756         std::string::iterator second_space = std::find(first_space + 1, parameters.end(), ' ');
01757         const std::string target(parameters.begin(), first_space);
01758         const std::string duration(first_space + 1, second_space);
01759         time_t parsed_time = time(NULL);
01760         if (ban_manager_.parse_time(duration, &parsed_time) == false) {
01761             *out << "Failed to parse the ban duration: '" << duration << "'\n"
01762                 << ban_manager_.get_ban_help();
01763             return;
01764         }
01765 
01766         if (second_space == parameters.end()) {
01767             --second_space;
01768         }
01769         std::string reason(second_space + 1, parameters.end());
01770         utils::strip(reason);
01771         if (reason.empty()) {
01772             *out << "You need to give a reason for the ban.";
01773             return;
01774         }
01775 
01776         std::string dummy_group;
01777 
01778         // if we find a '.' consider it an ip mask
01779         /** @todo  FIXME: make a proper check for valid IPs. */
01780         if (std::count(target.begin(), target.end(), '.') >= 1) {
01781             banned = true;
01782 
01783             *out << ban_manager_.ban(target, parsed_time, reason, issuer_name, dummy_group);
01784 
01785             for (wesnothd::player_map::const_iterator pl = players_.begin();
01786                     pl != players_.end(); ++pl)
01787             {
01788                 if (utils::wildcard_string_match(network::ip_address(pl->first), target)) {
01789                     *out << "\nKicked " << pl->second.name() << " ("
01790                         << network::ip_address(pl->first) << ").";
01791                     send_error(pl->first, "You have been banned. Reason: " + reason);
01792                     network::queue_disconnect(pl->first);
01793                 }
01794             }
01795         } else {
01796             for (wesnothd::player_map::const_iterator pl = players_.begin();
01797                     pl != players_.end(); ++pl)
01798             {
01799                 if (utils::wildcard_string_match(pl->second.name(), target)) {
01800                     if (banned) *out << "\n";
01801                     else banned = true;
01802                     const std::string ip = network::ip_address(pl->first);
01803                     *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, dummy_group, target);
01804                     *out << "\nKicked " << pl->second.name() << " (" << ip << ").";
01805                     send_error(pl->first, "You have been banned. Reason: " + reason);
01806                     network::queue_disconnect(pl->first);
01807                 }
01808             }
01809             if (!banned) {
01810                 // If nobody was banned yet check the ip_log but only if a
01811                 // simple username was used to prevent accidental bans.
01812                 // @todo FIXME: since we can have several entries now we should only ban the latest or so
01813                 /*if (utils::isvalid_username(target)) {
01814                     for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
01815                             i != ip_log_.end(); ++i) {
01816                         if (i->nick == target) {
01817                             if (banned) out << "\n";
01818                             else banned = true;
01819                             out << ban_manager_.ban(i->ip, parsed_time, reason, issuer_name, group, target);
01820                         }
01821                     }
01822                 }*/
01823                 if(!banned) {
01824                     *out << "Nickname mask '" << target << "' did not match, no bans set.";
01825                 }
01826             }
01827         }
01828     }
01829 
01830 void server::gban_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01831             assert(out != NULL);
01832 
01833             bool banned = false;
01834             std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
01835             if (first_space == parameters.end()) {
01836                 *out << ban_manager_.get_ban_help();
01837                 return;
01838             }
01839             std::string::iterator second_space = std::find(first_space + 1, parameters.end(), ' ');
01840             const std::string target(parameters.begin(), first_space);
01841 
01842             std::string group = std::string(first_space + 1, second_space);
01843             first_space = second_space;
01844             second_space = std::find(first_space + 1, parameters.end(), ' ');
01845 
01846             const std::string duration(first_space + 1, second_space);
01847             time_t parsed_time = time(NULL);
01848             if (ban_manager_.parse_time(duration, &parsed_time) == false) {
01849                 *out << "Failed to parse the ban duration: '" << duration << "'\n"
01850                     << ban_manager_.get_ban_help();
01851                 return;
01852             }
01853 
01854             if (second_space == parameters.end()) {
01855                 --second_space;
01856             }
01857             std::string reason(second_space + 1, parameters.end());
01858             utils::strip(reason);
01859             if (reason.empty()) {
01860                 *out << "You need to give a reason for the ban.";
01861                 return;
01862             }
01863 
01864             // if we find a '.' consider it an ip mask
01865             /** @todo  FIXME: make a proper check for valid IPs. */
01866             if (std::count(target.begin(), target.end(), '.') >= 1) {
01867                 banned = true;
01868 
01869                 *out << ban_manager_.ban(target, parsed_time, reason, issuer_name, group);
01870             } else {
01871                 for (wesnothd::player_map::const_iterator pl = players_.begin();
01872                         pl != players_.end(); ++pl)
01873                 {
01874                     if (utils::wildcard_string_match(pl->second.name(), target)) {
01875                         if (banned) *out << "\n";
01876                         else banned = true;
01877                         const std::string ip = network::ip_address(pl->first);
01878                         *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, group, target);
01879                     }
01880                 }
01881                 if (!banned) {
01882                     // If nobody was banned yet check the ip_log but only if a
01883                     // simple username was used to prevent accidental bans.
01884                     // @todo FIXME: since we can have several entries now we should only ban the latest or so
01885                     /*if (utils::isvalid_username(target)) {
01886                         for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
01887                                 i != ip_log_.end(); ++i) {
01888                             if (i->nick == target) {
01889                                 if (banned) out << "\n";
01890                                 else banned = true;
01891                                 out << ban_manager_.ban(i->ip, parsed_time, reason, issuer_name, group, target);
01892                             }
01893                         }
01894                     }*/
01895                     if(!banned) {
01896                         *out << "Nickname mask '" << target << "' did not match, no bans set.";
01897                     }
01898                 }
01899             }
01900         }
01901 
01902 void server::unban_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01903     assert(out != NULL);
01904 
01905     if (parameters == "") {
01906         *out << "You must enter an ipmask to unban.";
01907         return;
01908     }
01909     ban_manager_.unban(*out, parameters);
01910 }
01911 
01912 void server::ungban_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01913     assert(out != NULL);
01914 
01915     if (parameters == "") {
01916         *out << "You must enter an ipmask to ungban.";
01917         return;
01918     }
01919     ban_manager_.unban_group(*out, parameters);
01920 }
01921 
01922 void server::kick_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01923     assert(out != NULL);
01924 
01925     if (parameters == "") {
01926         *out <<  "You must enter a mask to kick.";
01927         return;
01928     }
01929     std::string::iterator i = std::find(parameters.begin(), parameters.end(), ' ');
01930     const std::string kick_mask = std::string(parameters.begin(), i);
01931     const std::string kick_message =
01932             (i == parameters.end() ? "You have been kicked."
01933             : "You have been kicked. Reason: " + std::string(i + 1, parameters.end()));
01934     bool kicked = false;
01935     // if we find a '.' consider it an ip mask
01936     const bool match_ip = (std::count(kick_mask.begin(), kick_mask.end(), '.') >= 1);
01937     for (wesnothd::player_map::const_iterator pl = players_.begin();
01938         pl != players_.end(); ++pl)
01939     {
01940         if ((match_ip && utils::wildcard_string_match(network::ip_address(pl->first), kick_mask))
01941         || (!match_ip && utils::wildcard_string_match(pl->second.name(), kick_mask))) {
01942             if (kicked) *out << "\n";
01943             else kicked = true;
01944             *out << "Kicked " << pl->second.name() << " ("
01945                 << network::ip_address(pl->first) << "). '"
01946                 << kick_message << "'";
01947             send_error(pl->first, kick_message);
01948             network::queue_disconnect(pl->first);
01949         }
01950     }
01951     if (!kicked) *out << "No user matched '" << kick_mask << "'.";
01952 }
01953 
01954 void server::motd_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01955     assert(out != NULL);
01956 
01957     if (parameters == "") {
01958         if (motd_ != "") {
01959             *out << "Message of the day:\n" << motd_;
01960             return;
01961         } else {
01962             *out << "No message of the day set.";
01963             return;
01964         }
01965     }
01966 
01967     motd_ = parameters;
01968     *out << "Message of the day set to: " << motd_;
01969 }
01970 
01971 void server::searchlog_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
01972     assert(out != NULL);
01973 
01974     if (parameters.empty()) {
01975         *out << "You must enter a mask to search for.";
01976         return;
01977     }
01978     *out << "IP/NICK LOG for '" << parameters << "'";
01979 
01980     bool found_something = false;
01981 
01982     // If this looks like an IP look up which nicks have been connected from it
01983     // Otherwise look for the last IP the nick used to connect
01984     const bool match_ip = (std::count(parameters.begin(), parameters.end(), '.') >= 1);
01985     for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
01986             i != ip_log_.end(); ++i) {
01987         const std::string& username = i->nick;
01988         const std::string& ip = i->ip;
01989         if ((match_ip && utils::wildcard_string_match(ip, parameters))
01990         || (!match_ip && utils::wildcard_string_match(username, parameters))) {
01991             found_something = true;
01992             wesnothd::player_map::const_iterator pl = std::find_if(players_.begin(), players_.end(), boost::bind(&::match_user, _1, username, ip));
01993             if (pl != players_.end()) {
01994                 *out << std::endl << player_status(pl);
01995             } else {
01996                 *out << "\n'" << username << "' @ " << ip << " last seen: " << lg::get_timestamp(i->log_off, "%H:%M:%S %d.%m.%Y");
01997             }
01998         }
01999     }
02000     if (!found_something) *out << "\nNo match found.";
02001 }
02002 
02003 void server::dul_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
02004     assert(out != NULL);
02005 
02006     if (parameters == "") {
02007         *out << "Unregistered login is " << (deny_unregistered_login_ ? "disallowed" : "allowed") << ".";
02008     } else {
02009         deny_unregistered_login_ = (utils::lowercase(parameters) == "yes");
02010         *out << "Unregistered login is now " << (deny_unregistered_login_ ? "disallowed" : "allowed") << ".";
02011     }
02012 }
02013 
02014 void server::process_nickserv(const network::connection sock, simple_wml::node& data) {
02015     const wesnothd::player_map::iterator pl = players_.find(sock);
02016     if (pl == players_.end()) {
02017         DBG_SERVER << "ERROR: Could not find player with socket: " << sock << "\n";
02018         return;
02019     }
02020 
02021     // Check if this server allows nick registration at all
02022     if(!user_handler_) {
02023         rooms_.lobby().send_server_message("This server does not allow username registration.", sock);
02024         return;
02025     }
02026 
02027     if(data.child("register")) {
02028         try {
02029             (user_handler_->add_user(pl->second.name(), (*data.child("register"))["mail"].to_string(),
02030                 (*data.child("register"))["password"].to_string()));
02031 
02032             std::stringstream msg;
02033             msg << "Your username has been registered." <<
02034                     // Warn that providing an email address might be a good idea
02035                     ((*data.child("register"))["mail"].empty() ?
02036                     " It is recommended that you provide an email address for password recovery." : "");
02037             rooms_.lobby().send_server_message(msg.str(), sock);
02038 
02039             // Mark the player as registered and send the other clients
02040             // an update to dislpay this change
02041             pl->second.mark_registered();
02042 
02043             simple_wml::document diff;
02044             make_change_diff(games_and_users_list_.root(), NULL,
02045                          "user", pl->second.config_address(), diff);
02046             rooms_.lobby().send_data(diff);
02047 
02048         } catch (user_handler::error& e) {
02049             rooms_.lobby().send_server_message("There was an error registering your username. The error message was: "
02050             + e.message, sock);
02051         }
02052         return;
02053     }
02054 
02055     // A user requested to update his password or mail
02056     if(data.child("set")) {
02057         if(!(user_handler_->user_exists(pl->second.name()))) {
02058             rooms_.lobby().send_server_message("You are not registered. Please register first.", sock);
02059             return;
02060         }
02061 
02062         const simple_wml::node& set = *(data.child("set"));
02063 
02064         try {
02065             user_handler_->set_user_detail(pl->second.name(), set["detail"].to_string(), set["value"].to_string());
02066 
02067             rooms_.lobby().send_server_message("Your details have been updated.", sock);
02068 
02069         } catch (user_handler::error& e) {
02070             rooms_.lobby().send_server_message("There was an error updating your details. The error message was: "
02071             + e.message, sock);
02072         }
02073 
02074         return;
02075     }
02076 
02077     // A user requested information about another user
02078     if(data.child("details")) {
02079         rooms_.lobby().send_server_message("Valid details for this server are: " +
02080                 user_handler_->get_valid_details(), sock);
02081         return;
02082     }
02083 
02084     // A user requested a list of which details can be set
02085     if(data.child("info")) {
02086         try {
02087             std::string res = user_handler_->user_info((*data.child("info"))["name"].to_string());
02088             rooms_.lobby().send_server_message(res, sock);
02089         } catch (user_handler::error& e) {
02090             rooms_.lobby().send_server_message("There was an error looking up the details of the user '" +
02091             (*data.child("info"))["name"].to_string() + "'. " +" The error message was: "
02092             + e.message, sock);
02093         }
02094         return;
02095     }
02096 
02097     // A user requested to delete his nick
02098     if(data.child("drop")) {
02099         if(!(user_handler_->user_exists(pl->second.name()))) {
02100             rooms_.lobby().send_server_message("You are not registered.", sock);
02101             return;
02102         }
02103 
02104         // With the current policy of dissallowing to log in with a
02105         // registerd username without the password we should never get
02106         // to call this
02107         if(!(pl->second.registered())) {
02108             rooms_.lobby().send_server_message("You are not logged in.", sock);
02109             return;
02110         }
02111 
02112         try {
02113             user_handler_->remove_user(pl->second.name());
02114             rooms_.lobby().send_server_message("Your username has been dropped.", sock);
02115 
02116             // Mark the player as not registered and send the other clients
02117             // an update to dislpay this change
02118             pl->second.mark_registered(false);
02119 
02120             simple_wml::document diff;
02121             make_change_diff(games_and_users_list_.root(), NULL,
02122                          "user", pl->second.config_address(), diff);
02123             rooms_.lobby().send_data(diff);
02124         } catch (user_handler::error& e) {
02125             rooms_.lobby().send_server_message("There was an error dropping your username. The error message was: "
02126             + e.message, sock);
02127         }
02128         return;
02129     }
02130 }
02131 
02132 void server::process_whisper(const network::connection sock,
02133                              simple_wml::node& whisper) const {
02134     if ((whisper["receiver"] == "") || (whisper["message"] == "")) {
02135         static simple_wml::document data(
02136           "[message]\n"
02137           "message=\"Invalid number of arguments\"\n"
02138           "sender=\"server\"\n"
02139           "[/message]\n", simple_wml::INIT_COMPRESSED);
02140         send_doc(data, sock);
02141         return;
02142     }
02143     const wesnothd::player_map::const_iterator pl = players_.find(sock);
02144     if (pl == players_.end()) {
02145         ERR_SERVER << "ERROR: Could not find whispering player. (socket: "
02146             << sock << ")\n";
02147         return;
02148     }
02149     whisper.set_attr_dup("sender", pl->second.name().c_str());
02150     bool dont_send = false;
02151     const simple_wml::string_span& whisper_receiver = whisper["receiver"];
02152     for (wesnothd::player_map::const_iterator i = players_.begin(); i != players_.end(); ++i) {
02153         if (whisper_receiver != i->second.name().c_str()) {
02154             continue;
02155         }
02156 
02157         std::vector<wesnothd::game*>::const_iterator g;
02158         for (g = games_.begin(); g != games_.end(); ++g) {
02159             if (!(*g)->is_member(i->first)) continue;
02160             // Don't send to players in a running game the sender is part of.
02161             dont_send = ((*g)->started() && (*g)->is_player(i->first) && (*g)->is_member(sock));
02162             break;
02163         }
02164         if (dont_send) {
02165             break;
02166         }
02167 
02168         simple_wml::document cwhisper;
02169         whisper.copy_into(cwhisper.root().add_child("whisper"));
02170         send_doc(cwhisper, i->first);
02171         return;
02172     }
02173 
02174     simple_wml::document data;
02175     simple_wml::node& msg = data.root().add_child("message");
02176 
02177     if (dont_send) {
02178         msg.set_attr("message", "You cannot send private messages to players in a running game you observe.");
02179     } else {
02180         msg.set_attr_dup("message", ("Can't find '" + whisper["receiver"].to_string() + "'.").c_str());
02181     }
02182 
02183     msg.set_attr("sender", "server");
02184     send_doc(data, sock);
02185 }
02186 
02187 void server::process_data_lobby(const network::connection sock,
02188                                 simple_wml::document& data) {
02189     DBG_SERVER << "in process_data_lobby...\n";
02190 
02191     const wesnothd::player_map::iterator pl = players_.find(sock);
02192     if (pl == players_.end()) {
02193         ERR_SERVER << "ERROR: Could not find player in players_. (socket: "
02194             << sock << ")\n";
02195         return;
02196     }
02197 
02198     if (const simple_wml::node* create_game = data.child("create_game")) {
02199         if (graceful_restart) {
02200             static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
02201             send_doc(leave_game_doc, sock);
02202             rooms_.lobby().send_server_message("This server is shutting down. You aren't allowed to make new games. Please reconnect to the new server.", sock);
02203             send_doc(games_and_users_list_, sock);
02204             return;
02205         }
02206         const std::string game_name = (*create_game)["name"].to_string();
02207         const std::string game_password = (*create_game)["password"].to_string();
02208         DBG_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02209             << "\tcreates a new game: \"" << game_name << "\".\n";
02210         // Create the new game, remove the player from the lobby
02211         // and set the player as the host/owner.
02212         games_.push_back(new wesnothd::game(players_, sock, game_name, save_replays_, replay_save_path_));
02213         wesnothd::game& g = *games_.back();
02214         if(game_password.empty() == false) {
02215             g.set_password(game_password);
02216         }
02217 
02218         create_game->copy_into(g.level().root());
02219         rooms_.exit_lobby(sock);
02220         simple_wml::document diff;
02221         if(make_change_diff(games_and_users_list_.root(), NULL,
02222                             "user", pl->second.config_address(), diff)) {
02223             rooms_.lobby().send_data(diff);
02224         }
02225         return;
02226     }
02227 
02228     // See if the player is joining a game
02229     if (const simple_wml::node* join = data.child("join")) {
02230         const bool observer = join->attr("observe").to_bool();
02231         const std::string& password = (*join)["password"].to_string();
02232         int game_id = (*join)["id"].to_int();
02233 
02234         const std::vector<wesnothd::game*>::iterator g =
02235             std::find_if(games_.begin(), games_.end(), wesnothd::game_id_matches(game_id));
02236 
02237         static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
02238         if (g == games_.end()) {
02239             WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02240                 << "\tattempted to join unknown game:\t" << game_id << ".\n";
02241             send_doc(leave_game_doc, sock);
02242             rooms_.lobby().send_server_message("Attempt to join unknown game.", sock);
02243             send_doc(games_and_users_list_, sock);
02244             return;
02245         } else if (!(*g)->level_init()) {
02246             WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02247                 << "\tattempted to join uninitialized game:\t\"" << (*g)->name()
02248                 << "\" (" << game_id << ").\n";
02249             send_doc(leave_game_doc, sock);
02250             rooms_.lobby().send_server_message("Attempt to join an uninitialized game.", sock);
02251             send_doc(games_and_users_list_, sock);
02252             return;
02253         } else if (pl->second.is_moderator()) {
02254             // Admins are always allowed to join.
02255         } else if ((*g)->player_is_banned(sock)) {
02256             DBG_SERVER << network::ip_address(sock) << "\tReject banned player: "
02257                 << pl->second.name() << "\tfrom game:\t\"" << (*g)->name()
02258                 << "\" (" << game_id << ").\n";
02259             send_doc(leave_game_doc, sock);
02260             rooms_.lobby().send_server_message("You are banned from this game.", sock);
02261             send_doc(games_and_users_list_, sock);
02262             return;
02263         } else if(!observer && !(*g)->password_matches(password)) {
02264             WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02265                 << "\tattempted to join game:\t\"" << (*g)->name() << "\" ("
02266                 << game_id << ") with bad password\n";
02267             send_doc(leave_game_doc, sock);
02268             rooms_.lobby().send_server_message("Incorrect password.", sock);
02269             send_doc(games_and_users_list_, sock);
02270             return;
02271         }
02272         // Hack to work around problems of players not getting properly removed
02273         // from a game. Still need to figure out actual cause...
02274         const std::vector<wesnothd::game*>::iterator g2 =
02275             std::find_if(games_.begin(), games_.end(), wesnothd::game_is_member(sock));
02276         if (g2 != games_.end()) {
02277             WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02278                 << "\tattempted to join a second game. He's already in game:\t\""
02279                 << (*g2)->name() << "\" (" << (*g2)->id()
02280                 << ") and the lobby. (socket: " << sock << ")\n"
02281                 << "Removing him from that game to fix the inconsistency...\n";
02282             (*g2)->remove_player(sock);
02283         }
02284         bool joined = (*g)->add_player(sock, observer);
02285         if (!joined) {
02286             WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02287                 << "\tattempted to observe game:\t\"" << (*g)->name() << "\" ("
02288                 << game_id << ") which doesn't allow observers.\n";
02289             send_doc(leave_game_doc, sock);
02290             rooms_.lobby().send_server_message("Attempt to observe a game that doesn't allow observers. (You probably joined the game shortly after it filled up.)", sock);
02291             send_doc(games_and_users_list_, sock);
02292             return;
02293         }
02294         rooms_.exit_lobby(sock);
02295         (*g)->describe_slots();
02296 
02297         //send notification of changes to the game and user
02298         simple_wml::document diff;
02299         bool diff1 = make_change_diff(*games_and_users_list_.child("gamelist"),
02300                           "gamelist", "game", (*g)->description(), diff);
02301         bool diff2 = make_change_diff(games_and_users_list_.root(), NULL,
02302                           "user", pl->second.config_address(), diff);
02303         if (diff1 || diff2) {
02304             rooms_.lobby().send_data(diff);
02305         }
02306     }
02307 
02308     if (data.child("room_join")) {
02309         rooms_.process_room_join(data, pl);
02310     }
02311 
02312     if (data.child("room_part")) {
02313         rooms_.process_room_part(data, pl);
02314     }
02315 
02316     if (data.child("room_query")) {
02317         rooms_.process_room_query(data, pl);
02318     }
02319 
02320     // See if it's a message, in which case we add the name of the sender,
02321     // and forward it to all players in the lobby.
02322     if (data.child("message")) {
02323         rooms_.process_message(data, pl);
02324     }
02325 
02326     // Player requests update of lobby content,
02327     // for example when cancelling the create game dialog
02328     if (data.child("refresh_lobby")) {
02329         send_doc(games_and_users_list_, sock);
02330     }
02331 }
02332 
02333 /**
02334  * Process data sent from a member of a game.
02335  */
02336 void server::process_data_game(const network::connection sock,
02337                                simple_wml::document& data) {
02338     DBG_SERVER << "in process_data_game...\n";
02339 
02340     const wesnothd::player_map::iterator pl = players_.find(sock);
02341     if (pl == players_.end()) {
02342         ERR_SERVER << "ERROR: Could not find player in players_. (socket: "
02343             << sock << ")\n";
02344         return;
02345     }
02346 
02347     const std::vector<wesnothd::game*>::iterator itor =
02348         std::find_if(games_.begin(), games_.end(), wesnothd::game_is_member(sock));
02349     if (itor == games_.end()) {
02350         ERR_SERVER << "ERROR: Could not find game for player: "
02351             << pl->second.name() << ". (socket: " << sock << ")\n";
02352         return;
02353     }
02354 
02355     wesnothd::game* g = *itor;
02356 
02357     // If this is data describing the level for a game.
02358     if (data.child("side")) {
02359         if (!g->is_owner(sock)) {
02360             return;
02361         }
02362         size_t nsides = 0;
02363         const simple_wml::node::child_list& sides = data.root().children("side");
02364         for (simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
02365             ++nsides;
02366         }
02367         if (nsides > gamemap::MAX_PLAYERS) {
02368             delete_game(itor);
02369             std::stringstream msg;
02370             msg << "This server does not support games with more than "
02371                 << gamemap::MAX_PLAYERS << " sides. Game aborted.";
02372             rooms_.lobby().send_server_message(msg.str(), sock);
02373             return;
02374         }
02375 
02376         // If this game is having its level data initialized
02377         // for the first time, and is ready for players to join.
02378         // We should currently have a summary of the game in g->level().
02379         // We want to move this summary to the games_and_users_list_, and
02380         // place a pointer to that summary in the game's description.
02381         // g->level() should then receive the full data for the game.
02382         if (!g->level_init()) {
02383             LOG_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02384                 << "\tcreated game:\t\"" << g->name() << "\" ("
02385                 << g->id() << ").\n";
02386             // Update our config object which describes the open games,
02387             // and save a pointer to the description in the new game.
02388             simple_wml::node* const gamelist = games_and_users_list_.child("gamelist");
02389             assert(gamelist != NULL);
02390             simple_wml::node& desc = gamelist->add_child("game");
02391             g->level().root().copy_into(desc);
02392             if (const simple_wml::node* m = data.child("multiplayer")) {
02393                 m->copy_into(desc);
02394             } else {
02395                 WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02396                     << "\tsent scenario data in game:\t\"" << g->name() << "\" ("
02397                     << g->id() << ") without a 'multiplayer' child.\n";
02398                 // Set the description so it can be removed in delete_game().
02399                 g->set_description(&desc);
02400                 delete_game(itor);
02401                 rooms_.lobby().send_server_message("The scenario data is missing the [multiplayer] tag which contains the game settings. Game aborted.", sock);
02402                 return;
02403             }
02404             g->set_description(&desc);
02405             desc.set_attr_dup("id", lexical_cast<std::string>(g->id()).c_str());
02406         } else {
02407             WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02408                 << "\tsent scenario data in game:\t\"" << g->name() << "\" ("
02409                 << g->id() << ") although it's already initialized.\n";
02410             return;
02411         }
02412 
02413         assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
02414 
02415         simple_wml::node& desc = *g->description();
02416         // Update the game's description.
02417         // If there is no shroud, then tell players in the lobby
02418         // what the map looks like
02419         if (!data["mp_shroud"].to_bool()) {
02420             desc.set_attr_dup("map_data", data["map_data"]);
02421         }
02422         if (const simple_wml::node* e = data.child("era")) {
02423             if (!e->attr("require_era").to_bool(true)) {
02424                 desc.set_attr("require_era", "no");
02425             }
02426         }
02427 
02428         // Record the full scenario in g->level()
02429         g->level().swap(data);
02430         // The host already put himself in the scenario so we just need
02431         // to update_side_data().
02432         //g->take_side(sock);
02433         g->update_side_data();
02434         g->describe_slots();
02435 
02436         assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
02437 
02438         // Send the update of the game description to the lobby.
02439         simple_wml::document diff;
02440         make_add_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", diff);
02441         rooms_.lobby().send_data(diff);
02442 
02443         /** @todo FIXME: Why not save the level data in the history_? */
02444         return;
02445 // Everything below should only be processed if the game is already intialized.
02446     } else if (!g->level_init()) {
02447         WRN_SERVER << network::ip_address(sock) << "\tReceived unknown data from: "
02448             << pl->second.name() << " (socket:" << sock
02449             << ") while the scenario wasn't yet initialized.\n" << data.output();
02450         return;
02451     // If the host is sending the next scenario data.
02452     } else if (const simple_wml::node* scenario = data.child("store_next_scenario")) {
02453         if (!g->is_owner(sock)) return;
02454         if (!g->level_init()) {
02455             WRN_SERVER << network::ip_address(sock) << "\tWarning: "
02456                 << pl->second.name() << "\tsent [store_next_scenario] in game:\t\""
02457                 << g->name() << "\" (" << g->id()
02458                 << ") while the scenario is not yet initialized.";
02459             return;
02460         }
02461         g->save_replay();
02462         size_t nsides = 0;
02463         const simple_wml::node::child_list& sides = scenario->children("side");
02464         for (simple_wml::node::child_list::const_iterator s = sides.begin(); s != sides.end(); ++s) {
02465                 ++nsides;
02466         }
02467         if (nsides > gamemap::MAX_PLAYERS) {
02468             delete_game(itor);
02469             std::stringstream msg;
02470             msg << "This server does not support games with more than "
02471                 << gamemap::MAX_PLAYERS << " sides.";
02472             rooms_.lobby().send_server_message(msg.str(), sock);
02473             return;
02474         }
02475         // Record the full scenario in g->level()
02476         g->level().clear();
02477         scenario->copy_into(g->level().root());
02478 
02479         if (g->description() == NULL) {
02480             ERR_SERVER << network::ip_address(sock) << "\tERROR: \""
02481                 << g->name() << "\" (" << g->id()
02482                 << ") is initialized but has no description_.\n";
02483             return;
02484         }
02485         simple_wml::node& desc = *g->description();
02486         // Update the game's description.
02487         if (const simple_wml::node* m = scenario->child("multiplayer")) {
02488             m->copy_into(desc);
02489         } else {
02490             WRN_SERVER << network::ip_address(sock) << "\t" << pl->second.name()
02491                 << "\tsent scenario data in game:\t\"" << g->name() << "\" ("
02492                 << g->id() << ") without a 'multiplayer' child.\n";
02493             delete_game(itor);
02494             rooms_.lobby().send_server_message("The scenario data is missing the [multiplayer] tag which contains the game settings. Game aborted.", sock);
02495             return;
02496         }
02497         // If there is no shroud, then tell players in the lobby
02498         // what the map looks like.
02499         const simple_wml::node& s = g->level().root();
02500         if (s["mp_shroud"].to_bool()) {
02501             desc.set_attr_dup("map_data", s["map_data"]);
02502         } else {
02503             desc.set_attr("map_data", "");
02504         }
02505         if (const simple_wml::node* e = data.child("era")) {
02506             if (!e->attr("require_era").to_bool(true)) {
02507                 desc.set_attr("require_era", "no");
02508             }
02509         }
02510         // Send the update of the game description to the lobby.
02511         update_game_in_lobby(g);
02512 
02513         g->start_game(pl);
02514         return;
02515     // If a player advances to the next scenario of a mp campaign. (deprecated)
02516     ///@deprecated r22619 a player advances to the next scenario of a mp campaign (notify_next_scenario)
02517     } else if(data.child("notify_next_scenario")) {
02518         //g->send_data(g->construct_server_message(pl->second.name()
02519         //      + " advanced to the next scenario."), sock);
02520         return;
02521     // A mp client sends a request for the next scenario of a mp campaign.
02522     } else if (data.child("load_next_scenario")) {
02523         g->load_next_scenario(pl);
02524         return;
02525     } else if (data.child("start_game")) {
02526         if (!g->is_owner(sock)) return;
02527         // Send notification of the game starting immediately.
02528         // g->start_game() will send data that assumes
02529         // the [start_game] message has been sent
02530         g->send_data(data, sock);
02531         g->start_game(pl);
02532 
02533         //update the game having changed in the lobby
02534         update_game_in_lobby(g);
02535         return;
02536     } else if (data.child("leave_game")) {
02537         // May be better to just let remove_player() figure out when a game ends.
02538         if ((g->is_player(sock) && g->nplayers() == 1)
02539         || (g->is_owner(sock) && (!g->started() || g->nplayers() == 0))) {
02540             // Remove the player in delete_game() with all other remaining
02541             // ones so he gets the updated gamelist.
02542             delete_game(itor);
02543         } else {
02544             g->remove_player(sock);
02545             rooms_.enter_lobby(sock);
02546             g->describe_slots();
02547 
02548             // Send all other players in the lobby the update to the gamelist.
02549             simple_wml::document diff;
02550             bool diff1 = make_change_diff(*games_and_users_list_.child("gamelist"),
02551                               "gamelist", "game", g->description(), diff);
02552             bool diff2 = make_change_diff(games_and_users_list_.root(), NULL,
02553                               "user", pl->second.config_address(), diff);
02554             if (diff1 || diff2) {
02555                 rooms_.lobby().send_data(diff, sock);
02556             }
02557 
02558             // Send the player who has quit the gamelist.
02559             send_doc(games_and_users_list_, sock);
02560         }
02561         return;
02562     // If this is data describing side changes by the host.
02563     } else if (const simple_wml::node* diff = data.child("scenario_diff")) {
02564         if (!g->is_owner(sock)) return;
02565         g->level().root().apply_diff(*diff);
02566         const simple_wml::node* cfg_change = diff->child("change_child");
02567         if (cfg_change && cfg_change->child("side")) {
02568             g->update_side_data();
02569         }
02570         if (g->describe_slots()) {
02571             update_game_in_lobby(g);
02572         }
02573         g->send_data(data, sock);
02574         return;
02575     // If a player changes his faction.
02576     } else if (data.child("change_faction")) {
02577         g->send_data(data, sock);
02578         return;
02579     // If the owner of a side is changing the controller.
02580     } else if (const simple_wml::node *change = data.child("change_controller")) {
02581         g->transfer_side_control(sock, *change);
02582         if (g->describe_slots()) {
02583             update_game_in_lobby(g);
02584         }
02585         // FIXME: Why not save it in the history_? (if successful)
02586         return;
02587     // If all observers should be muted. (toggles)
02588     } else if (data.child("muteall")) {
02589         if (!g->is_owner(sock)) {
02590             g->send_server_message("You cannot mute: not the game host.", sock);
02591             return;
02592         }
02593         g->mute_all_observers();
02594         return;
02595     // If an observer should be muted.
02596     } else if (const simple_wml::node* mute = data.child("mute")) {
02597         g->mute_observer(*mute, pl);
02598         return;
02599     // If an observer should be unmuted.
02600     } else if (const simple_wml::node* unmute = data.child("unmute")) {
02601         g->unmute_observer(*unmute, pl);
02602         return;
02603     // The owner is kicking/banning someone from the game.
02604     } else if (data.child("kick") || data.child("ban")) {
02605         bool ban = (data.child("ban") != NULL);
02606         const network::connection user =
02607                 (ban ? g->ban_user(*data.child("ban"), pl)
02608                 : g->kick_member(*data.child("kick"), pl));
02609         if (user) {
02610             rooms_.enter_lobby(user);
02611             if (g->describe_slots()) {
02612                 update_game_in_lobby(g, user);
02613             }
02614             // Send all other players in the lobby the update to the gamelist.
02615             simple_wml::document diff;
02616             make_change_diff(*games_and_users_list_.child("gamelist"),
02617                               "gamelist", "game", g->description(), diff);
02618             const wesnothd::player_map::iterator pl2 = players_.find(user);
02619             if (pl2 == players_.end()) {
02620                 ERR_SERVER << "ERROR: Could not find kicked player in players_."
02621                 " (socket: " << user << ")\n";
02622             } else {
02623                 make_change_diff(games_and_users_list_.root(), NULL, "user",
02624                         pl2->second.config_address(), diff);
02625             }
02626             rooms_.lobby().send_data(diff, sock);
02627             // Send the removed user the lobby game list.
02628             send_doc(games_and_users_list_, user);
02629         }
02630         return;
02631     } else if (const simple_wml::node* unban = data.child("unban")) {
02632         g->unban_user(*unban, pl);
02633         return;
02634     // If info is being provided about the game state.
02635     } else if (const simple_wml::node* info = data.child("info")) {
02636         if (!g->is_player(sock)) return;
02637         if ((*info)["type"] == "termination") {
02638             g->set_termination_reason((*info)["condition"].to_string());
02639             if ((*info)["condition"].to_string() == "out of sync") {
02640                 g->send_server_message_to_all(pl->second.name() + " reports out of sync errors.");
02641             }
02642         }
02643         return;
02644     } else if (data.child("turn")) {
02645         // Notify the game of the commands, and if it changes
02646         // the description, then sync the new description
02647         // to players in the lobby.
02648         if (g->process_turn(data, pl)) {
02649             update_game_in_lobby(g);
02650         }
02651         return;
02652     } else if (data.child("whiteboard")) {
02653         g->process_whiteboard(data,pl);
02654         return;
02655     } else if (data.child("message")) {
02656         g->process_message(data, pl);
02657         return;
02658     } else if (data.child("stop_updates")) {
02659         g->send_data(data, sock);
02660         return;
02661     } else if (data.child("wait_global")) {
02662         g->allow_global(data);
02663         return;
02664     // Data to ignore.
02665     } else if (data.child("error")
02666     || data.child("side_secured")
02667     || data.root().has_attr("failed")
02668     || data.root().has_attr("side_drop")
02669     || data.root().has_attr("side")) {
02670         return;
02671     }
02672 
02673     WRN_SERVER << network::ip_address(sock) << "\tReceived unknown data from: "
02674         << pl->second.name() << " (socket:" << sock << ") in game: \""
02675         << g->name() << "\" (" << g->id() << ")\n" << data.output();
02676 }
02677 
02678 void server::delete_game(std::vector<wesnothd::game*>::iterator game_it) {
02679     metrics_.game_terminated((*game_it)->termination_reason());
02680 
02681     simple_wml::node* const gamelist = games_and_users_list_.child("gamelist");
02682     assert(gamelist != NULL);
02683 
02684     // Send a diff of the gamelist with the game deleted to players in the lobby
02685     simple_wml::document diff;
02686     if(make_delete_diff(*gamelist, "gamelist", "game",
02687                         (*game_it)->description(), diff)) {
02688         rooms_.lobby().send_data(diff);
02689     }
02690 
02691     // Delete the game from the games_and_users_list_.
02692     const simple_wml::node::child_list& games = gamelist->children("game");
02693     const simple_wml::node::child_list::const_iterator g =
02694         std::find(games.begin(), games.end(), (*game_it)->description());
02695     if (g != games.end()) {
02696         const size_t index = g - games.begin();
02697         gamelist->remove_child("game", index);
02698     } else {
02699         // Can happen when the game ends before the scenario was transferred.
02700         LOG_SERVER << "Could not find game (" << (*game_it)->id()
02701             << ") to delete in games_and_users_list_.\n";
02702     }
02703 
02704     const wesnothd::user_vector& users = (*game_it)->all_game_users();
02705     // Set the availability status for all quitting users.
02706     for (wesnothd::user_vector::const_iterator user = users.begin();
02707         user != users.end(); ++user)
02708     {
02709         const wesnothd::player_map::iterator pl = players_.find(*user);
02710         if (pl != players_.end()) {
02711             pl->second.mark_available();
02712             simple_wml::document udiff;
02713             if (make_change_diff(games_and_users_list_.root(), NULL,
02714                          "user", pl->second.config_address(), udiff)) {
02715                 rooms_.lobby().send_data(udiff);
02716             }
02717         } else {
02718             ERR_SERVER << "ERROR: delete_game(): Could not find user in players_. (socket: "
02719                 << *user << ")\n";
02720         }
02721     }
02722 
02723     //send users in the game a notification to leave the game since it has ended
02724     static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
02725     (*game_it)->send_data(leave_game_doc);
02726     // Put the remaining users back in the lobby.
02727     rooms_.enter_lobby(**game_it);
02728 
02729     (*game_it)->send_data(games_and_users_list_);
02730 
02731     delete *game_it;
02732     games_.erase(game_it);
02733 }
02734 
02735 void server::update_game_in_lobby(const wesnothd::game* g, network::connection exclude)
02736 {
02737     simple_wml::document diff;
02738     if (make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g->description(), diff)) {
02739         rooms_.lobby().send_data(diff, exclude);
02740     }
02741 }
02742 
02743        #include <sys/types.h>
02744        #include <sys/stat.h>
02745        #include <fcntl.h>
02746 #ifndef _MSC_VER
02747        #include <unistd.h>
02748 #endif
02749 
02750 int main(int argc, char** argv) {
02751     int port = 15000;
02752     size_t min_threads = 5;
02753     size_t max_threads = 0;
02754 
02755     srand(static_cast<unsigned>(time(NULL)));
02756 
02757     std::string config_file;
02758 
02759     // setting path to currentworking directory
02760     game_config::path = get_cwd();
02761 
02762     // show 'info' by default
02763     lg::set_log_domain_severity("server", 2);
02764     lg::timestamps(true);
02765 
02766     for (int arg = 1; arg != argc; ++arg) {
02767         const std::string val(argv[arg]);
02768         if (val.empty()) {
02769             continue;
02770         }
02771 
02772         if ((val == "--config" || val == "-c") && arg+1 != argc) {
02773             config_file = argv[++arg];
02774         } else if (val == "--verbose" || val == "-v") {
02775             lg::set_log_domain_severity("all", 3);
02776         } else if (val.substr(0, 6) == "--log-") {
02777             size_t p = val.find('=');
02778             if (p == std::string::npos) {
02779                 std::cerr << "unknown option: " << val << '\n';
02780                 return 2;
02781             }
02782             std::string s = val.substr(6, p - 6);
02783             int severity;
02784             if (s == "error") severity = 0;
02785             else if (s == "warning") severity = 1;
02786             else if (s == "info") severity = 2;
02787             else if (s == "debug") severity = 3;
02788             else {
02789                 std::cerr << "unknown debug level: " << s << '\n';
02790                 return 2;
02791             }
02792             while (p != std::string::npos) {
02793                 size_t q = val.find(',', p + 1);
02794                 s = val.substr(p + 1, q == std::string::npos ? q : q - (p + 1));
02795                 if (!lg::set_log_domain_severity(s, severity)) {
02796                     std::cerr << "unknown debug domain: " << s << '\n';
02797                     return 2;
02798                 }
02799                 p = q;
02800             }
02801         } else if ((val == "--port" || val == "-p") && arg+1 != argc) {
02802             port = atoi(argv[++arg]);
02803         } else if (val == "--help" || val == "-h") {
02804             std::cout << "usage: " << argv[0]
02805                 << " [-dvV] [-c path] [-m n] [-p port] [-t n]\n"
02806                 << "  -c, --config <path>        Tells wesnothd where to find the config file to use.\n"
02807                 << "  -d, --daemon               Runs wesnothd as a daemon.\n"
02808                 << "  -h, --help                 Shows this usage message.\n"
02809                 << "  --log-<level>=<domain1>,<domain2>,...\n"
02810                 << "                             sets the severity level of the debug domains.\n"
02811                 << "                             'all' can be used to match any debug domain.\n"
02812                 << "                             Available levels: error, warning, info, debug.\n"
02813                 << "  -p, --port <port>          Binds the server to the specified port.\n"
02814                 << "  -t, --threads <n>          Uses n worker threads for network I/O (default: 5).\n"
02815                 << "  -v  --verbose              Turns on more verbose logging.\n"
02816                 << "  -V, --version              Returns the server version.\n";
02817             return 0;
02818         } else if (val == "--version" || val == "-V") {
02819             std::cout << "Battle for Wesnoth server " << game_config::version
02820                 << "\n";
02821             return 0;
02822         } else if (val == "--daemon" || val == "-d") {
02823 #ifdef _WIN32
02824             ERR_SERVER << "Running as a daemon is not supported on this platform\n";
02825             return -1;
02826 #else
02827             const pid_t pid = fork();
02828             if (pid < 0) {
02829                 ERR_SERVER << "Could not fork and run as a daemon\n";
02830                 return -1;
02831             } else if (pid > 0) {
02832                 std::cout << "Started wesnothd as a daemon with process id "
02833                     << pid << "\n";
02834                 return 0;
02835             }
02836 
02837             setsid();
02838 #endif
02839         } else if ((val == "--threads" || val == "-t") && arg+1 != argc) {
02840             min_threads = atoi(argv[++arg]);
02841             if (min_threads > 30) {
02842                 min_threads = 30;
02843             }
02844         } else if ((val == "--max-threads" || val == "-T") && arg+1 != argc) {
02845             max_threads = atoi(argv[++arg]);
02846         } else if(val == "--request_sample_frequency" && arg+1 != argc) {
02847             request_sample_frequency = atoi(argv[++arg]);
02848         } else {
02849             ERR_SERVER << "unknown option: " << val << "\n";
02850             return 2;
02851         }
02852     }
02853 
02854     network::set_raw_data_only();
02855 
02856     try {
02857         server(port, config_file, min_threads, max_threads).run();
02858     } catch(network::error& e) {
02859         ERR_SERVER << "Caught network error while server was running. Aborting.: "
02860             << e.message << "\n";
02861 
02862         /**
02863          * @todo errno should be passed here with the error or it might not be
02864          * the true errno anymore. Seems to work good enough for now though.
02865          */
02866         return errno;
02867     }
02868 
02869     return 0;
02870 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:45 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs