00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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"
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
00092 clock_t get_cpu_time(bool ) {
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
00108
00109
00110 #define ERR_SERVER LOG_STREAM(err, log_server)
00111
00112
00113 #define WRN_SERVER LOG_STREAM(warn, log_server)
00114
00115
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
00124 #ifndef SIGHUP
00125 #define SIGHUP 20
00126 #endif
00127
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
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
00254
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 }
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
00543
00544
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
00575
00576
00577
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
00590
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
00635
00636 fps_limit_.limit();
00637 try {
00638
00639 if (graceful_restart && games_.empty() && ++graceful_counter > 500 )
00640 {
00641
00642
00643
00644
00645
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
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
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
00676 graceful_restart = true;
00677 }
00678
00679 ban_manager_.check_ban_times(now);
00680
00681 if (last_stats_ + 5 * 60 <= now) {
00682 dump_stats(now);
00683 if (rooms_.dirty()) rooms_.write_rooms();
00684 }
00685
00686
00687 if (last_uh_clean_ + 60 * 60 * 24 <= now) {
00688 clean_user_handler(now);
00689 }
00690
00691
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
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
00720
00721
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));
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
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
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
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
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
00902
00903
00904
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
00919
00920
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
00932 simple_wml::node& root = data.root();
00933 if(root.has_attr("ping")) {
00934
00935 return;
00936 } else if(not_logged_in_.find(sock) != not_logged_in_.end()) {
00937
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
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
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
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
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
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
01028 if (login == NULL) {
01029 send_error(sock, "You must login first.", MP_MUST_LOGIN);
01030 return;
01031 }
01032
01033
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
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
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
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
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
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
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
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
01111 }
01112 else if(exists) {
01113
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
01128
01129 if(seeds_[sock].empty()) {
01130 send_password_request(sock, "Please try again.", username, MP_NO_SEED_ERROR);
01131 }
01132
01133 else if(!(user_handler_->login(username, password, seeds_[sock]))) {
01134 const time_t now = time(NULL);
01135
01136
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
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
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
01170 LOG_SERVER << network::ip_address(sock) << "\t"
01171 << "Login attempt with incorrect password for nickname '" << username << "'.\n";
01172 return;
01173 }
01174
01175 registered = true;
01176
01177 seeds_.erase(sock);
01178 user_handler_->user_logged_in(username);
01179
01180 if (name_taken) {
01181
01182 process_command("kick " + username + " autokick by registered user", username);
01183 }
01184 }
01185 }
01186
01187
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
01195 if (name_taken && !registered) {
01196 send_error(sock, "The nickname '" + username + "' is already taken.", MP_NAME_TAKEN_ERROR);
01197 return;
01198 }
01199
01200
01201
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
01218
01219
01220
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
01228 send_doc(games_and_users_list_, sock);
01229
01230 if (motd_ != "") {
01231 rooms_.lobby().send_server_message(motd_, sock);
01232 }
01233
01234
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
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
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
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
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
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
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
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
01347
01348
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
01361
01362
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
01391 void server::shut_down_handler(const std::string& issuer_name, const std::string& , 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
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& , std::string& , 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
01423 server_.stop();
01424 input_.reset();
01425
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& , 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& , const std::string& , std::string& , std::ostringstream *out) {
01451 assert(out != NULL);
01452 *out << help_msg;
01453 }
01454
01455 void server::stats_handler(const std::string& , const std::string& , std::string& , 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& , const std::string& , std::string& , std::ostringstream *out) {
01464 assert(out != NULL);
01465 *out << metrics_;
01466 }
01467
01468 void server::requests_handler(const std::string& , const std::string& , std::string& , std::ostringstream *out) {
01469 assert(out != NULL);
01470 metrics_.requests(*out);
01471 }
01472
01473 void server::games_handler(const std::string& , const std::string& , std::string& , std::ostringstream *out) {
01474 assert(out != NULL);
01475 metrics_.games(*out);
01476 }
01477
01478 void server::wml_handler(const std::string& , const std::string& , std::string& , std::ostringstream *out) {
01479 assert(out != NULL);
01480 *out << simple_wml::document::stats();
01481 }
01482
01483 void server::netstats_handler(const std::string& , const std::string& , 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();
01495 }
01496 }
01497
01498 void server::adminmsg_handler(const std::string& issuer_name, const std::string& , 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& , 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
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& , const std::string& , 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& , const std::string& , 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& , std::string& parameters, std::ostringstream *out) {
01604 assert(out != NULL);
01605
01606 *out << "STATUS REPORT for '" << parameters << "'";
01607 bool found_something = false;
01608
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
01619
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& , const std::string& , std::string& , 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& , const std::string& , 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& , 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
01710
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
01728
01729
01730
01731
01732
01733
01734
01735
01736
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& , 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
01779
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
01811
01812
01813
01814
01815
01816
01817
01818
01819
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& , 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
01865
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
01883
01884
01885
01886
01887
01888
01889
01890
01891
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& , const std::string& , 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& , const std::string& , 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& , const std::string& , 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
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& , const std::string& , 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& , const std::string& , 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
01983
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& , const std::string& , 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
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
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
02040
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
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
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
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
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
02105
02106
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
02117
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
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
02211
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
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
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
02273
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
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
02321
02322 if (data.child("message")) {
02323 rooms_.process_message(data, pl);
02324 }
02325
02326
02327
02328 if (data.child("refresh_lobby")) {
02329 send_doc(games_and_users_list_, sock);
02330 }
02331 }
02332
02333
02334
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
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
02377
02378
02379
02380
02381
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
02387
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
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
02417
02418
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
02429 g->level().swap(data);
02430
02431
02432
02433 g->update_side_data();
02434 g->describe_slots();
02435
02436 assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
02437
02438
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
02444 return;
02445
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
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
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
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
02498
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
02511 update_game_in_lobby(g);
02512
02513 g->start_game(pl);
02514 return;
02515
02516
02517 } else if(data.child("notify_next_scenario")) {
02518
02519
02520 return;
02521
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
02528
02529
02530 g->send_data(data, sock);
02531 g->start_game(pl);
02532
02533
02534 update_game_in_lobby(g);
02535 return;
02536 } else if (data.child("leave_game")) {
02537
02538 if ((g->is_player(sock) && g->nplayers() == 1)
02539 || (g->is_owner(sock) && (!g->started() || g->nplayers() == 0))) {
02540
02541
02542 delete_game(itor);
02543 } else {
02544 g->remove_player(sock);
02545 rooms_.enter_lobby(sock);
02546 g->describe_slots();
02547
02548
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
02559 send_doc(games_and_users_list_, sock);
02560 }
02561 return;
02562
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
02576 } else if (data.child("change_faction")) {
02577 g->send_data(data, sock);
02578 return;
02579
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
02586 return;
02587
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
02596 } else if (const simple_wml::node* mute = data.child("mute")) {
02597 g->mute_observer(*mute, pl);
02598 return;
02599
02600 } else if (const simple_wml::node* unmute = data.child("unmute")) {
02601 g->unmute_observer(*unmute, pl);
02602 return;
02603
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
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
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
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
02646
02647
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
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
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
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
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
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
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
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
02760 game_config::path = get_cwd();
02761
02762
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
02864
02865
02866 return errno;
02867 }
02868
02869 return 0;
02870 }