The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
server.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Wesnoth-Server, for multiplayer-games.
18  */
19 
20 #include "server.hpp"
21 
22 #include "global.hpp"
23 
24 #include "config.hpp"
25 #include "game_config.hpp"
26 #include "log.hpp"
27 #include "map/map.hpp" // gamemap::MAX_PLAYERS
28 #include "filesystem.hpp"
30 #include "serialization/parser.hpp"
34 #include "util.hpp"
35 
36 #include "game.hpp"
37 #include "metrics.hpp"
38 #include "player.hpp"
39 #include "simple_wml.hpp"
40 #include "ban.hpp"
41 #include "exceptions.hpp"
42 
43 #include "user_handler.hpp"
44 #include "sample_user_handler.hpp"
45 
46 #ifdef HAVE_MYSQLPP
47 #include "forum_user_handler.hpp"
48 #endif
49 
50 #include "utils/functional.hpp"
51 
52 #include <boost/scoped_ptr.hpp>
53 #include <boost/scoped_array.hpp>
54 #include <boost/make_shared.hpp>
55 #include <boost/utility.hpp>
56 #include <algorithm>
57 #include <cassert>
58 #include <cerrno>
59 #include <cstdlib>
60 #include <iomanip>
61 #include <iostream>
62 #include <map>
63 #include <set>
64 #include <sstream>
65 #include <vector>
66 #include <queue>
67 
68 #include <csignal>
69 
70 static lg::log_domain log_server("server");
71 /**
72  * fatal and directly server related errors/warnings,
73  * ie not caused by erroneous client data
74  */
75 #define ERR_SERVER LOG_STREAM(err, log_server)
76 
77 /** clients send wrong/unexpected data */
78 #define WRN_SERVER LOG_STREAM(warn, log_server)
79 
80 /** normal events */
81 #define LOG_SERVER LOG_STREAM(info, log_server)
82 #define DBG_SERVER LOG_STREAM(debug, log_server)
83 
84 static lg::log_domain log_config("config");
85 #define ERR_CONFIG LOG_STREAM(err, log_config)
86 #define WRN_CONFIG LOG_STREAM(warn, log_config)
87 
88 //compatibility code for MS compilers
89 #ifndef SIGHUP
90 #define SIGHUP 20
91 #endif
92 
93 namespace
94 {
95 
96 struct server_shutdown : public game::error
97 {
98  server_shutdown(const std::string& msg) : game::error(msg) {}
99 };
100 
101 }
102 
103 namespace wesnothd
104 {
105 
106 void async_send_error(socket_ptr socket, const std::string& msg, const char* error_code = "");
107 
109 {
110  boost::system::error_code error;
111  std::string result = socket->remote_endpoint(error).address().to_string();
112  if(error)
113  return "<unknown address>";
114  else
115  return result;
116 }
117 
118 // we take profiling info on every n requests
120 
121 static bool check_error(const boost::system::error_code& error, socket_ptr socket)
122 {
123  if(error) {
124  ERR_SERVER << client_address(socket) << "\t" << error.message() << "\n";
125  return true;
126  }
127  return false;
128 }
129 
130 template<typename Handler, typename ErrorHandler>
132 {
133  Handler handler;
134  ErrorHandler error_handler;
136  union DataSize
137  {
139  char buf[4];
140  };
143  boost::shared_array<char> buffer;
144  handle_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler, boost::uint32_t size, boost::shared_ptr<simple_wml::document> doc) :
145  handler(handler), error_handler(error_handler), socket(socket), data_size(new DataSize), doc(doc)
146  {
147  data_size->size = htonl(size);
148  }
149  handle_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler) :
150  handler(handler), error_handler(error_handler), socket(socket), data_size(new DataSize)
151  {
152  }
153  void operator()(const boost::system::error_code& error, std::size_t)
154  {
155  if(check_error(error, socket)) {
156  error_handler(socket);
157  return;
158  }
159  handler(socket);
160  }
161 };
162 
163 template<typename Handler, typename ErrorHandler>
164 void async_send_doc(socket_ptr socket, simple_wml::document& doc, Handler handler, ErrorHandler error_handler)
165 {
166  try {
168  simple_wml::string_span s = doc_ptr->output_compressed();
169  std::vector<boost::asio::const_buffer> buffers;
170 
171  handle_doc<Handler, ErrorHandler> handle_send_doc(socket, handler, error_handler, s.size(), doc_ptr);
172  buffers.push_back(boost::asio::buffer(handle_send_doc.data_size->buf, 4));
173  buffers.push_back(boost::asio::buffer(s.begin(), s.size()));
174  async_write(*socket, buffers, handle_send_doc);
175  } catch (simple_wml::error& e) {
176  WRN_CONFIG << __func__ << ": simple_wml error: " << e.message << std::endl;
177  }
178 }
179 
181 {
182 }
183 
184 template<typename Handler>
186 {
187  async_send_doc(socket, doc, handler, null_handler);
188 }
189 
191 {
193 }
194 
195 template<typename Handler, typename ErrorHandler>
196 struct handle_receive_doc : public handle_doc<Handler, ErrorHandler>
197 {
198  std::size_t buf_size;
200  handle_doc<Handler, ErrorHandler>(socket, handler, error_handler)
201  {
202  }
203  void operator()(const boost::system::error_code& error, std::size_t size)
204  {
205  if(check_error(error, this->socket)) {
206  this->error_handler(this->socket);
207  return;
208  }
209  if(!this->buffer) {
210  assert(size == 4);
211  buf_size = ntohl(this->data_size->size);
212  this->buffer = boost::shared_array<char>(new char[buf_size]);
213  async_read(*(this->socket), boost::asio::buffer(this->buffer.get(), buf_size), *this);
214  } else {
215  simple_wml::string_span compressed_buf(this->buffer.get(), buf_size);
216  try {
217  this->doc.reset(new simple_wml::document(compressed_buf));
218  } catch (simple_wml::error& e) {
219  ERR_SERVER <<
220  client_address(this->socket) <<
221  "\tsimple_wml error in received data: " << e.message << std::endl;
222  async_send_error(this->socket, "Invalid WML received: " + e.message);
223  this->error_handler(this->socket);
224  return;
225  }
226  this->handler(this->socket, this->doc);
227  }
228  }
229 };
230 
231 template<typename Handler, typename ErrorHandler>
232 void async_receive_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
233 {
234  handle_receive_doc<Handler, ErrorHandler> handle_receive_doc(socket, handler, error_handler);
235  async_read(*socket, boost::asio::buffer(handle_receive_doc.data_size->buf, 4), handle_receive_doc);
236 }
237 
238 template<typename Handler>
239 void async_receive_doc(socket_ptr socket, Handler handler)
240 {
241  async_receive_doc(socket, handler, null_handler);
242 }
243 
244 static void make_add_diff(const simple_wml::node& src, const char* gamelist,
245  const char* type,
246  simple_wml::document& out, int index=-1)
247 {
248  if (!out.child("gamelist_diff")) {
249  out.root().add_child("gamelist_diff");
250  }
251 
252  simple_wml::node* top = out.child("gamelist_diff");
253  if(gamelist) {
254  top = &top->add_child("change_child");
255  top->set_attr_int("index", 0);
256  top = &top->add_child("gamelist");
257  }
258 
259  simple_wml::node& insert = top->add_child("insert_child");
260  const simple_wml::node::child_list& children = src.children(type);
261  assert(!children.empty());
262  if(index < 0) {
263  index = children.size() - 1;
264  }
265 
266  assert(index < static_cast<int>(children.size()));
267  insert.set_attr_int("index", index);
268  children[index]->copy_into(insert.add_child(type));
269 }
270 
272  const char* gamelist,
273  const char* type,
274  const simple_wml::node* remove,
276 {
277  if (!out.child("gamelist_diff")) {
278  out.root().add_child("gamelist_diff");
279  }
280 
281  simple_wml::node* top = out.child("gamelist_diff");
282  if(gamelist) {
283  top = &top->add_child("change_child");
284  top->set_attr_int("index", 0);
285  top = &top->add_child("gamelist");
286  }
287 
288  const simple_wml::node::child_list& children = src.children(type);
289  const simple_wml::node::child_list::const_iterator itor =
290  std::find(children.begin(), children.end(), remove);
291  if(itor == children.end()) {
292  return false;
293  }
294  const int index = itor - children.begin();
295  simple_wml::node& del = top->add_child("delete_child");
296  del.set_attr_int("index", index);
297  del.add_child(type);
298  return true;
299 }
300 
302  const char* gamelist,
303  const char* type,
304  const simple_wml::node* item,
306 {
307  if (!out.child("gamelist_diff")) {
308  out.root().add_child("gamelist_diff");
309  }
310 
311  simple_wml::node* top = out.child("gamelist_diff");
312  if(gamelist) {
313  top = &top->add_child("change_child");
314  top->set_attr_int("index", 0);
315  top = &top->add_child("gamelist");
316  }
317  const simple_wml::node::child_list& children = src.children(type);
318  const simple_wml::node::child_list::const_iterator itor =
319  std::find(children.begin(), children.end(), item);
320  if(itor == children.end()) {
321  return false;
322  }
323 
324  simple_wml::node& diff = *top;
325  simple_wml::node& del = diff.add_child("delete_child");
326  const int index = itor - children.begin();
327  del.set_attr_int("index", index);
328  del.add_child(type);
329 
330  //inserts will be processed first by the client, so insert at index+1,
331  //and then when the delete is processed we'll slide into the right position
332  simple_wml::node& insert = diff.add_child("insert_child");
333  insert.set_attr_int("index", index + 1);
334  children[index]->copy_into(insert.add_child(type));
335  return true;
336 }
337 
339  std::ostringstream out;
340  //const network::connection_stats& stats = network::get_connection_stats(pl->first);
341  //const int time_connected = stats.time_connected / 1000;
342  //const int seconds = time_connected % 60;
343  //const int minutes = (time_connected / 60) % 60;
344  //const int hours = time_connected / (60 * 60);
345  out << "'" << player.name() << "' @ " << client_address(player.socket());
346 // << " connected for " << std::setw(2) << hours << ":" << std::setw(2) << minutes << ":" << std::setw(2) << seconds
347 // << " sent " << stats.bytes_sent << " bytes, received "
348 // << stats.bytes_received << " bytes";
349  return out.str();
350 }
351 
352  const std::string denied_msg = "You're not allowed to execute this command.";
353  const std::string help_msg = "Available commands are: adminmsg <msg>,"
354  " ban <mask> <time> <reason>, bans [deleted] [<ipmask>], clones,"
355  " dul|deny_unregistered_login [yes|no], kick <mask> [<reason>],"
356  " k[ick]ban <mask> <time> <reason>, help, games, metrics,"
357  " netstats [all], [lobby]msg <message>, motd [<message>],"
358  " pm|privatemsg <nickname> <message>, requests, sample, searchlog <mask>,"
359  " signout, stats, status [<mask>], unban <ipmask>\n"
360  "Specific strings (those not inbetween <> like the command names)"
361  " are case insensitive.";
362 
363 server::server(int port, bool keep_alive, const std::string& config_file, size_t /*min_threads*/,
364  size_t /*max_threads*/) :
365  io_service_(),
366  acceptor_(io_service_),
367  ban_manager_(),
368  ip_log_(),
369  failed_logins_(),
370  user_handler_(nullptr),
371 #ifndef _WIN32
372  input_(io_service_),
373 #endif
374  input_path_(),
375  config_file_(config_file),
376  cfg_(read_config()),
377  accepted_versions_(),
378  redirected_versions_(),
379  proxy_versions_(),
380  disallowed_names_(),
381  admin_passwd_(),
382  motd_(),
383  default_max_messages_(0),
384  default_time_period_(0),
385  concurrent_connections_(0),
386  graceful_restart(false),
387  lan_server_(time(nullptr)),
388  last_user_seen_time_(time(nullptr)),
389  restart_command(),
390  max_ip_log_size_(0),
391  uh_name_(),
392  deny_unregistered_login_(false),
393  save_replays_(false),
394  replay_save_path_(),
395  allow_remote_shutdown_(false),
396  tor_ip_list_(),
397  failed_login_limit_(),
398  failed_login_ban_(),
399  failed_login_buffer_size_(),
400  version_query_response_("[version]\n[/version]\n", simple_wml::INIT_COMPRESSED),
401  login_response_("[mustlogin]\n[/mustlogin]\n", simple_wml::INIT_COMPRESSED),
402  join_lobby_response_("[join_lobby]\n[/join_lobby]\n", simple_wml::INIT_COMPRESSED),
403  games_and_users_list_("[gamelist]\n[/gamelist]\n", simple_wml::INIT_STATIC),
404  metrics_(),
405  last_ping_(time(nullptr)),
406  last_stats_(last_ping_),
407  last_uh_clean_(last_ping_),
408  cmd_handlers_(),
409 #ifndef _WIN32
410  sighup_(io_service_, SIGHUP),
411 #endif
412  sigs_(io_service_, SIGINT, SIGTERM),
413  timer_(io_service_)
414 {
415  boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
416  acceptor_.open(endpoint.protocol());
417  acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
418  acceptor_.set_option(boost::asio::ip::tcp::acceptor::keep_alive(keep_alive));
419  acceptor_.bind(endpoint);
420  acceptor_.listen();
421  serve();
422 
423  handshake_response_.connection_num = 42;
424 
425  setup_handlers();
426  load_config();
427  ban_manager_.read();
428 
429 #ifndef _WIN32
430  sighup_.async_wait(boost::bind(&server::handle_sighup, this, _1, _2));
431 #endif
432  sigs_.async_wait(boost::bind(&server::handle_termination, this, _1, _2));
433 }
434 
435 #ifndef _WIN32
436 void server::handle_sighup(const boost::system::error_code& error, int) {
437  assert(!error);
438 
439  WRN_SERVER << "SIGHUP caught, reloading config\n";
440 
441  cfg_ = read_config();
442  load_config();
443 
444  sighup_.async_wait(boost::bind(&server::handle_sighup, this, _1, _2));
445 }
446 #endif
447 
448 void server::handle_termination(const boost::system::error_code& error, int signal_number)
449 {
450  assert(!error);
451 
452  std::string signame;
453  if(signal_number == SIGINT) signame = "SIGINT";
454  else if(signal_number == SIGTERM) signame = "SIGTERM";
455  else signame = lexical_cast<std::string>(signal_number);
456  LOG_SERVER << signame << " caught, exiting without cleanup immediately.\n";
457  exit(128 + signal_number);
458 }
459 
460 void server::handle_graceful_timeout(const boost::system::error_code& error)
461 {
462  assert(!error);
463 
464  if(games().empty()) {
465  process_command("msg All games ended. Shutting down now. Reconnect to the new server instance.", "system");
466  throw server_shutdown("graceful shutdown timeout");
467  } else {
468  timer_.expires_from_now(boost::posix_time::seconds(1));
469  timer_.async_wait(boost::bind(&server::handle_graceful_timeout, this, _1));
470  }
471 }
472 
474 #ifndef _WIN32
475  const int res = mkfifo(input_path_.c_str(),0660);
476  if(res != 0 && errno != EEXIST) {
477  ERR_SERVER << "could not make fifo at '" << input_path_ << "' (" << strerror(errno) << ")\n";
478  return;
479  }
480  int fifo = open(input_path_.c_str(), O_RDWR|O_NONBLOCK);
481  input_.assign(fifo);
482  LOG_SERVER << "opened fifo at '" << input_path_ << "'. Server commands may be written to this file.\n";
483  read_from_fifo();
484 #endif
485 }
486 
487 #ifndef _WIN32
489  async_read_until(input_,
490  admin_cmd_, '\n',
491  boost::bind(&server::handle_read_from_fifo, this, _1, _2));
492 }
493 
494 void server::handle_read_from_fifo(const boost::system::error_code& error, std::size_t) {
495  if(error) {
496  std::cout << error.message() << std::endl;
497  return;
498  }
499 
500  std::istream is(&admin_cmd_);
501  std::string cmd;
502  std::getline(is, cmd);
503 
504  LOG_SERVER << "Admin Command: type: " << cmd << "\n";
505  const std::string res = process_command(cmd, "*socket*");
506  // Only mark the response if we fake the issuer (i.e. command comes from IRC or so)
507  if (cmd.at(0) == '+') {
508  LOG_SERVER << "[admin_command_response]\n" << res << "\n" << "[/admin_command_response]\n";
509  } else {
510  LOG_SERVER << res << "\n";
511  }
512 
513  read_from_fifo();
514 
515 }
516 
517 #endif
518 
520 {
534  cmd_handlers_["privatemsg"] = &server::pm_handler;
536  cmd_handlers_["lobbymsg"] = &server::msg_handler;
551  cmd_handlers_["deny_unregistered_login"] = &server::dul_handler;
552 }
553 
554 void async_send_error(socket_ptr socket, const std::string& msg, const char* error_code)
555 {
557  doc.root().add_child("error").set_attr_dup("message", msg.c_str());
558  if(*error_code != '\0') {
559  doc.child("error")->set_attr("error_code", error_code);
560  }
561 
562  async_send_doc(socket, doc);
563 }
564 
565 static void async_send_warning(socket_ptr socket, const std::string& msg, const char* warning_code)
566 {
568  doc.root().add_child("warning").set_attr_dup("message", msg.c_str());
569  if(*warning_code != '\0') {
570  doc.child("warning")->set_attr("warning_code", warning_code);
571  }
572 
573  async_send_doc(socket, doc);
574 }
575 
577  config configuration;
578  if (config_file_ == "") return configuration;
579  try {
581  read(configuration, *stream);
582  LOG_SERVER << "Server configuration from file: '" << config_file_
583  << "' read.\n";
584  } catch(config::error& e) {
585  ERR_CONFIG << "ERROR: Could not read configuration file: '"
586  << config_file_ << "': '" << e.message << "'.\n";
587  }
588  return configuration;
589 }
590 
592 #ifndef FIFODIR
593 # ifdef _MSC_VER
594 # pragma message ("No FIFODIR set")
595 # define FIFODIR "d:/"
596 # else
597 # ifdef _WIN32
598 # define FIFODIR "d:/"
599 # else
600 # warning "No FIFODIR set"
601 # define FIFODIR "/var/run/wesnothd"
602 # endif
603 # endif
604 #endif
605  const std::string fifo_path = (cfg_["fifo_path"].empty() ? std::string(FIFODIR) + "/socket" : std::string(cfg_["fifo_path"]));
606  // Reset (replace) the input stream only if the FIFO path changed.
607  if(fifo_path != input_path_) {
608 #ifndef _WIN32
609  input_.close();
610 #endif
611  input_path_ = fifo_path;
612  setup_fifo();
613  }
614 
615  save_replays_ = cfg_["save_replays"].to_bool();
616  replay_save_path_ = cfg_["replay_save_path"].str();
617 
618  tor_ip_list_ = utils::split(cfg_["tor_ip_list_path"].empty() ? "" : filesystem::read_file(cfg_["tor_ip_list_path"]), '\n');
619 
620  admin_passwd_ = cfg_["passwd"].str();
621  motd_ = cfg_["motd"].str();
622  lan_server_ = lexical_cast_default<time_t>(cfg_["lan_server"], 0);
623  uh_name_ = cfg_["user_handler"].str();
624 
625  deny_unregistered_login_ = cfg_["deny_unregistered_login"].to_bool();
626 
627  allow_remote_shutdown_ = cfg_["allow_remote_shutdown"].to_bool();
628 
629  disallowed_names_.clear();
630  if (cfg_["disallow_names"] == "") {
631  disallowed_names_.push_back("*admin*");
632  disallowed_names_.push_back("*admln*");
633  disallowed_names_.push_back("*server*");
634  disallowed_names_.push_back("player");
635  disallowed_names_.push_back("network");
636  disallowed_names_.push_back("human");
637  disallowed_names_.push_back("computer");
638  disallowed_names_.push_back("ai");
639  disallowed_names_.push_back("ai?");
640  } else {
641  disallowed_names_ = utils::split(cfg_["disallow_names"]);
642  }
643  default_max_messages_ = cfg_["max_messages"].to_int(4);
644  default_time_period_ = cfg_["messages_time_period"].to_int(10);
645  concurrent_connections_ = cfg_["connections_allowed"].to_int(5);
646  max_ip_log_size_ = cfg_["max_ip_log_size"].to_int(500);
647 
648  failed_login_limit_ = cfg_["failed_logins_limit"].to_int(10);
649  failed_login_ban_ = cfg_["failed_logins_ban"].to_int(3600);
650  failed_login_buffer_size_ = cfg_["failed_logins_buffer_size"].to_int(500);
651 
652  // Example config line:
653  // restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
654  // remember to make new one as a daemon or it will block old one
655  restart_command = cfg_["restart_command"].str();
656 
657  accepted_versions_.clear();
658  const std::string& versions = cfg_["versions_accepted"];
659  if (versions.empty() == false) {
660  accepted_versions_ = utils::split(versions);
661  } else {
663  accepted_versions_.push_back("test");
664  }
665 
666  redirected_versions_.clear();
667  for(const config &redirect : cfg_.child_range("redirect")) {
668  for(const std::string &version : utils::split(redirect["version"])) {
669  redirected_versions_[version] = redirect;
670  }
671  }
672 
673  proxy_versions_.clear();
674  for(const config &proxy : cfg_.child_range("proxy")) {
675  for(const std::string &version : utils::split(proxy["version"])) {
676  proxy_versions_[version] = proxy;
677  }
678  }
680 
681  // If there is a [user_handler] tag in the config file
682  // allow nick registration, otherwise we set user_handler_
683  // to nullptr. Thus we must check user_handler_ for not being
684  // nullptr everytime we want to use it.
685  user_handler_.reset();
686 
687  if (const config &user_handler = cfg_.child("user_handler")) {
688  if(uh_name_ == "sample") {
689  user_handler_.reset(new suh(user_handler));
690  }
691 #ifdef HAVE_MYSQLPP
692  else if(uh_name_ == "forum" || uh_name_.empty()) {
693  user_handler_.reset(new fuh(user_handler));
694  }
695 #endif
696  // Initiate the mailer class with the [mail] tag
697  // from the config file
698  if (user_handler_) user_handler_->init_mailer(cfg_.child("mail"));
699  }
700 }
701 
703  if (!tor_ip_list_.empty()) {
704  if (find(tor_ip_list_.begin(), tor_ip_list_.end(), ip) != tor_ip_list_.end()) return "TOR IP";
705  }
706  return ban_manager_.is_ip_banned(ip);
707 }
708 
709 void server::dump_stats(const time_t& now) {
710  last_stats_ = now;
711  LOG_SERVER << "Statistics:"
712  << "\tnumber_of_games = " << games().size()
713  << "\tnumber_of_users = " << player_connections_.size() << "\n";
714 }
715 
716 void server::clean_user_handler(const time_t& now) {
717  if(!user_handler_) {
718  return;
719  }
720  last_uh_clean_ = now;
721  user_handler_->clean_up();
722 }
723 
725 {
726  socket_ptr socket = boost::make_shared<boost::asio::ip::tcp::socket>(boost::ref(io_service_));
727  acceptor_.async_accept(*socket, boost::bind(&server::accept_connection, this, _1, socket));
728 }
729 
730 void server::accept_connection(const boost::system::error_code& error, socket_ptr socket)
731 {
732  if(!graceful_restart)
733  serve();
734  if(error) {
735  ERR_SERVER << "Accept failed: " << error.message() << "\n";
736  return;
737  }
738 
739  const std::string ip = client_address(socket);
740 
741  const std::string reason = is_ip_banned(ip);
742  if (!reason.empty()) {
743  LOG_SERVER << ip << "\trejected banned user. Reason: " << reason << "\n";
744  async_send_error(socket, "You are banned. Reason: " + reason);
745  return;
746  /*} else if (ip_exceeds_connection_limit(ip)) {
747  LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
748  async_send_error(socket, "Too many connections from your IP.");
749  return;*/
750  } else {
751 
752  DBG_SERVER << ip << "\tnew connection accepted\n";
753  serverside_handshake(socket);
754  }
755 
756 }
757 
759 {
760  boost::shared_array<char> handshake(new char[4]);
761  async_read(
762  *socket, boost::asio::buffer(handshake.get(), 4),
763  boost::bind(&server::handle_handshake, this, _1, socket, handshake)
764  );
765 }
766 
767 void server::handle_handshake(const boost::system::error_code& error, socket_ptr socket, boost::shared_array<char> handshake)
768 {
769  if(check_error(error, socket))
770  return;
771 
772  if(strcmp(handshake.get(), "\0\0\0\0") != 0) {
773  ERR_SERVER << client_address(socket) << "\tincorrect handshake\n";
774  return;
775  }
776  async_write(
777  *socket, boost::asio::buffer(handshake_response_.buf, 4),
778  boost::bind(&server::request_version, this, _1, socket)
779  );
780 }
781 
782 void server::request_version(const boost::system::error_code& error, socket_ptr socket)
783 {
784  if(check_error(error, socket))
785  return;
786 
788  boost::bind(&server::handle_version, this, _1)
789  );
790 }
791 
793 {
794  async_receive_doc(socket,
795  boost::bind(&server::read_version, this, _1, _2)
796  );
797 }
798 
800 {
801  if (const simple_wml::node* const version = doc->child("version")) {
802  const simple_wml::string_span& version_str_span = (*version)["version"];
803  const std::string version_str(version_str_span.begin(),
804  version_str_span.end());
805  std::vector<std::string>::const_iterator accepted_it;
806  // Check if it is an accepted version.
807  accepted_it = std::find_if(accepted_versions_.begin(), accepted_versions_.end(),
808  boost::bind(&utils::wildcard_string_match, version_str, _1));
809  if(accepted_it != accepted_versions_.end()) {
810  LOG_SERVER << client_address(socket)
811  << "\tplayer joined using accepted version " << version_str
812  << ":\ttelling them to log in.\n";
813  async_send_doc(socket, login_response_, boost::bind(&server::login, this, _1));
814  return;
815  }
816 
817  simple_wml::document response;
818 
819  // Check if it is a redirected version
820  for(const auto& redirect_version : redirected_versions_) {
821  if(utils::wildcard_string_match(version_str, redirect_version.first)) {
822  LOG_SERVER << client_address(socket)
823  << "\tplayer joined using version " << version_str
824  << ":\tredirecting them to " << redirect_version.second["host"]
825  << ":" << redirect_version.second["port"] << "\n";
826  simple_wml::node& redirect = response.root().add_child("redirect");
827  for(const auto& attr : redirect_version.second.attribute_range()) {
828  redirect.set_attr(attr.first.c_str(), attr.second.str().c_str());
829  }
830  send_to_player(socket, response);
831  return;
832  }
833  }
834 
835  LOG_SERVER << client_address(socket)
836  << "\tplayer joined using unknown version " << version_str
837  << ":\trejecting them\n";
838 
839  // For compatibility with older clients
840  response.set_attr("version", accepted_versions_.begin()->c_str());
841 
842  simple_wml::node& reject = response.root().add_child("reject");
843  reject.set_attr_dup("accepted_versions", utils::join(accepted_versions_).c_str());
844  send_to_player(socket, response);
845  } else {
846  LOG_SERVER << client_address(socket)
847  << "\tclient didn't send its version: rejecting\n";
848  }
849 }
850 
852 {
853  async_receive_doc(socket,
854  boost::bind(&server::handle_login, this, _1, _2)
855  );
856 }
857 
859 {
860  if(const simple_wml::node* const login = doc->child("login")) {
861  // Check if the username is valid (all alpha-numeric plus underscore and hyphen)
862  std::string username = (*login)["username"].to_string();
863  if (!utils::isvalid_username(username)) {
864  async_send_error(socket, "The nickname '" + username + "' contains invalid "
865  "characters. Only alpha-numeric characters, underscores and hyphens"
866  "are allowed.", MP_INVALID_CHARS_IN_NAME_ERROR);
867  server::login(socket);
868  return;
869  }
870  if (username.size() > 20) {
871  async_send_error(socket, "The nickname '" + username + "' is too long. Nicks must be 20 characters or less.",
873  server::login(socket);
874  return;
875  }
876  // Check if the username is allowed.
877  for (std::vector<std::string>::const_iterator d_it = disallowed_names_.begin();
878  d_it != disallowed_names_.end(); ++d_it)
879  {
881  utf8::lowercase(*d_it)))
882  {
883  async_send_error(socket, "The nickname '" + username + "' is reserved and cannot be used by players",
885  server::login(socket);
886  return;
887  }
888  }
889 
890  // If this is a request for password reminder
891  if(user_handler_) {
892  std::string password_reminder = (*login)["password_reminder"].to_string();
893  if(password_reminder == "yes") {
894  try {
895  user_handler_->password_reminder(username);
896  async_send_error(socket, "Your password reminder email has been sent.");
897  } catch (user_handler::error& e) {
898  async_send_error(socket, "There was an error sending your password reminder email. The error message was: " +
899  e.message);
900  }
901  return;
902  }
903  }
904 
905  // Check the username isn't already taken
906  auto p = player_connections_.get<name_t>().find(username);
907  bool name_taken = p != player_connections_.get<name_t>().end();
908 
909  // Check for password
910 
911  // Current login procedure for registered nicks is:
912  // - Client asks to log in with a particular nick
913  // - Server sends client random salt plus some info
914  // generated from the original hash that is required to
915  // regenerate the hash
916  // - Client generates hash for the user provided password
917  // and mixes it with the received random salt
918  // - Server received salted hash, salts the valid hash with
919  // the same salt it sent to the client and compares the results
920 
921  bool registered = false;
922  if(user_handler_) {
923  std::string password = (*login)["password"].to_string();
924  const bool exists = user_handler_->user_exists(username);
925  // This name is registered but the account is not active
926  if(exists && !user_handler_->user_is_active(username)) {
927  async_send_warning(socket, "The nickname '" + username + "' is inactive. You cannot claim ownership of this "
928  "nickname until you activate your account via email or ask an administrator to do it for you.", MP_NAME_INACTIVE_WARNING);
929  //registered = false;
930  }
931  else if(exists) {
932  // This name is registered and no password provided
933  if(password.empty()) {
934  if(!name_taken) {
935  send_password_request(socket, "The nickname '" + username +"' is registered on this server.",
936  username, MP_PASSWORD_REQUEST);
937  } else {
938  send_password_request(socket, "The nickname '" + username + "' is registered on this server."
939  "\n\nWARNING: There is already a client using this username, "
940  "logging in will cause that client to be kicked!",
942  }
943  return;
944  }
945 
946  // A password (or hashed password) was provided, however
947  // there is no seed
948  if(seeds_[reinterpret_cast<long int>(socket.get())].empty()) {
949  send_password_request(socket, "Please try again.", username, MP_NO_SEED_ERROR);
950  return;
951  }
952  // This name is registered and an incorrect password provided
953  else if(!(user_handler_->login(username, password, seeds_[reinterpret_cast<unsigned long>(socket.get())]))) {
954  const time_t now = time(NULL);
955 
956  // Reset the random seed
957  seeds_.erase(reinterpret_cast<unsigned long>(socket.get()));
958 
959  login_log login_ip = login_log(client_address(socket), 0, now);
961  if(i == failed_logins_.end()) {
962  failed_logins_.push_back(login_ip);
963  i = --failed_logins_.end();
964 
965  // Remove oldest entry if maximum size is exceeded
967  failed_logins_.pop_front();
968 
969  }
970 
971  if (i->first_attempt + failed_login_ban_ < now) {
972  // Clear and move to the beginning
973  failed_logins_.erase(i);
974  failed_logins_.push_back(login_ip);
975  i = --failed_logins_.end();
976  }
977 
978  i->attempts++;
979 
980  if (i->attempts > failed_login_limit_) {
981  LOG_SERVER << ban_manager_.ban(login_ip.ip, now + failed_login_ban_, "Maximum login attempts exceeded", "automatic", "", username);
982  async_send_error(socket, "You have made too many failed login attempts.", MP_TOO_MANY_ATTEMPTS_ERROR);
983  } else {
984  send_password_request(socket, "The password you provided for the nickname '" + username +
985  "' was incorrect.", username, MP_INCORRECT_PASSWORD_ERROR);
986  }
987 
988  // Log the failure
989  LOG_SERVER << client_address(socket) << "\t"
990  << "Login attempt with incorrect password for nickname '" << username << "'.\n";
991  return;
992  }
993  // This name exists and the password was neither empty nor incorrect
994  registered = true;
995  // Reset the random seed
996  seeds_.erase(reinterpret_cast<long int>(socket.get()));
997  user_handler_->user_logged_in(username);
998  }
999  }
1000 
1001  // If we disallow unregistered users and this user is not registered send an error
1002  if(user_handler_ && !registered && deny_unregistered_login_) {
1003  async_send_error(socket, "The nickname '" + username + "' is not registered. "
1004  "This server disallows unregistered nicknames.", MP_NAME_UNREGISTERED_ERROR);
1005  return;
1006  }
1007 
1008  if(name_taken) {
1009  if(registered) {
1010  // If there is already a client using this username kick it
1011  process_command("kick " + p->info().name() + " autokick by registered user", username);
1012  } else {
1013  async_send_error(socket, "The nickname '" + username + "' is already taken.", MP_NAME_TAKEN_ERROR);
1014  server::login(socket);
1015  return;
1016  }
1017  }
1018 
1019  simple_wml::node& player_cfg = games_and_users_list_.root().add_child("user");
1021  boost::bind(&server::add_player, this, _1,
1022  wesnothd::player(username, player_cfg, registered,
1023  default_max_messages_, default_time_period_, false/*selective_ping*/,
1024  user_handler_ && user_handler_->user_is_moderator(username))
1025  )
1026  );
1027  LOG_SERVER << client_address(socket) << "\t" << username
1028  << "\thas logged on" << (registered ? " to a registered account" : "") << "\n";
1029 
1030  if(user_handler_ && user_handler_->user_is_moderator(username)) {
1031  LOG_SERVER << "Admin automatically recognized: IP: "
1032  << client_address(socket) << "\tnick: "
1033  << username << std::endl;
1034  // This string is parsed by the client!
1035  send_server_message(socket, "You are now recognized as an administrator. "
1036  "If you no longer want to be automatically authenticated use '/query signout'.");
1037  }
1038 
1039  // Log the IP
1040  connection_log ip_name = connection_log(username, client_address(socket), 0);
1041  if (std::find(ip_log_.begin(), ip_log_.end(), ip_name) == ip_log_.end()) {
1042  ip_log_.push_back(ip_name);
1043  // Remove the oldest entry if the size of the IP log exceeds the maximum size
1044  if(ip_log_.size() > max_ip_log_size_) ip_log_.pop_front();
1045  }
1046  } else {
1047  async_send_error(socket, "You must login first.", MP_MUST_LOGIN);
1048  }
1049 }
1050 
1052  const std::string& user, const char* error_code, bool force_confirmation)
1053 {
1054  std::string salt = user_handler_->create_salt();
1055  std::string pepper = user_handler_->create_pepper(user);
1056  std::string spices = pepper + salt;
1057  if(user_handler_->use_phpbb_encryption() && pepper.empty()) {
1058  async_send_error(socket, "Even though your nickname is registered on this server you "
1059  "cannot log in due to an error in the hashing algorithm. "
1060  "Logging into your forum account on http://forum.wesnoth.org "
1061  "may fix this problem.");
1062  return;
1063  }
1064 
1065  seeds_[reinterpret_cast<long int>(socket.get())] = salt;
1066 
1068  simple_wml::node& e = doc.root().add_child("error");
1069  e.set_attr_dup("message", msg.c_str());
1070  e.set_attr("password_request", "yes");
1071  e.set_attr("phpbb_encryption", user_handler_->use_phpbb_encryption() ? "yes" : "no");
1072  e.set_attr_dup("salt", spices.c_str());
1073  e.set_attr("force_confirmation", force_confirmation ? "yes" : "no");
1074  if(*error_code != '\0') {
1075  e.set_attr("error_code", error_code);
1076  }
1077 
1078  async_send_doc(socket, doc,
1079  boost::bind(&server::login, this, _1)
1080  );
1081 }
1082 
1084 {
1085  bool inserted;
1086  boost::tie(boost::tuples::ignore, inserted) = player_connections_.insert(player_connections::value_type(socket, player));
1087  assert(inserted);
1088 
1090 
1091  if (motd_ != "") {
1092  send_server_message(socket, motd_);
1093  }
1094 
1095  read_from_player(socket);
1096 
1097  // Send other players in the lobby the update that the player has joined
1098  simple_wml::document diff;
1099  make_add_diff(games_and_users_list_.root(), NULL, "user", diff);
1100  send_to_lobby(diff, socket);
1101 }
1102 
1104 {
1105  async_receive_doc(socket,
1106  boost::bind(&server::handle_read_from_player, this, _1, _2),
1107  boost::bind(&server::remove_player, this, _1)
1108  );
1109 }
1110 
1112 {
1113  read_from_player(socket);
1114  //DBG_SERVER << client_address(socket) << "\tWML received:\n" << doc->output() << std::endl;
1115  if(doc->child("refresh_lobby")) {
1117  }
1118 
1119  if(simple_wml::node* whisper = doc->child("whisper")) {
1120  handle_whisper(socket, *whisper);
1121  }
1122  if(simple_wml::node* query = doc->child("query")) {
1123  handle_query(socket, *query);
1124  }
1125  if(simple_wml::node* nickserv = doc->child("nickserv")) {
1126  handle_nickserv(socket, *nickserv);
1127  }
1128 
1129  if(!player_is_in_game(socket))
1130  handle_player_in_lobby(socket, doc);
1131  else
1132  handle_player_in_game(socket, doc);
1133 
1134 }
1135 
1137  if(simple_wml::node* message = doc->child("message")) {
1138  handle_message(socket, *message);
1139  }
1140 
1141  if(simple_wml::node* create_game = doc->child("create_game")) {
1142  handle_create_game(socket, *create_game);
1143  }
1144 
1145  if(simple_wml::node* join = doc->child("join")) {
1146  handle_join_game(socket, *join);
1147  }
1148 }
1149 
1151 {
1152  if((whisper["receiver"] == "") || (whisper["message"] == "")) {
1153  static simple_wml::document data(
1154  "[message]\n"
1155  "message=\"Invalid number of arguments\"\n"
1156  "sender=\"server\"\n"
1157  "[/message]\n", simple_wml::INIT_COMPRESSED);
1158  send_to_player(socket, data);
1159  return;
1160  }
1161 
1162  auto receiver_iter = player_connections_.get<name_t>().find(whisper["receiver"].to_string());
1163  if(receiver_iter == player_connections_.get<name_t>().end()) {
1164  send_server_message(socket, "Can't find '" + whisper["receiver"].to_string() + "'.");
1165  } else {
1166  simple_wml::document cwhisper;
1167  whisper.copy_into(cwhisper.root().add_child("whisper"));
1168  send_to_player(receiver_iter->socket(), cwhisper);
1169  // TODO: Refuse to send from an observer to a game he observes
1170  }
1171 }
1172 
1174 {
1175  auto iter = player_connections_.find(socket);
1176  if(iter == player_connections_.end())
1177  return;
1178 
1179  wesnothd::player& player = iter->info();
1180 
1181  const std::string command(query["type"].to_string());
1182  std::ostringstream response;
1183  const std::string& help_msg = "Available commands are: adminmsg <msg>, help, games, metrics,"
1184  " motd, netstats [all], requests, sample, stats, status, wml.";
1185  // Commands a player may issue.
1186  if (command == "status") {
1187  response << process_command(command + " " + player.name(), player.name());
1188  } else if (command.find("adminmsg") == 0 || command.find("report") == 0
1189  || command == "games"
1190  || command == "metrics"
1191  || command == "motd"
1192  || command == "netstats"
1193  || command == "netstats all"
1194  || command == "requests"
1195  || command == "sample"
1196  || command == "stats"
1197  || command == "status " + player.name()
1198  || command == "wml")
1199  {
1200  response << process_command(command, player.name());
1201  } else if (player.is_moderator()) {
1202  if (command == "signout") {
1203  LOG_SERVER << "Admin signed out: IP: "
1204  << client_address(socket) << "\tnick: "
1205  << player.name() << std::endl;
1206  player.set_moderator(false);
1207  // This string is parsed by the client!
1208  response << "You are no longer recognized as an administrator.";
1209  if(user_handler_) {
1210  user_handler_->set_is_moderator(player.name(), false);
1211  }
1212  } else {
1213  LOG_SERVER << "Admin Command: type: " << command
1214  << "\tIP: "<< client_address(socket)
1215  << "\tnick: "<< player.name() << std::endl;
1216  response << process_command(command, player.name());
1217  LOG_SERVER << response.str() << std::endl;
1218  }
1219  } else if (command == "help" || command.empty()) {
1220  response << help_msg;
1221  } else if (command == "admin" || command.find("admin ") == 0) {
1222  if (admin_passwd_.empty()) {
1223  send_server_message(socket, "No password set.");
1224  return;
1225  }
1226  std::string passwd;
1227  if (command.size() >= 6) passwd = command.substr(6);
1228  if (passwd == admin_passwd_) {
1229  LOG_SERVER << "New Admin recognized: IP: "
1230  << client_address(socket) << "\tnick: "
1231  << player.name() << std::endl;
1232  player.set_moderator(true);
1233  // This string is parsed by the client!
1234  response << "You are now recognized as an administrator.";
1235  if (user_handler_) {
1236  user_handler_->set_is_moderator(player.name(), true);
1237  }
1238  } else {
1239  WRN_SERVER << "FAILED Admin attempt with password: '" << passwd << "'\tIP: "
1240  << client_address(socket) << "\tnick: "
1241  << player.name() << std::endl;
1242  response << "Error: wrong password";
1243  }
1244  } else {
1245  response << "Error: unrecognized query: '" << command << "'\n" << help_msg;
1246  }
1247  send_server_message(socket, response.str());
1248 }
1249 
1251 {
1252  // Check if this server allows nick registration at all
1253  if(!user_handler_) {
1254  send_server_message(socket, "This server does not allow username registration.");
1255  return;
1256  }
1257 
1258  if(nickserv.child("register")) {
1259  try {
1260  (user_handler_->add_user(player_connections_.find(socket)->name(), (*nickserv.child("register"))["mail"].to_string(),
1261  (*nickserv.child("register"))["password"].to_string()));
1262 
1263  std::stringstream msg;
1264  msg << "Your username has been registered." <<
1265  // Warn that providing an email address might be a good idea
1266  ((*nickserv.child("register"))["mail"].empty() ?
1267  " It is recommended that you provide an email address for password recovery." : "");
1268  send_server_message(socket, msg.str());
1269 
1270  // Mark the player as registered and send the other clients
1271  // an update to dislpay this change
1272  player_connections_.find(socket)->info().mark_registered();
1273 
1274  simple_wml::document diff;
1276  "user", player_connections_.find(socket)->info().config_address(), diff);
1277  send_to_lobby(diff);
1278 
1279  } catch (user_handler::error& e) {
1280  send_server_message(socket, "There was an error registering your username. The error message was: "
1281  + e.message);
1282  }
1283  return;
1284  }
1285 
1286  // A user requested to update his password or mail
1287  if(nickserv.child("set")) {
1288  if(!(user_handler_->user_exists(player_connections_.find(socket)->name()))) {
1289  send_server_message(socket, "You are not registered. Please register first.");
1290  return;
1291  }
1292 
1293  const simple_wml::node& set = *(nickserv.child("set"));
1294 
1295  try {
1296  user_handler_->set_user_detail(player_connections_.find(socket)->name(), set["detail"].to_string(), set["value"].to_string());
1297 
1298  send_server_message(socket, "Your details have been updated.");
1299 
1300  } catch (user_handler::error& e) {
1301  send_server_message(socket, "There was an error updating your details. The error message was: "
1302  + e.message);
1303  }
1304 
1305  return;
1306  }
1307 
1308  // A user requested information about another user
1309  if(nickserv.child("details")) {
1310  send_server_message(socket, "Valid details for this server are: " +
1311  user_handler_->get_valid_details());
1312  return;
1313  }
1314 
1315  // A user requested a list of which details can be set
1316  if(nickserv.child("info")) {
1317  try {
1318  std::string res = user_handler_->user_info((*nickserv.child("info"))["name"].to_string());
1319  send_server_message(socket, res);
1320  } catch (user_handler::error& e) {
1321  send_server_message(socket, "There was an error looking up the details of the user '" +
1322  (*nickserv.child("info"))["name"].to_string() + "'. " +" The error message was: "
1323  + e.message);
1324  }
1325  return;
1326  }
1327 
1328  // A user requested to delete his nick
1329  if(nickserv.child("drop")) {
1330  if(!(user_handler_->user_exists(player_connections_.find(socket)->name()))) {
1331  send_server_message(socket, "You are not registered.");
1332  return;
1333  }
1334 
1335  // With the current policy of dissallowing to log in with a
1336  // registerd username without the password we should never get
1337  // to call this
1338  if(!(player_connections_.find(socket)->info().registered())) {
1339  send_server_message(socket, "You are not logged in.");
1340  return;
1341  }
1342 
1343  try {
1344  user_handler_->remove_user(player_connections_.find(socket)->name());
1345  send_server_message(socket, "Your username has been dropped.");
1346 
1347  // Mark the player as not registered and send the other clients
1348  // an update to dislpay this change
1349  player_connections_.find(socket)->info().mark_registered(false);
1350 
1351  simple_wml::document diff;
1353  "user", player_connections_.find(socket)->info().config_address(), diff);
1354  send_to_lobby(diff);
1355  } catch (user_handler::error& e) {
1356  send_server_message(socket, "There was an error dropping your username. The error message was: "
1357  + e.message);
1358  }
1359  return;
1360  }
1361 }
1362 
1364 {
1365  simple_wml::document relay_message;
1366  message.copy_into(relay_message.root().add_child("message"));
1367  send_to_lobby(relay_message, socket);
1368 }
1369 
1371 {
1372  if (graceful_restart) {
1373  static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
1374  send_to_player(socket, leave_game_doc);
1375  send_server_message(socket, "This server is shutting down. You aren't allowed to make new games. Please reconnect to the new server.");
1377  return;
1378  }
1379 
1380  player_connections_.modify(
1381  player_connections_.find(socket),
1382  boost::bind(&server::create_game, this, _1, boost::ref(create_game))
1383  );
1384 
1385  simple_wml::document diff;
1387  "user", player_connections_.find(socket)->info().config_address(), diff)) {
1388  send_to_lobby(diff);
1389  }
1390  return;
1391 }
1392 
1393 void server::create_game(player_record& host_record, simple_wml::node& create_game)
1394 {
1395  const std::string game_name = create_game["name"].to_string();
1396  const std::string game_password = create_game["password"].to_string();
1397 
1398  DBG_SERVER << client_address(host_record.socket()) << "\t" << host_record.info().name()
1399  << "\tcreates a new game: \"" << game_name << "\".\n";
1400 
1401  // Create the new game, remove the player from the lobby
1402  // and set the player as the host/owner.
1403  host_record.get_game().reset(
1404  new wesnothd::game(player_connections_, host_record.socket(), game_name, save_replays_, replay_save_path_),
1405  boost::bind(&server::cleanup_game, this, _1));
1406  wesnothd::game& g = *host_record.get_game();
1407  if(game_password.empty() == false) {
1408  g.set_password(game_password);
1409  }
1410 
1411  create_game.copy_into(g.level().root());
1412 }
1413 
1415 {
1417 
1418  simple_wml::node* const gamelist = games_and_users_list_.child("gamelist");
1419  assert(gamelist != NULL);
1420 
1421  // Send a diff of the gamelist with the game deleted to players in the lobby
1422  simple_wml::document diff;
1423  if(make_delete_diff(*gamelist, "gamelist", "game",
1424  game_ptr->description(), diff)) {
1425  send_to_lobby(diff);
1426  }
1427 
1428  // Delete the game from the games_and_users_list_.
1429  const simple_wml::node::child_list& games = gamelist->children("game");
1430  const simple_wml::node::child_list::const_iterator g =
1431  std::find(games.begin(), games.end(), game_ptr->description());
1432  if (g != games.end()) {
1433  const size_t index = g - games.begin();
1434  gamelist->remove_child("game", index);
1435  } else {
1436  // Can happen when the game ends before the scenario was transferred.
1437  LOG_SERVER << "Could not find game (" << game_ptr->id()
1438  << ") to delete in games_and_users_list_.\n";
1439  }
1440 }
1441 
1443 {
1444  const bool observer = join.attr("observe").to_bool();
1445  const std::string& password = join["password"].to_string();
1446  int game_id = join["id"].to_int();
1447 
1448  auto g_iter = player_connections_.get<game_t>().find(game_id);
1449  const boost::shared_ptr<game> g = g_iter->get_game();
1450 
1451  static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
1452  if (g_iter == player_connections_.get<game_t>().end()) {
1453  WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name()
1454  << "\tattempted to join unknown game:\t" << game_id << ".\n";
1455  async_send_doc(socket, leave_game_doc);
1456  send_server_message(socket, "Attempt to join unknown game.");
1458  return;
1459  } else if (!g->level_init()) {
1460  WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name()
1461  << "\tattempted to join uninitialized game:\t\"" << g->name()
1462  << "\" (" << game_id << ").\n";
1463  async_send_doc(socket, leave_game_doc);
1464  send_server_message(socket, "Attempt to join an uninitialized game.");
1466  return;
1467  } else if (player_connections_.find(socket)->info().is_moderator()) {
1468  // Admins are always allowed to join.
1469  } else if (g->registered_users_only() && !player_connections_.find(socket)->info().registered()) {
1470  async_send_doc(socket, leave_game_doc);
1471  send_server_message(socket, "Only registered users are allowed to join this game.");
1473  return;
1474  } else if (g->player_is_banned(socket)) {
1475  DBG_SERVER << client_address(socket) << "\tReject banned player: "
1476  << player_connections_.find(socket)->info().name() << "\tfrom game:\t\"" << g->name()
1477  << "\" (" << game_id << ").\n";
1478  async_send_doc(socket, leave_game_doc);
1479  send_server_message(socket, "You are banned from this game.");
1481  return;
1482  } else if(!observer && !g->password_matches(password)) {
1483  WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name()
1484  << "\tattempted to join game:\t\"" << g->name() << "\" ("
1485  << game_id << ") with bad password\n";
1486  async_send_doc(socket, leave_game_doc);
1487  send_server_message(socket, "Incorrect password.");
1489  return;
1490  }
1491  bool joined = g->add_player(socket, observer);
1492  if (!joined) {
1493  WRN_SERVER << client_address(socket) << "\t" << player_connections_.find(socket)->info().name()
1494  << "\tattempted to observe game:\t\"" << g->name() << "\" ("
1495  << game_id << ") which doesn't allow observers.\n";
1496  async_send_doc(socket, leave_game_doc);
1497  send_server_message(socket, "Attempt to observe a game that doesn't allow observers. (You probably joined the game shortly after it filled up.)");
1499  return;
1500  }
1501  player_connections_.modify(
1502  player_connections_.find(socket),
1503  boost::bind(&player_record::set_game, _1, player_connections_.get<game_t>().find(game_id)->get_game()));
1504  g->describe_slots();
1505 
1506  //send notification of changes to the game and user
1507  simple_wml::document diff;
1508  bool diff1 = make_change_diff(*games_and_users_list_.child("gamelist"),
1509  "gamelist", "game", g->description(), diff);
1510  bool diff2 = make_change_diff(games_and_users_list_.root(), NULL,
1511  "user", player_connections_.find(socket)->info().config_address(), diff);
1512  if (diff1 || diff2) {
1513  send_to_lobby(diff);
1514  }
1515 }
1516 
1518  DBG_SERVER << "in process_data_game...\n";
1519 
1520  auto p = player_connections_.find(socket);
1521  wesnothd::player& player = p->info();
1522  game& g = *(p->get_game());
1523 
1524  simple_wml::document& data = *doc;
1525 
1526  // If this is data describing the level for a game.
1527  if (doc->child("snapshot") || doc->child("scenario")) {
1528  if (!g.is_owner(socket)) {
1529  return;
1530  }
1531  // If this game is having its level data initialized
1532  // for the first time, and is ready for players to join.
1533  // We should currently have a summary of the game in g.level().
1534  // We want to move this summary to the games_and_users_list_, and
1535  // place a pointer to that summary in the game's description.
1536  // g.level() should then receive the full data for the game.
1537  if (!g.level_init()) {
1538  LOG_SERVER << client_address(socket) << "\t" << player.name()
1539  << "\tcreated game:\t\"" << g.name() << "\" ("
1540  << g.id() << ").\n";
1541  // Update our config object which describes the open games,
1542  // and save a pointer to the description in the new game.
1543  simple_wml::node* const gamelist = games_and_users_list_.child("gamelist");
1544  assert(gamelist != NULL);
1545  simple_wml::node& desc = gamelist->add_child("game");
1546  g.level().root().copy_into(desc);
1547  if (const simple_wml::node* m = doc->child("multiplayer")) {
1548  m->copy_into(desc);
1549  } else {
1550  WRN_SERVER << client_address(socket) << "\t" << player.name()
1551  << "\tsent scenario data in game:\t\"" << g.name() << "\" ("
1552  << g.id() << ") without a 'multiplayer' child.\n";
1553  // Set the description so it can be removed in delete_game().
1554  g.set_description(&desc);
1555  delete_game(g.id());
1556  send_server_message(socket, "The scenario data is missing the [multiplayer] tag which contains the game settings. Game aborted.");
1557  return;
1558  }
1559 
1560  g.set_description(&desc);
1561  desc.set_attr_dup("id", lexical_cast_default<std::string>(g.id()).c_str());
1562  } else {
1563  WRN_SERVER << client_address(socket) << "\t" << player.name()
1564  << "\tsent scenario data in game:\t\"" << g.name() << "\" ("
1565  << g.id() << ") although it's already initialized.\n";
1566  return;
1567  }
1568 
1569  assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
1570 
1571  simple_wml::node& desc = *g.description();
1572  // Update the game's description.
1573  // If there is no shroud, then tell players in the lobby
1574  // what the map looks like
1575  if (!data["mp_shroud"].to_bool()) {
1576  desc.set_attr_dup("map_data", (*wesnothd::game::starting_pos(data.root()))["map_data"]);
1577  }
1578  if (const simple_wml::node* e = data.child("era")) {
1579  if (!e->attr("require_era").to_bool(true)) {
1580  desc.set_attr("require_era", "no");
1581  }
1582  }
1583 
1584  if (data.attr("require_scenario").to_bool(false)) {
1585  desc.set_attr("require_scenario", "yes");
1586  }
1587 
1588  const simple_wml::node::child_list& mlist = data.children("modification");
1589  for(const simple_wml::node* m : mlist) {
1590  desc.add_child_at("modification", 0);
1591  desc.child("modification")->set_attr_dup("id", m->attr("id"));
1592  if (m->attr("require_modification").to_bool(false))
1593  desc.child("modification")->set_attr("require_modification", "yes");
1594  }
1595 
1596  // Record the full scenario in g.level()
1597  g.level().swap(data);
1598  // The host already put himself in the scenario so we just need
1599  // to update_side_data().
1600  //g.take_side(sock);
1601  g.update_side_data();
1602  g.describe_slots();
1603 
1604  assert(games_and_users_list_.child("gamelist")->children("game").empty() == false);
1605 
1606  // Send the update of the game description to the lobby.
1607  simple_wml::document diff;
1608  make_add_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", diff);
1609  send_to_lobby(diff);
1610  /** @todo FIXME: Why not save the level data in the history_? */
1611  return;
1612 // Everything below should only be processed if the game is already intialized.
1613  } else if (!g.level_init()) {
1614  WRN_SERVER << client_address(socket) << "\tReceived unknown data from: "
1615  << player.name() << " (socket:" << socket
1616  << ") while the scenario wasn't yet initialized.\n" << data.output();
1617  return;
1618  // If the host is sending the next scenario data.
1619  } else if (const simple_wml::node* scenario = data.child("store_next_scenario")) {
1620  if (!g.is_owner(socket)) return;
1621  if (!g.level_init()) {
1622  WRN_SERVER << client_address(socket) << "\tWarning: "
1623  << player.name() << "\tsent [store_next_scenario] in game:\t\""
1624  << g.name() << "\" (" << g.id()
1625  << ") while the scenario is not yet initialized.";
1626  return;
1627  }
1628  g.save_replay();
1630  // Record the full scenario in g.level()
1631  g.level().clear();
1632  scenario->copy_into(g.level().root());
1633 
1634  if (g.description() == NULL) {
1635  ERR_SERVER << client_address(socket) << "\tERROR: \""
1636  << g.name() << "\" (" << g.id()
1637  << ") is initialized but has no description_.\n";
1638  return;
1639  }
1640  simple_wml::node& desc = *g.description();
1641  // Update the game's description.
1642  if (const simple_wml::node* m = scenario->child("multiplayer")) {
1643  m->copy_into(desc);
1644  } else {
1645  WRN_SERVER << client_address(socket) << "\t" << player.name()
1646  << "\tsent scenario data in game:\t\"" << g.name() << "\" ("
1647  << g.id() << ") without a 'multiplayer' child.\n";
1648  delete_game(g.id());
1649  send_server_message(socket, "The scenario data is missing the [multiplayer] tag which contains the game settings. Game aborted.");
1650  return;
1651  }
1652 
1653  // If there is no shroud, then tell players in the lobby
1654  // what the map looks like.
1656  desc.set_attr_dup("map_data", s["mp_shroud"].to_bool() ? "" :
1657  s["map_data"]);
1658  if (const simple_wml::node* e = data.child("era")) {
1659  if (!e->attr("require_era").to_bool(true)) {
1660  desc.set_attr("require_era", "no");
1661  }
1662  }
1663 
1664  if (data.attr("require_scenario").to_bool(false)) {
1665  desc.set_attr("require_scenario", "yes");
1666  }
1667 
1668  // Tell everyone that the next scenario data is available.
1669  static simple_wml::document notify_next_scenario(
1670  "[notify_next_scenario]\n[/notify_next_scenario]\n",
1672  g.send_data(notify_next_scenario, socket);
1673 
1674  // Send the update of the game description to the lobby.
1676 
1677  return;
1678  // A mp client sends a request for the next scenario of a mp campaign.
1679  } else if (data.child("load_next_scenario")) {
1680  g.load_next_scenario(socket);
1681  return;
1682  } else if (data.child("start_game")) {
1683  if (!g.is_owner(socket)) return;
1684  //perform controller tweaks, assigning sides as human for their owners etc.
1686  // Send notification of the game starting immediately.
1687  // g.start_game() will send data that assumes
1688  // the [start_game] message has been sent
1689  g.send_data(data, socket);
1690  g.start_game(socket);
1691 
1692  //update the game having changed in the lobby
1694  return;
1695  } else if (data.child("update_game")) {
1696  g.update_game();
1698  return;
1699  } else if (data.child("leave_game")) {
1700  // May be better to just let remove_player() figure out when a game ends.
1701  if ((g.is_player(socket) && g.nplayers() == 1)
1702  || (g.is_owner(socket) && (!g.started() || g.nplayers() == 0))) {
1703  // Remove the player in delete_game() with all other remaining
1704  // ones so he gets the updated gamelist.
1705  delete_game(g.id());
1706  } else {
1707  g.remove_player(socket);
1709  g.describe_slots();
1710 
1711  // Send all other players in the lobby the update to the gamelist.
1712  simple_wml::document diff;
1713  bool diff1 = make_change_diff(*games_and_users_list_.child("gamelist"),
1714  "gamelist", "game", g.description(), diff);
1715  bool diff2 = make_change_diff(games_and_users_list_.root(), NULL,
1716  "user", player.config_address(), diff);
1717  if (diff1 || diff2) {
1718  send_to_lobby(diff, socket);
1719  }
1720 
1721  // Send the player who has quit the gamelist.
1723  }
1724  return;
1725  // If this is data describing side changes by the host.
1726  } else if (const simple_wml::node* diff = data.child("scenario_diff")) {
1727  if (!g.is_owner(socket)) return;
1728  g.level().root().apply_diff(*diff);
1729  const simple_wml::node* cfg_change = diff->child("change_child");
1730  if (cfg_change
1731  /**&& cfg_change->child("side") it is very likeley that
1732  the diff changes a side so this check isn't that important.
1733  Note that [side] is not at toplevel but inside
1734  [scenario] or [snapshot] **/) {
1735  g.update_side_data();
1736  }
1737  if (g.describe_slots()) {
1739  }
1740  g.send_data(data, socket);
1741  return;
1742  // If a player changes his faction.
1743  } else if (data.child("change_faction")) {
1744  g.send_data(data, socket);
1745  return;
1746  // If the owner of a side is changing the controller.
1747  } else if (const simple_wml::node *change = data.child("change_controller")) {
1748  g.transfer_side_control(socket, *change);
1749  if (g.describe_slots()) {
1751  }
1752  return;
1753  // If all observers should be muted. (toggles)
1754  } else if (data.child("muteall")) {
1755  if (!g.is_owner(socket)) {
1756  g.send_server_message("You cannot mute: not the game host.", socket);
1757  return;
1758  }
1759  g.mute_all_observers();
1760  return;
1761  // If an observer should be muted.
1762  } else if (const simple_wml::node* mute = data.child("mute")) {
1763  g.mute_observer(*mute, socket);
1764  return;
1765  // If an observer should be unmuted.
1766  } else if (const simple_wml::node* unmute = data.child("unmute")) {
1767  g.unmute_observer(*unmute, socket);
1768  return;
1769  // The owner is kicking/banning someone from the game.
1770  } else if (data.child("kick") || data.child("ban")) {
1771  bool ban = (data.child("ban") != NULL);
1772  const socket_ptr user =
1773  (ban ? g.ban_user(*data.child("ban"), socket)
1774  : g.kick_member(*data.child("kick"), socket));
1775  if (user) {
1777  if (g.describe_slots()) {
1778  update_game_in_lobby(g, user);
1779  }
1780  // Send all other players in the lobby the update to the gamelist.
1781  simple_wml::document diff;
1783  "gamelist", "game", g.description(), diff);
1785  player_connections_.find(user)->info().config_address(), diff);
1786  send_to_lobby(diff, socket);
1787  // Send the removed user the lobby game list.
1789  }
1790  return;
1791  } else if (const simple_wml::node* unban = data.child("unban")) {
1792  g.unban_user(*unban, socket);
1793  return;
1794  // If info is being provided about the game state.
1795  } else if (const simple_wml::node* info = data.child("info")) {
1796  if (!g.is_player(socket)) return;
1797  if ((*info)["type"] == "termination") {
1798  g.set_termination_reason((*info)["condition"].to_string());
1799  if ((*info)["condition"].to_string() == "out of sync") {
1800  g.send_server_message_to_all(player.name() + " reports out of sync errors.");
1801  }
1802  }
1803  return;
1804  } else if (data.child("turn")) {
1805  // Notify the game of the commands, and if it changes
1806  // the description, then sync the new description
1807  // to players in the lobby.
1808  if (g.process_turn(data, socket)) {
1810  }
1811  return;
1812  } else if (data.child("whiteboard")) {
1813  g.process_whiteboard(data,socket);
1814  return;
1815  } else if (data.child("change_turns_wml")) {
1816  g.process_change_turns_wml(data,socket);
1818  return;
1819  } else if (simple_wml::node* sch = data.child("request_choice")) {
1820  g.handle_choice(*sch, socket);
1821  return;
1822  } else if (data.child("message")) {
1823  g.process_message(data, socket);
1824  return;
1825  } else if (data.child("stop_updates")) {
1826  g.send_data(data, socket);
1827  return;
1828  // Data to ignore.
1829  } else if (data.child("error")
1830  || data.child("side_secured")
1831  || data.root().has_attr("failed")
1832  || data.root().has_attr("side_drop")
1833  || data.root().has_attr("side")) {
1834  return;
1835  }
1836 
1837  WRN_SERVER << client_address(socket) << "\tReceived unknown data from: "
1838  << player.name() << " (socket:" << socket << ") in game: \""
1839  << g.name() << "\" (" << g.id() << ")\n" << data.output();
1840 }
1841 
1842 typedef std::map<socket_ptr, std::deque<boost::shared_ptr<simple_wml::document> > > SendQueue;
1843 SendQueue send_queue;
1844 void handle_send_to_player(socket_ptr socket);
1845 
1847 {
1848  SendQueue::iterator iter = send_queue.find(socket);
1849  if(iter == send_queue.end()) {
1850  send_queue[socket];
1851  async_send_doc(socket, doc,
1854  );
1855  } else {
1856  send_queue[socket].push_back(boost::shared_ptr<simple_wml::document>(doc.clone()));
1857  }
1858 }
1859 
1861 {
1862  if(send_queue[socket].empty()) {
1863  send_queue.erase(socket);
1864  } else {
1865  async_send_doc(socket, *(send_queue[socket].front()),
1868  );
1869  send_queue[socket].pop_front();
1870  }
1871 }
1872 
1874 {
1876  simple_wml::node& msg = server_message.root().add_child("message");
1877  msg.set_attr("sender", "server");
1878  msg.set_attr_dup("message", message.c_str());
1879  send_to_player(socket, server_message);
1880 }
1881 
1883 {
1884  std::string ip = client_address(socket);
1885 
1886  auto iter = player_connections_.find(socket);
1887  if(iter == player_connections_.end())
1888  return;
1889 
1890  const boost::shared_ptr<game> g = iter->get_game();
1891  if(g)
1892  g->remove_player(socket, true, false);
1893 
1895  const size_t index = std::find(users.begin(), users.end(), iter->info().config_address()) - users.begin();
1896 
1897  // Notify other players in lobby
1898  simple_wml::document diff;
1899  if(make_delete_diff(games_and_users_list_.root(), NULL, "user",
1900  iter->info().config_address(), diff)) {
1901  send_to_lobby(diff, socket);
1902  }
1903  games_and_users_list_.root().remove_child("user", index);
1904 
1905  LOG_SERVER << ip << "\t" << iter->info().name()
1906  << "\twas logged off" << "\n";
1907 
1908  // Find the matching nick-ip pair in the log and update the sign off time
1909  connection_log ip_name = connection_log(iter->info().name(), ip, 0);
1910  std::deque<connection_log>::iterator i = std::find(ip_log_.begin(), ip_log_.end(), ip_name);
1911  if(i != ip_log_.end()) {
1912  i->log_off = time(nullptr);
1913  }
1914 
1915  player_connections_.erase(iter);
1916 
1917  if(socket->is_open())
1918  socket->close();
1919 }
1920 
1922 {
1923  for(const auto& player : player_connections_.get<game_t>().equal_range(0))
1924  if(player.socket() != exclude)
1925  send_to_player(player.socket(), data);
1926 }
1927 
1929 {
1930  for(const auto& player : player_connections_.get<game_t>().equal_range(0)) {
1931  if(player.socket() != exclude)
1932  send_server_message(player.socket(), message);
1933  }
1934 }
1935 
1937 {
1938  for(const auto& player: player_connections_) {
1939  if(player.socket() != exclude)
1940  send_server_message(player.socket(), message);
1941  }
1942 }
1943 
1944 void server::run() {
1945  try {
1946  io_service_.run();
1947  LOG_SERVER << "Server has shut down because event loop is out of work\n";
1948  } catch(const server_shutdown& e) {
1949  LOG_SERVER << "Server has been shut down: " << e.what() << "\n";
1950  }
1951 
1952  /*
1953  int graceful_counter = 0;
1954 
1955  for (int loop = 0;; ++loop) {
1956  // Try to run with 50 FPS all the time
1957  // Server will respond a bit faster under heavy load
1958  fps_limit_.limit();
1959  try {
1960  // We are going to waith 10 seconds before shutting down so users can get out of game.
1961  if (graceful_restart && games_.empty() && ++graceful_counter > 500 )
1962  {
1963  // TODO: We should implement client side autoreconnect.
1964  // Idea:
1965  // server should send [reconnect]host=host,port=number[/reconnect]
1966  // Then client would reconnect to new server automatically.
1967  // This would also allow server to move to new port or address if there is need
1968 
1969  process_command("msg All games ended. Shutting down now. Reconnect to the new server instance.", "system");
1970  throw network::error("shut down");
1971  }
1972 
1973  if (config_reload == 1) {
1974  cfg_ = read_config();
1975  load_config();
1976  config_reload = 0;
1977  }
1978 
1979  // Process commands from the server socket/fifo
1980  std::string admin_cmd;
1981  if (input_ && input_->read_line(admin_cmd)) {
1982  LOG_SERVER << "Admin Command: type: " << admin_cmd << "\n";
1983  const std::string res = process_command(admin_cmd, "*socket*");
1984  // Only mark the response if we fake the issuer (i.e. command comes from IRC or so)
1985  if (admin_cmd.at(0) == '+') {
1986  LOG_SERVER << "[admin_command_response]\n" << res << "\n" << "[/admin_command_response]\n";
1987  } else {
1988  LOG_SERVER << res << "\n";
1989  }
1990  }
1991 
1992  time_t now = time(NULL);
1993  if (last_ping_ + network::ping_interval <= now) {
1994  if (lan_server_ && players_.empty() && last_user_seen_time_ + lan_server_ < now)
1995  {
1996  LOG_SERVER << "Lan server has been empty for " << (now - last_user_seen_time_) << " seconds. Shutting down!\n";
1997  // We have to shutdown
1998  graceful_restart = true;
1999  }
2000  // and check if bans have expired
2001  ban_manager_.check_ban_times(now);
2002  // Make sure we log stats every 5 minutes
2003  if (last_stats_ + 5 * 60 <= now) {
2004  dump_stats(now);
2005  if (rooms_.dirty()) rooms_.write_rooms();
2006  }
2007 
2008  // Cleaning the user_handler once a day should be more than enough
2009  if (last_uh_clean_ + 60 * 60 * 24 <= now) {
2010  clean_user_handler(now);
2011  }
2012 
2013  // Send network stats every hour
2014  static int prev_hour = localtime(&now)->tm_hour;
2015  if (prev_hour != localtime(&now)->tm_hour)
2016  {
2017  prev_hour = localtime(&now)->tm_hour;
2018  LOG_SERVER << network::get_bandwidth_stats();
2019 
2020  }
2021 
2022  // send a 'ping' to all players to detect ghosts
2023  DBG_SERVER << "Pinging inactive players.\n" ;
2024  std::ostringstream strstr ;
2025  strstr << "ping=\"" << now << "\"" ;
2026  simple_wml::document ping( strstr.str().c_str(),
2027  simple_wml::INIT_COMPRESSED );
2028  simple_wml::string_span s = ping.output_compressed();
2029  BOOST_FOREACH(network::connection sock, ghost_players_) {
2030  if (!lg::debug.dont_log(log_server)) {
2031  wesnothd::player_map::const_iterator i = players_.find(sock);
2032  if (i != players_.end()) {
2033  DBG_SERVER << "Pinging " << i->second.name() << "(" << i->first << ").\n";
2034  } else {
2035  ERR_SERVER << "Player " << sock << " is in ghost_players_ but not in players_." << std::endl;
2036  }
2037  }
2038  network::send_raw_data(s.begin(), s.size(), sock, "ping") ;
2039  }
2040 
2041  // Copy new player list on top of ghost_players_ list.
2042  // Only a single thread should be accessing this
2043  // Erase before we copy - speeds inserts
2044  ghost_players_.clear();
2045  BOOST_FOREACH(const wesnothd::player_map::value_type v, players_) {
2046  ghost_players_.insert(v.first);
2047  }
2048  last_ping_ = now;
2049  }
2050 
2051  network::process_send_queue();
2052 
2053  network::connection sock = network::accept_connection();
2054  if (sock) {
2055  const std::string ip = network::ip_address(sock);
2056  const std::string reason = is_ip_banned(ip);
2057  if (!reason.empty()) {
2058  LOG_SERVER << ip << "\trejected banned user. Reason: " << reason << "\n";
2059  send_error(sock, "You are banned. Reason: " + reason);
2060  network::queue_disconnect(sock);
2061  } else if (ip_exceeds_connection_limit(ip)) {
2062  LOG_SERVER << ip << "\trejected ip due to excessive connections\n";
2063  send_error(sock, "Too many connections from your IP.");
2064  network::queue_disconnect(sock);
2065  } else {
2066  DBG_SERVER << ip << "\tnew connection accepted. (socket: "
2067  << sock << ")\n";
2068  send_doc(version_query_response_, sock);
2069  }
2070  not_logged_in_.insert(sock);
2071  }
2072 
2073  static int sample_counter = 0;
2074 
2075  std::vector<char> buf;
2076  network::bandwidth_in_ptr bandwidth_type;
2077  while ((sock = network::receive_data(buf, &bandwidth_type)) != network::null_connection) {
2078  metrics_.service_request();
2079 
2080  if(buf.empty()) {
2081  WRN_SERVER << "received empty packet" << std::endl;
2082  continue;
2083  }
2084 
2085  const bool sample = request_sample_frequency >= 1 && (sample_counter++ % request_sample_frequency) == 0;
2086 
2087  const clock_t before_parsing = get_cpu_time(sample);
2088 
2089  char* buf_ptr = new char [buf.size()];
2090  memcpy(buf_ptr, &buf[0], buf.size());
2091  simple_wml::string_span compressed_buf(buf_ptr, buf.size());
2092  boost::scoped_ptr<simple_wml::document> data_ptr;
2093  try {
2094  data_ptr.reset(new simple_wml::document(compressed_buf)); // might throw a simple_wml::error
2095  data_ptr->take_ownership_of_buffer(buf_ptr);
2096 
2097  } catch (simple_wml::error& e) {
2098  WRN_CONFIG << "simple_wml error in received data: " << e.message << std::endl;
2099  send_error(sock, "Invalid WML received: " + e.message);
2100  delete [] buf_ptr;
2101  continue;
2102  } catch(...) {
2103  delete [] buf_ptr;
2104  throw;
2105  }
2106 
2107  simple_wml::document& data = *data_ptr;
2108  std::vector<char>().swap(buf);
2109 
2110  const clock_t after_parsing = get_cpu_time(sample);
2111 
2112  process_data(sock, data);
2113 
2114  bandwidth_type->set_type(data.root().first_child().to_string());
2115  if(sample) {
2116  const clock_t after_processing = get_cpu_time(sample);
2117  metrics_.record_sample(data.root().first_child(),
2118  after_parsing - before_parsing,
2119  after_processing - after_parsing);
2120  }
2121 
2122  }
2123 
2124  metrics_.no_requests();
2125 
2126  } catch(simple_wml::error& e) {
2127  WRN_CONFIG << "Warning: error in received data: " << e.message << std::endl;
2128  } catch(network::error& e) {
2129  if (e.message == "shut down") {
2130  LOG_SERVER << "Try to disconnect all users...\n";
2131  for (wesnothd::player_map::const_iterator pl = players_.begin();
2132  pl != players_.end(); ++pl)
2133  {
2134  network::disconnect(pl->first);
2135  }
2136  LOG_SERVER << "Shutting server down.\n";
2137  break;
2138  }
2139  if (!e.socket) {
2140  ERR_SERVER << "network error: " << e.message << std::endl;
2141  continue;
2142  }
2143  DBG_SERVER << "socket closed: " << e.message << "\n";
2144  const std::string ip = network::ip_address(e.socket);
2145  if (proxy::is_proxy(e.socket)) {
2146  LOG_SERVER << ip << "\tProxy user disconnected.\n";
2147  proxy::disconnect(e.socket);
2148  e.disconnect();
2149  DBG_SERVER << "done closing socket...\n";
2150  continue;
2151  }
2152  // Was the user already logged in?
2153  const wesnothd::player_map::iterator pl_it = players_.find(e.socket);
2154  if (pl_it == players_.end()) {
2155  std::set<network::connection>::iterator i = not_logged_in_.find(e.socket);
2156  if (i != not_logged_in_.end()) {
2157  DBG_SERVER << ip << "\tNot logged in user disconnected.\n";
2158  not_logged_in_.erase(i);
2159  } else {
2160  WRN_SERVER << ip << "\tWarning: User disconnected right after the connection was accepted." << std::endl;
2161  }
2162  e.disconnect();
2163  DBG_SERVER << "done closing socket...\n";
2164  continue;
2165  }
2166  const simple_wml::node::child_list& users = games_and_users_list_.root().children("user");
2167  const size_t index = std::find(users.begin(), users.end(), pl_it->second.config_address()) - users.begin();
2168  if (index < users.size()) {
2169  simple_wml::document diff;
2170  if(make_delete_diff(games_and_users_list_.root(), NULL, "user",
2171  pl_it->second.config_address(), diff)) {
2172  for (t_games::const_iterator g = games_.begin(); g != games_.end(); ++g) {
2173  // Note: This string is parsed by the client to identify lobby leave messages!
2174  g->send_server_message_to_all(pl_it->second.name() + " has disconnected");
2175  }
2176  rooms_.lobby().send_data(diff, e.socket);
2177  }
2178 
2179  games_and_users_list_.root().remove_child("user", index);
2180  } else {
2181  ERR_SERVER << ip << "ERROR: Could not find user to remove: "
2182  << pl_it->second.name() << " in games_and_users_list_.\n";
2183  }
2184  // Was the player in the lobby or a game?
2185  if (rooms_.in_lobby(e.socket)) {
2186  rooms_.remove_player(e.socket);
2187  LOG_SERVER << ip << "\t" << pl_it->second.name()
2188  << "\thas logged off. (socket: " << e.socket << ")\n";
2189 
2190  } else {
2191  for (t_games::iterator g = games_.begin();
2192  g != games_.end(); ++g)
2193  {
2194  if (!g->is_member(e.socket)) {
2195  continue;
2196  }
2197  // Did the last player leave?
2198  if (g->remove_player(e.socket, true)) {
2199  delete_game(g);
2200  break;
2201  } else {
2202  g->describe_slots();
2203 
2204  update_game_in_lobby(*g, e.socket);
2205  }
2206  break;
2207  }
2208  }
2209 
2210  // Find the matching nick-ip pair in the log and update the sign off time
2211  connection_log ip_name = connection_log(pl_it->second.name(), ip, 0);
2212  std::deque<connection_log>::iterator i = std::find(ip_log_.begin(), ip_log_.end(), ip_name);
2213  if(i != ip_log_.end()) {
2214  i->log_off = time(NULL);
2215  }
2216 
2217  players_.erase(pl_it);
2218  ghost_players_.erase(e.socket);
2219  if (lan_server_)
2220  {
2221  last_user_seen_time_ = time(0);
2222  }
2223  e.disconnect();
2224  DBG_SERVER << "done closing socket...\n";
2225 
2226  // Catch user_handler exceptions here, to prevent the
2227  // server from going down completely. Once we are sure
2228  // all user_handler exceptions are caught correctly
2229  // this can removed.
2230  } catch (user_handler::error& e) {
2231  ERR_SERVER << "Uncaught user_handler exception: " << e.message << std::endl;
2232  }
2233  }
2234  */
2235 }
2236 
2238  if (restart_command.empty())
2239  return;
2240 
2241  // Example config line:
2242  // restart_command="./wesnothd-debug -d -c ~/.wesnoth1.5/server.cfg"
2243  // remember to make new one as a daemon or it will block old one
2244  if (std::system(restart_command.c_str())) {
2245  ERR_SERVER << "Failed to start new server with command: " << restart_command << std::endl;
2246  } else {
2247  LOG_SERVER << "New server started with command: " << restart_command << "\n";
2248  }
2249 }
2250 
2252  utils::strip(query);
2253 
2254  if (issuer_name == "*socket*" && query.at(0) == '+') {
2255  // The first argument might be "+<issuer>: ".
2256  // In that case we use +<issuer>+ as the issuer_name.
2257  // (Mostly used for communication with IRC.)
2258  std::string::iterator issuer_end =
2259  std::find(query.begin(), query.end(), ':');
2260  std::string issuer(query.begin() + 1, issuer_end);
2261  if (!issuer.empty()) {
2262  issuer_name = "+" + issuer + "+";
2263  query = std::string(issuer_end + 1, query.end());
2264  utils::strip(query);
2265  }
2266  }
2267 
2268  const std::string::iterator i = std::find(query.begin(), query.end(), ' ');
2269 
2270  try {
2271 
2272  const std::string command = utf8::lowercase(std::string(query.begin(), i));
2273  std::string parameters = (i == query.end() ? "" : std::string(i + 1, query.end()));
2274  utils::strip(parameters);
2275 
2276  std::ostringstream out;
2278  if(handler_itor == cmd_handlers_.end()) {
2279  out << "Command '" << command << "' is not recognized.\n" << help_msg;
2280  } else {
2281  const cmd_handler &handler = handler_itor->second;
2282  try {
2283  handler(this, issuer_name, query, parameters, &out);
2284  } catch (std::bad_function_call & ex) {
2285  ERR_SERVER << "While handling a command '" << command << "', caught a std::bad_function_call exception.\n";
2286  ERR_SERVER << ex.what() << std::endl;
2287  out << "An internal server error occurred (std::bad_function_call) while executing '" << command << "'\n";
2288  }
2289  }
2290 
2291  return out.str();
2292 
2293  } catch ( utf8::invalid_utf8_exception & e ) {
2294  std::string msg = "While handling a command, caught an invalid utf8 exception: ";
2295  msg += e.what();
2296  ERR_SERVER << msg << std::endl;
2297  return (msg + '\n');
2298  }
2299 }
2300 
2301 // Shutdown, restart and sample commands can only be issued via the socket.
2302 void server::shut_down_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2303  assert(out != NULL);
2304 
2305  if (issuer_name != "*socket*" && !allow_remote_shutdown_) {
2306  *out << denied_msg;
2307  return;
2308  }
2309  if (parameters == "now") {
2310  throw server_shutdown("shut down by admin command");
2311  } else {
2312  // Graceful shut down.
2313  graceful_restart = true;
2314  acceptor_.close();
2315  timer_.expires_from_now(boost::posix_time::seconds(10));
2316  timer_.async_wait(boost::bind(&server::handle_graceful_timeout, this, _1));
2317  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);
2318  *out << "Server is doing graceful shut down.";
2319  }
2320 }
2321 
2322 void server::restart_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2323  assert(out != NULL);
2324 
2325  if (issuer_name != "*socket*" && !allow_remote_shutdown_) {
2326  *out << denied_msg;
2327  return;
2328  }
2329 
2330  if (restart_command.empty()) {
2331  *out << "No restart_command configured! Not restarting.";
2332  } else {
2333  graceful_restart = true;
2334  acceptor_.close();
2335  timer_.expires_from_now(boost::posix_time::seconds(10));
2336  timer_.async_wait(boost::bind(&server::handle_graceful_timeout, this, _1));
2337  start_new_server();
2338  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);
2339  *out << "New server started.";
2340  }
2341 }
2342 
2343 void server::sample_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2344  assert(out != NULL);
2345 
2346  if (parameters.empty()) {
2347  *out << "Current sample frequency: " << request_sample_frequency;
2348  return;
2349  } else if (issuer_name != "*socket*") {
2350  *out << denied_msg;
2351  return;
2352  }
2353  request_sample_frequency = atoi(parameters.c_str());
2354  if (request_sample_frequency <= 0) {
2355  *out << "Sampling turned off.";
2356  } else {
2357  *out << "Sampling every " << request_sample_frequency << " requests.";
2358  }
2359 }
2360 
2361 void server::help_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2362  assert(out != NULL);
2363  *out << help_msg;
2364 }
2365 
2366 void server::stats_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2367  assert(out != NULL);
2368 
2369  *out << "Number of games = " << games().size()
2370  << "\nTotal number of users = " << player_connections_.size() << "\n";
2371 }
2372 
2373 void server::metrics_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2374  assert(out != NULL);
2375  *out << metrics_;
2376 }
2377 
2378 void server::requests_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2379  assert(out != NULL);
2380  metrics_.requests(*out);
2381 }
2382 
2383 void server::games_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2384  assert(out != NULL);
2385  metrics_.games(*out);
2386 }
2387 
2388 void server::wml_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2389  assert(out != NULL);
2390  *out << simple_wml::document::stats();
2391 }
2392 
2393 void server::netstats_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream* /*out*/) {
2394  /*
2395  assert(out != NULL);
2396 
2397  network::pending_statistics stats = network::get_pending_stats();
2398  *out << "Network stats:\nPending send buffers: "
2399  << stats.npending_sends << "\nBytes in buffers: "
2400  << stats.nbytes_pending_sends << "\n";
2401 
2402  try {
2403 
2404  if (utf8::lowercase(parameters) == "all") {
2405  *out << network::get_bandwidth_stats_all();
2406  } else {
2407  *out << network::get_bandwidth_stats(); // stats from previuos hour
2408  }
2409 
2410  } catch ( utf8::invalid_utf8_exception & e ) {
2411  ERR_SERVER << "While handling a netstats command, caught an invalid utf8 exception: " << e.what() << std::endl;
2412  }
2413  */
2414 }
2415 
2416 void server::adminmsg_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2417  assert(out != NULL);
2418 
2419  if (parameters == "") {
2420  *out << "You must type a message.";
2421  return;
2422  }
2423 
2424  const std::string& sender = issuer_name;
2425  const std::string& message = parameters;
2426  LOG_SERVER << "Admin message: <" << sender << (message.find("/me ") == 0
2427  ? std::string(message.begin() + 3, message.end()) + ">"
2428  : "> " + message) << "\n";
2429 
2431  simple_wml::node& msg = data.root().add_child("whisper");
2432  msg.set_attr_dup("sender", ("admin message from " + sender).c_str());
2433  msg.set_attr_dup("message", message.c_str());
2434  int n = 0;
2435  for (const auto& player : player_connections_) {
2436  if (player.info().is_moderator()) {
2437  ++n;
2438  send_to_player(player.socket(), data);
2439  }
2440  }
2441 
2442  if (n == 0) {
2443  *out << "Sorry, no admin available right now. But your message got logged.";
2444  return;
2445  }
2446 
2447  *out << "Message sent to " << n << " admins.";
2448 }
2449 
2450 void server::pm_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2451  assert(out != NULL);
2452 
2453  std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
2454  if (first_space == parameters.end()) {
2455  *out << "You must name a receiver.";
2456  return;
2457  }
2458 
2459  const std::string& sender = issuer_name;
2460  const std::string receiver(parameters.begin(), first_space);
2461  std::string message(first_space + 1, parameters.end());
2463  if (message.empty()) {
2464  *out << "You must type a message.";
2465  return;
2466  }
2467 
2469  simple_wml::node& msg = data.root().add_child("whisper");
2470  // This string is parsed by the client!
2471  msg.set_attr_dup("sender", ("server message from " + sender).c_str());
2472  msg.set_attr_dup("message", message.c_str());
2473  for (const auto& player : player_connections_) {
2474  if (receiver != player.info().name().c_str()) {
2475  continue;
2476  }
2477  send_to_player(player.socket(), data);
2478  *out << "Message to " << receiver << " successfully sent.";
2479  return;
2480  }
2481 
2482  *out << "No such nick: " << receiver;
2483 }
2484 
2485 void server::msg_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2486  assert(out != nullptr);
2487 
2488  if (parameters == "") {
2489  *out << "You must type a message.";
2490  return;
2491  }
2492 
2493  send_server_message_to_all(parameters);
2494 
2495  LOG_SERVER << "<server" << (parameters.find("/me ") == 0
2496  ? std::string(parameters.begin() + 3, parameters.end()) + ">"
2497  : "> " + parameters) << "\n";
2498 
2499  *out << "message '" << parameters << "' relayed to players";
2500 }
2501 
2502 void server::lobbymsg_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2503  assert(out != nullptr);
2504 
2505  if (parameters == "") {
2506  *out << "You must type a message.";
2507  return;
2508  }
2509 
2510  send_server_message_to_lobby(parameters);
2511  LOG_SERVER << "<server" << (parameters.find("/me ") == 0
2512  ? std::string(parameters.begin() + 3, parameters.end()) + ">"
2513  : "> " + parameters) << "\n";
2514 
2515  *out << "message '" << parameters << "' relayed to players";
2516 }
2517 
2518 void server::status_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2519  assert(out != NULL);
2520 
2521  *out << "STATUS REPORT for '" << parameters << "'";
2522  bool found_something = false;
2523  // If a simple username is given we'll check for its IP instead.
2524  if (utils::isvalid_username(parameters)) {
2525  for (const auto& player : player_connections_) {
2526  if (parameters == player.info().name()) {
2527  parameters = client_address(player.socket());
2528  found_something = true;
2529  break;
2530  }
2531  }
2532  if (!found_something) {
2533  //out << "\nNo match found. You may want to check with 'searchlog'.";
2534  //return out.str();
2535  *out << process_command("searchlog " + parameters, issuer_name);
2536  return;
2537  }
2538  }
2539  const bool match_ip = (std::count(parameters.begin(), parameters.end(), '.') >= 1);
2540  for (const auto& player : player_connections_) {
2541  if (parameters == "" || parameters == "*"
2542  || (match_ip && utils::wildcard_string_match(client_address(player.socket()), parameters))
2543  || (!match_ip && utils::wildcard_string_match(player.info().name(), parameters))) {
2544  found_something = true;
2545  *out << std::endl << player_status(player);
2546  }
2547  }
2548  if (!found_something) *out << "\nNo match found. You may want to check with 'searchlog'.";
2549 }
2550 
2551 void server::clones_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& /*parameters*/, std::ostringstream *out) {
2552  assert(out != NULL);
2553 
2554  *out << "CLONES STATUS REPORT";
2555  std::set<std::string> clones;
2556  for (player_connections::iterator it = player_connections_.begin(); it != player_connections_.end(); ++it) {
2557  if (clones.find(client_address(it->socket())) != clones.end()) continue;
2558  bool found = false;
2559  for (player_connections::iterator clone = boost::next(it); clone != player_connections_.end(); ++clone) {
2560  if (client_address(it->socket()) == client_address(clone->socket())) {
2561  if (!found) {
2562  found = true;
2563  clones.insert(client_address(it->socket()));
2564  *out << std::endl << player_status(*it);
2565  }
2566  *out << std::endl << player_status(*clone);
2567  }
2568  }
2569  }
2570  if (clones.empty()) {
2571  *out << "No clones found.";
2572  }
2573 }
2574 
2575 void server::bans_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2576  assert(out != nullptr);
2577 
2578  try
2579  {
2580 
2581  if (parameters.empty()) {
2582  ban_manager_.list_bans(*out);
2583  } else if (utf8::lowercase(parameters) == "deleted") {
2585  } else if (utf8::lowercase(parameters).find("deleted") == 0) {
2586  std::string mask = parameters.substr(7);
2588  } else {
2589  ban_manager_.list_bans(*out, utils::strip(parameters));
2590  }
2591 
2592  } catch ( utf8::invalid_utf8_exception & e ) {
2593  ERR_SERVER << "While handling bans, caught an invalid utf8 exception: " << e.what() << std::endl;
2594  }
2595 }
2596 
2597 void server::ban_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2598  assert(out != NULL);
2599 
2600  bool banned = false;
2601  std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
2602 
2603  if (first_space == parameters.end()) {
2604  *out << ban_manager_.get_ban_help();
2605  return;
2606  }
2607 
2608  std::string::iterator second_space = std::find(first_space + 1, parameters.end(), ' ');
2609  const std::string target(parameters.begin(), first_space);
2610 
2611  const std::string duration(first_space + 1, second_space);
2612  time_t parsed_time = time(NULL);
2613  if (ban_manager_.parse_time(duration, &parsed_time) == false) {
2614  *out << "Failed to parse the ban duration: '" << duration << "'\n"
2616  return;
2617  }
2618 
2619  if (second_space == parameters.end()) {
2620  --second_space;
2621  }
2622  std::string reason(second_space + 1, parameters.end());
2623  utils::strip(reason);
2624  if (reason.empty()) {
2625  *out << "You need to give a reason for the ban.";
2626  return;
2627  }
2628 
2629  std::string dummy_group;
2630 
2631  // if we find a '.' consider it an ip mask
2632  /** @todo FIXME: make a proper check for valid IPs. */
2633  if (std::count(target.begin(), target.end(), '.') >= 1) {
2634  banned = true;
2635 
2636  *out << ban_manager_.ban(target, parsed_time, reason, issuer_name, dummy_group);
2637  } else {
2638  for (const auto& player : player_connections_)
2639  {
2641  if (banned) *out << "\n";
2642  else banned = true;
2643  const std::string ip = client_address(player.socket());
2644  *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, dummy_group, target);
2645  }
2646  }
2647  if (!banned) {
2648  // If nobody was banned yet check the ip_log but only if a
2649  // simple username was used to prevent accidental bans.
2650  // @todo FIXME: since we can have several entries now we should only ban the latest or so
2651  /*if (utils::isvalid_username(target)) {
2652  for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
2653  i != ip_log_.end(); ++i) {
2654  if (i->nick == target) {
2655  if (banned) out << "\n";
2656  else banned = true;
2657  out << ban_manager_.ban(i->ip, parsed_time, reason, issuer_name, group, target);
2658  }
2659  }
2660  }*/
2661  if(!banned) {
2662  *out << "Nickname mask '" << target << "' did not match, no bans set.";
2663  }
2664  }
2665  }
2666 }
2667 
2668 void server::kickban_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2669  assert(out != NULL);
2670 
2671  bool banned = false;
2672  std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
2673  if (first_space == parameters.end()) {
2674  *out << ban_manager_.get_ban_help();
2675  return;
2676  }
2677  std::string::iterator second_space = std::find(first_space + 1, parameters.end(), ' ');
2678  const std::string target(parameters.begin(), first_space);
2679  const std::string duration(first_space + 1, second_space);
2680  time_t parsed_time = time(NULL);
2681  if (ban_manager_.parse_time(duration, &parsed_time) == false) {
2682  *out << "Failed to parse the ban duration: '" << duration << "'\n"
2684  return;
2685  }
2686 
2687  if (second_space == parameters.end()) {
2688  --second_space;
2689  }
2690  std::string reason(second_space + 1, parameters.end());
2691  utils::strip(reason);
2692  if (reason.empty()) {
2693  *out << "You need to give a reason for the ban.";
2694  return;
2695  }
2696 
2697  std::string dummy_group;
2698  std::vector<socket_ptr> users_to_kick;
2699 
2700  // if we find a '.' consider it an ip mask
2701  /** @todo FIXME: make a proper check for valid IPs. */
2702  if (std::count(target.begin(), target.end(), '.') >= 1) {
2703  banned = true;
2704 
2705  *out << ban_manager_.ban(target, parsed_time, reason, issuer_name, dummy_group);
2706 
2707  for (const auto& player : player_connections_)
2708  {
2710  users_to_kick.push_back(player.socket());
2711  }
2712  }
2713  } else {
2714  for (const auto& player : player_connections_)
2715  {
2717  if (banned) *out << "\n";
2718  else banned = true;
2719  const std::string ip = client_address(player.socket());
2720  users_to_kick.push_back(player.socket());
2721  }
2722  }
2723  if (!banned) {
2724  // If nobody was banned yet check the ip_log but only if a
2725  // simple username was used to prevent accidental bans.
2726  // @todo FIXME: since we can have several entries now we should only ban the latest or so
2727  /*if (utils::isvalid_username(target)) {
2728  for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
2729  i != ip_log_.end(); ++i) {
2730  if (i->nick == target) {
2731  if (banned) out << "\n";
2732  else banned = true;
2733  out << ban_manager_.ban(i->ip, parsed_time, reason, issuer_name, group, target);
2734  }
2735  }
2736  }*/
2737  if(!banned) {
2738  *out << "Nickname mask '" << target << "' did not match, no bans set.";
2739  }
2740  }
2741  }
2742 
2743  for(const auto& user : users_to_kick) {
2744  *out << "\nKicked " << player_connections_.find(user)->info().name() << " ("
2745  << client_address(user) << ").";
2746  async_send_error(user, "You have been banned. Reason: " + reason);
2747  remove_player(user);
2748  }
2749  }
2750 
2751 void server::gban_handler(const std::string& issuer_name, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2752  assert(out != NULL);
2753 
2754  bool banned = false;
2755  std::string::iterator first_space = std::find(parameters.begin(), parameters.end(), ' ');
2756  if (first_space == parameters.end()) {
2757  *out << ban_manager_.get_ban_help();
2758  return;
2759  }
2760  std::string::iterator second_space = std::find(first_space + 1, parameters.end(), ' ');
2761  const std::string target(parameters.begin(), first_space);
2762 
2763  std::string group = std::string(first_space + 1, second_space);
2764  first_space = second_space;
2765  second_space = std::find(first_space + 1, parameters.end(), ' ');
2766 
2767  const std::string duration(first_space + 1, second_space);
2768  time_t parsed_time = time(NULL);
2769  if (ban_manager_.parse_time(duration, &parsed_time) == false) {
2770  *out << "Failed to parse the ban duration: '" << duration << "'\n"
2772  return;
2773  }
2774 
2775  if (second_space == parameters.end()) {
2776  --second_space;
2777  }
2778  std::string reason(second_space + 1, parameters.end());
2779  utils::strip(reason);
2780  if (reason.empty()) {
2781  *out << "You need to give a reason for the ban.";
2782  return;
2783  }
2784 
2785  // if we find a '.' consider it an ip mask
2786  /** @todo FIXME: make a proper check for valid IPs. */
2787  if (std::count(target.begin(), target.end(), '.') >= 1) {
2788  banned = true;
2789 
2790  *out << ban_manager_.ban(target, parsed_time, reason, issuer_name, group);
2791  } else {
2792  for (const auto& player : player_connections_)
2793  {
2795  if (banned) *out << "\n";
2796  else banned = true;
2797  const std::string ip = client_address(player.socket());
2798  *out << ban_manager_.ban(ip, parsed_time, reason, issuer_name, group, target);
2799  }
2800  }
2801  if (!banned) {
2802  // If nobody was banned yet check the ip_log but only if a
2803  // simple username was used to prevent accidental bans.
2804  // @todo FIXME: since we can have several entries now we should only ban the latest or so
2805  /*if (utils::isvalid_username(target)) {
2806  for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
2807  i != ip_log_.end(); ++i) {
2808  if (i->nick == target) {
2809  if (banned) out << "\n";
2810  else banned = true;
2811  out << ban_manager_.ban(i->ip, parsed_time, reason, issuer_name, group, target);
2812  }
2813  }
2814  }*/
2815  if(!banned) {
2816  *out << "Nickname mask '" << target << "' did not match, no bans set.";
2817  }
2818  }
2819  }
2820  }
2821 
2822 void server::unban_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2823  assert(out != NULL);
2824 
2825  if (parameters == "") {
2826  *out << "You must enter an ipmask to unban.";
2827  return;
2828  }
2829  ban_manager_.unban(*out, parameters);
2830 }
2831 
2832 void server::ungban_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2833  assert(out != NULL);
2834 
2835  if (parameters == "") {
2836  *out << "You must enter an ipmask to ungban.";
2837  return;
2838  }
2839  ban_manager_.unban_group(*out, parameters);
2840 }
2841 
2842 void server::kick_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2843  assert(out != NULL);
2844 
2845  if (parameters == "") {
2846  *out << "You must enter a mask to kick.";
2847  return;
2848  }
2849  std::string::iterator i = std::find(parameters.begin(), parameters.end(), ' ');
2850  const std::string kick_mask = std::string(parameters.begin(), i);
2851  const std::string kick_message =
2852  (i == parameters.end() ? "You have been kicked."
2853  : "You have been kicked. Reason: " + std::string(i + 1, parameters.end()));
2854  bool kicked = false;
2855  // if we find a '.' consider it an ip mask
2856  const bool match_ip = (std::count(kick_mask.begin(), kick_mask.end(), '.') >= 1);
2857  std::vector<socket_ptr> users_to_kick;
2858  for (const auto& player : player_connections_)
2859  {
2860  if ((match_ip && utils::wildcard_string_match(client_address(player.socket()), kick_mask))
2861  || (!match_ip && utils::wildcard_string_match(player.info().name(), kick_mask))) {
2862  users_to_kick.push_back(player.socket());
2863  }
2864  }
2865  for(const auto& socket : users_to_kick) {
2866  if (kicked) *out << "\n";
2867  else kicked = true;
2868  *out << "Kicked " << player_connections_.find(socket)->name() << " ("
2869  << client_address(socket) << "). '"
2870  << kick_message << "'";
2871  async_send_error(socket, kick_message);
2872  remove_player(socket);
2873  }
2874  if (!kicked) *out << "No user matched '" << kick_mask << "'.";
2875 }
2876 
2877 void server::motd_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2878  assert(out != NULL);
2879 
2880  if (parameters == "") {
2881  if (motd_ != "") {
2882  *out << "Message of the day:\n" << motd_;
2883  return;
2884  } else {
2885  *out << "No message of the day set.";
2886  return;
2887  }
2888  }
2889 
2890  motd_ = parameters;
2891  *out << "Message of the day set to: " << motd_;
2892 }
2893 
2894 void server::searchlog_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2895  assert(out != NULL);
2896 
2897  if (parameters.empty()) {
2898  *out << "You must enter a mask to search for.";
2899  return;
2900  }
2901  *out << "IP/NICK LOG for '" << parameters << "'";
2902 
2903  bool found_something = false;
2904 
2905  // If this looks like an IP look up which nicks have been connected from it
2906  // Otherwise look for the last IP the nick used to connect
2907  const bool match_ip = (std::count(parameters.begin(), parameters.end(), '.') >= 1);
2908  for (std::deque<connection_log>::const_iterator i = ip_log_.begin();
2909  i != ip_log_.end(); ++i) {
2910  const std::string& username = i->nick;
2911  const std::string& ip = i->ip;
2912  if ((match_ip && utils::wildcard_string_match(ip, parameters))
2913  || (!match_ip && utils::wildcard_string_match(username, parameters))) {
2914  found_something = true;
2915  auto player = player_connections_.get<name_t>().find(username);
2916  if (player != player_connections_.get<name_t>().end() && client_address(player->socket()) == ip) {
2917  *out << std::endl << player_status(*player);
2918  } else {
2919  *out << "\n'" << username << "' @ " << ip << " last seen: " << lg::get_timestamp(i->log_off, "%H:%M:%S %d.%m.%Y");
2920  }
2921  }
2922  }
2923  if (!found_something) *out << "\nNo match found.";
2924 }
2925 
2926 void server::dul_handler(const std::string& /*issuer_name*/, const std::string& /*query*/, std::string& parameters, std::ostringstream *out) {
2927  assert(out != NULL);
2928 
2929  try {
2930 
2931  if (parameters == "") {
2932  *out << "Unregistered login is " << (deny_unregistered_login_ ? "disallowed" : "allowed") << ".";
2933  } else {
2934  deny_unregistered_login_ = (utf8::lowercase(parameters) == "yes");
2935  *out << "Unregistered login is now " << (deny_unregistered_login_ ? "disallowed" : "allowed") << ".";
2936  }
2937 
2938  } catch ( utf8::invalid_utf8_exception & e ) {
2939  ERR_SERVER << "While handling dul (deny unregistered logins), caught an invalid utf8 exception: " << e.what() << std::endl;
2940  }
2941 }
2942 
2943 void server::delete_game(int gameid) {
2944  const boost::shared_ptr<game>& game_ptr = player_connections_.get<game_t>().find(gameid)->get_game();
2945 
2946  // Set the availability status for all quitting users.
2947  for(const auto& user : player_connections_.get<game_t>().equal_range(gameid)) {
2948  user.info().mark_available();
2949  simple_wml::document udiff;
2951  "user", user.info().config_address(), udiff)) {
2952  send_to_lobby(udiff);
2953  } else {
2954  ERR_SERVER << "ERROR: delete_game(): Could not find user in players_. (socket: "
2955  << user.socket() << ")\n";
2956  }
2957  }
2958 
2959  //send users in the game a notification to leave the game since it has ended
2960  static simple_wml::document leave_game_doc("[leave_game]\n[/leave_game]\n", simple_wml::INIT_COMPRESSED);
2961  game_ptr->send_data(leave_game_doc);
2962  // Put the remaining users back in the lobby.
2964  boost::tie(iter, i_end) = player_connections_.get<game_t>().equal_range(gameid);
2965  for(;iter != i_end; iter++)
2967 
2968  game_ptr->send_data(games_and_users_list_);
2969 }
2970 
2972 {
2973  simple_wml::document diff;
2974  if (make_change_diff(*games_and_users_list_.child("gamelist"), "gamelist", "game", g.description(), diff)) {
2975  send_to_lobby(diff, exclude);
2976  }
2977 }
2978 
2979 } // namespace wesnothd
2980 
2981 int main(int argc, char** argv) {
2982  int port = 15000;
2983  bool keep_alive = false;
2984  size_t min_threads = 5;
2985  size_t max_threads = 0;
2986 
2987  srand(static_cast<unsigned>(time(nullptr)));
2988 
2989  std::string config_file;
2990 
2991  // setting path to currentworking directory
2993 
2994  // show 'info' by default
2996  lg::timestamps(true);
2997 
2998  for (int arg = 1; arg != argc; ++arg) {
2999  const std::string val(argv[arg]);
3000  if (val.empty()) {
3001  continue;
3002  }
3003 
3004  if ((val == "--config" || val == "-c") && arg+1 != argc) {
3005  config_file = argv[++arg];
3006  } else if (val == "--verbose" || val == "-v") {
3008  } else if (val.substr(0, 6) == "--log-") {
3009  size_t p = val.find('=');
3010  if (p == std::string::npos) {
3011  std::cerr << "unknown option: " << val << '\n';
3012  return 2;
3013  }
3014  std::string s = val.substr(6, p - 6);
3015  int severity;
3016  if (s == "error") severity = lg::err().get_severity();
3017  else if (s == "warning") severity = lg::warn().get_severity();
3018  else if (s == "info") severity = lg::info().get_severity();
3019  else if (s == "debug") severity = lg::debug().get_severity();
3020  else {
3021  std::cerr << "unknown debug level: " << s << '\n';
3022  return 2;
3023  }
3024  while (p != std::string::npos) {
3025  size_t q = val.find(',', p + 1);
3026  s = val.substr(p + 1, q == std::string::npos ? q : q - (p + 1));
3027  if (!lg::set_log_domain_severity(s, severity)) {
3028  std::cerr << "unknown debug domain: " << s << '\n';
3029  return 2;
3030  }
3031  p = q;
3032  }
3033  } else if ((val == "--port" || val == "-p") && arg+1 != argc) {
3034  port = atoi(argv[++arg]);
3035  } else if (val == "--keepalive") {
3036  keep_alive = true;
3037  } else if (val == "--help" || val == "-h") {
3038  std::cout << "usage: " << argv[0]
3039  << " [-dvV] [-c path] [-m n] [-p port] [-t n]\n"
3040  << " -c, --config <path> Tells wesnothd where to find the config file to use.\n"
3041  << " -d, --daemon Runs wesnothd as a daemon.\n"
3042  << " -h, --help Shows this usage message.\n"
3043  << " --log-<level>=<domain1>,<domain2>,...\n"
3044  << " sets the severity level of the debug domains.\n"
3045  << " 'all' can be used to match any debug domain.\n"
3046  << " Available levels: error, warning, info, debug.\n"
3047  << " -p, --port <port> Binds the server to the specified port.\n"
3048  << " --keepalive Enable TCP keepalive.\n"
3049  << " -t, --threads <n> Uses n worker threads for network I/O (default: 5).\n"
3050  << " -v --verbose Turns on more verbose logging.\n"
3051  << " -V, --version Returns the server version.\n";
3052  return 0;
3053  } else if (val == "--version" || val == "-V") {
3054  std::cout << "Battle for Wesnoth server " << game_config::version
3055  << "\n";
3056  return 0;
3057  } else if (val == "--daemon" || val == "-d") {
3058 #ifdef _WIN32
3059  ERR_SERVER << "Running as a daemon is not supported on this platform" << std::endl;
3060  return -1;
3061 #else
3062  const pid_t pid = fork();
3063  if (pid < 0) {
3064  ERR_SERVER << "Could not fork and run as a daemon" << std::endl;
3065  return -1;
3066  } else if (pid > 0) {
3067  std::cout << "Started wesnothd as a daemon with process id "
3068  << pid << "\n";
3069  return 0;
3070  }
3071 
3072  setsid();
3073 #endif
3074  } else if ((val == "--threads" || val == "-t") && arg+1 != argc) {
3075  min_threads = atoi(argv[++arg]);
3076  if (min_threads > 30) {
3077  min_threads = 30;
3078  }
3079  } else if ((val == "--max-threads" || val == "-T") && arg+1 != argc) {
3080  max_threads = atoi(argv[++arg]);
3081  } else if(val == "--request_sample_frequency" && arg+1 != argc) {
3082  wesnothd::request_sample_frequency = atoi(argv[++arg]);
3083  } else {
3084  ERR_SERVER << "unknown option: " << val << std::endl;
3085  return 2;
3086  }
3087  }
3088 
3089  wesnothd::server(port, keep_alive, config_file, min_threads, max_threads).run();
3090 
3091  return 0;
3092 }
bool process_turn(simple_wml::document &data, const socket_ptr user)
Handles [end_turn], repackages [commands] with private [speak]s in them and sends the data...
Definition: game.cpp:886
std::string motd_
Definition: server.hpp:149
boost::shared_ptr< DataSize > data_size
Definition: server.cpp:141
node & add_child(const char *name)
Definition: simple_wml.cpp:465
config read_config() const
Read the server config from file 'config_file_'.
Definition: server.cpp:576
static void enter_lobby(player_record &)
child_itors child_range(const std::string &key)
Definition: config.cpp:613
std::deque< boost::shared_ptr< game > > games()
Definition: server.hpp:121
void start_game(const socket_ptr starter)
Definition: game.cpp:271
static thandler * handler
Definition: handler.cpp:60
void async_send_error(socket_ptr socket, const std::string &msg, const char *error_code="")
Definition: server.cpp:554
std::ostream & games(std::ostream &out) const
Definition: metrics.cpp:111
void update_game()
Definition: game.cpp:338
void handle_nickserv(socket_ptr socket, simple_wml::node &nickserv)
Definition: server.cpp:1250
SendQueue send_queue
Definition: server.cpp:1843
node & set_attr(const char *key, const char *value)
Definition: simple_wml.hpp:269
void handle_whisper(socket_ptr socket, simple_wml::node &whisper)
Definition: server.cpp:1150
bool isvalid_username(const std::string &username)
Check if the username contains only valid characters.
void shut_down_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2302
void serverside_handshake(socket_ptr socket)
Definition: server.cpp:758
simple_wml::document games_and_users_list_
Definition: server.hpp:177
std::deque< login_log >::size_type failed_login_buffer_size_
Definition: server.hpp:166
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
Definition: cursor.cpp:154
void handle_read_from_player(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
Definition: server.cpp:1111
void apply_diff(const node &diff)
Definition: simple_wml.cpp:827
void unmute_observer(const simple_wml::node &unmute, const socket_ptr unmuter)
Definition: game.cpp:696
const socket_ptr socket() const
#define FIFODIR
void list_deleted_bans(std::ostringstream &out, const std::string &mask="*") const
Definition: ban.cpp:584
void load_config(const config &)
Definition: ban.cpp:696
std::string get_timestamp(const time_t &t, const std::string &format)
Definition: log.cpp:174
void save_replay()
Definition: game.cpp:1506
const std::string denied_msg
Definition: server.cpp:352
void handle_version(socket_ptr socket)
Definition: server.cpp:792
void status_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2518
boost::asio::posix::stream_descriptor input_
server socket/fifo.
Definition: server.hpp:133
handle_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler, boost::uint32_t size, boost::shared_ptr< simple_wml::document > doc)
Definition: server.cpp:144
void games_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2383
std::string ban(const std::string &, const time_t &, const std::string &, const std::string &, const std::string &, const std::string &="")
Definition: ban.cpp:490
void send_server_message(const char *message, socket_ptr sock=socket_ptr(), simple_wml::document *doc=nullptr) const
Definition: game.cpp:1667
handle_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
Definition: server.cpp:149
int failed_login_limit_
Definition: server.hpp:164
static std::string player_status(const wesnothd::player_record &player)
Definition: server.cpp:338
void handle_login(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
Definition: server.cpp:858
GLuint GLuint GLsizei GLenum type
Definition: glew.h:1221
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
#define MP_TOO_MANY_ATTEMPTS_ERROR
logger & info()
Definition: log.cpp:91
boost::uint32_t uint32_t
Definition: xbrz.hpp:45
void update_side_data()
Resets the side configuration according to the scenario data.
Definition: game.cpp:400
void accept_connection(const boost::system::error_code &error, socket_ptr socket)
Definition: server.cpp:730
bool save_replays_
Definition: server.hpp:160
int main(int argc, char **argv)
Definition: server.cpp:2981
void kickban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2668
void handle_query(socket_ptr socket, simple_wml::node &query)
Definition: server.cpp:1173
void timestamps(bool t)
Definition: log.cpp:76
node & set_attr_int(const char *key, int value)
Definition: simple_wml.cpp:439
#define MP_NO_SEED_ERROR
void help_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2361
#define WRN_SERVER
clients send wrong/unexpected data
Definition: server.cpp:78
node & set_attr(const char *key, const char *value)
Definition: simple_wml.cpp:411
bool wildcard_string_match(const std::string &str, const std::string &match)
Match using '*' as any number of characters (including none), and '?' as any one character.
void login(socket_ptr socket)
Definition: server.cpp:851
void wml_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2388
utf8::string lowercase(const utf8::string &s)
Returns a lowercased version of the string.
Definition: unicode.cpp:53
server(int port, bool keep_alive, const std::string &config_file, size_t min_threads, size_t max_threads)
Definition: server.cpp:363
void transfer_side_control(const socket_ptr sock, const simple_wml::node &cfg)
Let's a player owning a side give it to another player or observer.
Definition: game.cpp:464
GLuint const GLfloat * val
Definition: glew.h:2614
#define ERR_CONFIG
Definition: server.cpp:85
boost::asio::signal_set sighup_
Definition: server.hpp:236
void process_message(simple_wml::document &data, const socket_ptr user)
Definition: game.cpp:830
void stats_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2366
size_t default_max_messages_
Definition: server.hpp:150
size_t nplayers() const
Definition: game.hpp:89
#define MP_NAME_TOO_LONG_ERROR
void handle_read_from_fifo(const boost::system::error_code &error, std::size_t bytes_transferred)
Definition: server.cpp:494
void update_game_in_lobby(const wesnothd::game &g, const socket_ptr &exclude=socket_ptr())
Definition: server.cpp:2971
static bool make_delete_diff(const simple_wml::node &src, const char *gamelist, const char *type, const simple_wml::node *remove, simple_wml::document &out)
Definition: server.cpp:271
GLboolean GLboolean g
Definition: glew.h:7319
GLenum src
Definition: glew.h:2392
void requests_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2378
bool started() const
Definition: game.hpp:87
Define the errors the server may send during the login procedure.
std::vector< std::string > accepted_versions_
Definition: server.hpp:144
int get_severity() const
Definition: log.hpp:127
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
bool empty() const
Definition: config.cpp:1105
To lexical_cast(From value)
Lexical cast converts one type to another.
void delete_game(int)
Definition: server.cpp:2943
void ban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2597
Definitions for the interface to Wesnoth Markup Language (WML).
void handle_sighup(const boost::system::error_code &error, int signal_number)
Definition: server.cpp:436
std::string uh_name_
Definition: server.hpp:158
boost::scoped_ptr< user_handler > user_handler_
Definition: server.hpp:117
void handle_create_game(socket_ptr socket, simple_wml::node &create_game)
Definition: server.cpp:1370
An example of how to implement user_handler.
void handle_termination(const boost::system::error_code &error, int signal_number)
Definition: server.cpp:448
std::string is_ip_banned(const std::string &ip) const
Definition: ban.cpp:655
bool deny_unregistered_login_
Definition: server.hpp:159
void handle_graceful_timeout(const boost::system::error_code &error)
Definition: server.cpp:460
void async_receive_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
Definition: server.cpp:232
std::string & strip(std::string &str)
Remove whitespace from the front and back of the string 'str'.
#define MP_INCORRECT_PASSWORD_ERROR
void gban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2751
std::string get_cwd()
static bool check_error(const boost::system::error_code &error, socket_ptr socket)
Definition: server.cpp:121
bool is_player(const socket_ptr player) const
Definition: game.cpp:168
boost::asio::signal_set sigs_
Definition: server.hpp:239
GLuint GLuint stream
Definition: glew.h:5239
GLdouble GLdouble GLdouble GLdouble q
Definition: glew.h:1382
node * child(const char *name)
Definition: simple_wml.hpp:257
std::ostream & requests(std::ostream &out) const
Definition: metrics.cpp:126
const char * output()
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
Definition: image.cpp:1187
node * child(const char *name)
Definition: simple_wml.cpp:607
socket_ptr kick_member(const simple_wml::node &kick, const socket_ptr kicker)
Kick a member by name.
Definition: game.cpp:736
bool set_log_domain_severity(std::string const &name, int severity)
Definition: log.cpp:117
void handle_choice(const simple_wml::node &data, const socket_ptr user)
Definition: game.cpp:1059
#define MP_MUST_LOGIN
simple_wml::document login_response_
Definition: server.hpp:175
#define MP_NAME_UNREGISTERED_ERROR
bool parse_time(const std::string &duration, time_t *time) const
Parses the given duration and adds it to *time except if the duration is '0' or 'permanent' in which ...
Definition: ban.cpp:326
A user_handler implementation to link the server with a phpbb3 forum.
GLuint GLuint end
Definition: glew.h:1221
GLuint64EXT * result
Definition: glew.h:10727
bool allow_remote_shutdown_
Definition: server.hpp:162
std::map< std::string, config > proxy_versions_
Definition: server.hpp:146
const std::string & name() const
Definition: player.hpp:51
void unban_group(std::ostringstream &os, const std::string &group)
Definition: ban.cpp:549
void msg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2485
void send_data(simple_wml::document &data, const socket_ptr exclude=socket_ptr(), std::string packet_type="") const
Definition: game.cpp:1384
void add_player(socket_ptr socket, const wesnothd::player &)
Definition: server.cpp:1083
void remove_player(socket_ptr socket)
Definition: server.cpp:1882
void handle_handshake(const boost::system::error_code &error, socket_ptr socket, boost::shared_array< char > buf)
Definition: server.cpp:767
simple_wml::node * description() const
Definition: game.hpp:223
std::string restart_command
Definition: server.hpp:156
void remove_child(const char *name, size_t index)
Definition: simple_wml.cpp:602
const std::string & termination_reason() const
Definition: game.hpp:230
bool describe_slots()
Set the description to the number of available slots.
Definition: game.cpp:604
socket_ptr ban_user(const simple_wml::node &ban, const socket_ptr banner)
Ban and kick a user by name.
Definition: game.cpp:767
void clones_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2551
void handle_player_in_lobby(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
Definition: server.cpp:1136
void read_version(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
Definition: server.cpp:799
static void set_game(player_record &, boost::shared_ptr< game >)
std::string input_path_
Definition: server.hpp:135
void handle_player_in_game(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
Definition: server.cpp:1517
An interface class to handle nick registration To activate it put a [user_handler] section into the s...
bool has_attr(const char *key) const
Definition: simple_wml.cpp:403
boost::asio::deadline_timer timer_
Definition: server.hpp:242
void motd_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2877
GLenum GLuint GLsizei const char * buf
Definition: glew.h:2498
std::vector< std::string > tor_ip_list_
Definition: server.hpp:163
std::string path
const char * end() const
Definition: simple_wml.hpp:91
void bans_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2575
void searchlog_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2894
GLenum GLint GLuint mask
Definition: glew.h:1813
void request_version(const boost::system::error_code &error, socket_ptr socket)
Definition: server.cpp:782
union wesnothd::server::@25 handshake_response_
void send_password_request(socket_ptr socket, const std::string &msg, const std::string &user, const char *error_code="", bool force_confirmation=false)
Definition: server.cpp:1051
std::string is_ip_banned(const std::string &ip) const
Definition: server.cpp:702
logger & debug()
Definition: log.cpp:97
boost::shared_array< char > buffer
Definition: server.cpp:143
simple_wml::document version_query_response_
Definition: server.hpp:174
GLfloat GLfloat p
Definition: glew.h:12766
Templates and utility-routines for strings and numbers.
void perform_controller_tweaks()
Definition: game.cpp:212
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::string replay_save_path_
Definition: server.hpp:161
void kick_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2842
void clean_user_handler(const time_t &now)
Definition: server.cpp:716
GLuint GLuint GLsizei count
Definition: glew.h:1221
static void make_add_diff(const simple_wml::node &src, const char *gamelist, const char *type, simple_wml::document &out, int index=-1)
Definition: server.cpp:244
GLenum severity
Definition: glew.h:2497
size_t concurrent_connections_
Definition: server.hpp:152
void operator()(const boost::system::error_code &error, std::size_t)
Definition: server.cpp:153
void send_to_player(socket_ptr socket, simple_wml::document &doc)
Definition: server.cpp:1846
void netstats_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2393
size_t default_time_period_
Definition: server.hpp:151
void handle_send_to_player(socket_ptr socket)
Definition: server.cpp:1860
bool player_is_in_game(socket_ptr socket) const
Definition: server.hpp:80
ErrorHandler error_handler
Definition: server.cpp:134
std::string join(T const &v, const std::string &s=",")
Generates a new string joining container items in a list.
void send_server_message_to_all(const std::string &message, socket_ptr exclude=socket_ptr()) const
Definition: server.cpp:1936
void swap(document &o)
std::string client_address(socket_ptr socket)
Definition: server.cpp:108
GLuint buffer
Definition: glew.h:1648
void async_send_doc(socket_ptr socket, simple_wml::document &doc, Handler handler, ErrorHandler error_handler)
Definition: server.cpp:164
void operator()(const boost::system::error_code &error, std::size_t size)
Definition: server.cpp:203
boost::asio::ip::tcp::acceptor acceptor_
Definition: server.hpp:42
const std::string help_msg
Definition: server.cpp:353
const boost::shared_ptr< game > get_game() const
void cleanup_game(game *)
Definition: server.cpp:1414
std::string server_message
static void async_send_warning(socket_ptr socket, const std::string &msg, const char *warning_code)
Definition: server.cpp:565
GLuint res
Definition: glew.h:9258
static simple_wml::node * starting_pos(simple_wml::node &data)
Definition: game.hpp:65
void set_moderator(bool moderator)
Definition: player.hpp:71
static std::string stats()
time_t last_uh_clean_
Definition: server.hpp:185
bool graceful_restart
Definition: server.hpp:153
void game_terminated(const std::string &reason)
Definition: metrics.cpp:106
void sample_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2343
bool is_owner(const socket_ptr player) const
Definition: game.hpp:54
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
logger & err()
Definition: log.cpp:79
void dump_stats(const time_t &now)
Definition: server.cpp:709
Thrown by operations encountering invalid UTF-8 data.
node & set_attr_dup(const char *key, const char *value)
Definition: simple_wml.cpp:427
void start_new_server()
Definition: server.cpp:2237
void unban(std::ostringstream &os, const std::string &ip)
Definition: ban.cpp:524
void read_from_player(socket_ptr socket)
Definition: server.cpp:1103
void process_whiteboard(simple_wml::document &data, const socket_ptr user)
Handles incoming [whiteboard] data.
Definition: game.cpp:1093
void unban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2822
const std::string config_file_
Definition: server.hpp:137
const GLuint * buffers
Definition: glew.h:1651
GLuint index
Definition: glew.h:1782
boost::shared_ptr< simple_wml::document > doc
Definition: server.cpp:142
std::map< socket_ptr, std::deque< boost::shared_ptr< simple_wml::document > > > SendQueue
Definition: server.cpp:1842
const std::string & parameters
Definition: filter.cpp:155
std::vector< node * > child_list
Definition: simple_wml.hpp:121
size_t i
Definition: function.cpp:1057
std::function< void(server *, const std::string &, const std::string &, std::string &, std::ostringstream *)> cmd_handler
Definition: server.hpp:205
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
void copy_into(node &n) const
Definition: simple_wml.cpp:806
socket_ptr socket
Definition: server.cpp:135
std::string admin_passwd_
Definition: server.hpp:148
const child_list & children(const char *name) const
Definition: simple_wml.cpp:634
void reset_last_synced_context_id()
Definition: game.hpp:244
wesnothd::ban_manager ban_manager_
Definition: server.hpp:84
std::deque< login_log > failed_logins_
Definition: server.hpp:115
void adminmsg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2416
#define ERR_SERVER
fatal and directly server related errors/warnings, ie not caused by erroneous client data ...
Definition: server.cpp:75
metrics metrics_
Definition: server.hpp:179
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
Definition: parser.cpp:400
bool to_bool(bool default_value=false) const
Definition: simple_wml.cpp:157
bool is_moderator() const
Definition: player.hpp:72
void list_bans(std::ostringstream &out, const std::string &mask="*") const
Definition: ban.cpp:612
static void null_handler(socket_ptr)
Definition: server.cpp:180
#define MP_PASSWORD_REQUEST
void load_next_scenario(const socket_ptr user)
A user (player only?) asks for the next scenario to advance to.
Definition: game.cpp:1338
void set_description(simple_wml::node *desc)
Functions to set/get the address of the game's summary description as sent to players in the lobby...
Definition: game.cpp:1568
#define MP_NAME_INACTIVE_WARNING
std::map< std::string, config > redirected_versions_
Definition: server.hpp:145
void set_password(const std::string &passwd)
Definition: game.hpp:225
std::string observer
Definition: game_config.cpp:84
void send_server_message_to_all(const char *message, socket_ptr exclude=socket_ptr()) const
Definition: game.cpp:1660
const char * begin() const
Definition: simple_wml.hpp:90
void handle_message(socket_ptr socket, simple_wml::node &message)
Definition: server.cpp:1363
int id() const
Definition: game.hpp:51
int request_sample_frequency
Definition: server.cpp:119
void restart_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2322
#define MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME
handle_receive_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
Definition: server.cpp:199
#define next(ls)
Definition: llex.cpp:27
GLsizeiptr size
Definition: glew.h:1649
#define MP_NAME_TAKEN_ERROR
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
const std::string & get_ban_help() const
Definition: ban.hpp:167
boost::asio::io_service io_service_
Definition: server.hpp:41
GLclampd n
Definition: glew.h:5903
time_t failed_login_ban_
Definition: server.hpp:165
GLenum GLint ref
Definition: glew.h:1813
GLboolean GLuint group
Definition: glew.h:2589
logger & warn()
Definition: log.cpp:85
std::map< long int, std::string > seeds_
Definition: server.hpp:118
const GLdouble * m
Definition: glew.h:6968
-file actions.hpp
const std::string & name() const
#define WRN_CONFIG
Definition: server.cpp:86
bool find(E event, F functor)
Tests whether an event handler is available.
void handle_join_game(socket_ptr socket, simple_wml::node &join)
Definition: server.cpp:1442
Base class for all the errors encountered by the engine.
Definition: exceptions.hpp:27
void send_to_lobby(simple_wml::document &data, socket_ptr exclude=socket_ptr()) const
Definition: server.cpp:1921
void setup_handlers()
Definition: server.cpp:519
size_t max_ip_log_size_
Definition: server.hpp:157
Definition: ban.cpp:28
void read_from_fifo()
Definition: server.cpp:488
std::map< std::string, cmd_handler > cmd_handlers_
Definition: server.hpp:206
#define LOG_SERVER
normal events
Definition: server.cpp:81
void setup_fifo()
Definition: server.cpp:473
Standard logging facilities (interface).
bool level_init() const
Definition: game.hpp:64
void dul_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2926
static lg::log_domain log_config("config")
std::string message
Definition: exceptions.hpp:29
player_connections player_connections_
Definition: server.hpp:120
GLsizei GLenum GLuint GLuint GLsizei char * message
Definition: glew.h:2499
time_t lan_server_
Definition: server.hpp:154
void load_config()
Parse the server config into local variables.
Definition: server.cpp:591
simple_wml::document join_lobby_response_
Definition: server.hpp:176
utf8::string & insert(utf8::string &str, const size_t pos, const utf8::string &insert)
Insert a UTF-8 string at the specified position.
Definition: unicode.cpp:101
std::vector< std::string > split(std::string const &val, const char c, const int flags)
Splits a (comma-)separated string into a vector of pieces.
std::deque< connection_log > ip_log_
Definition: server.hpp:99
#define e
time_t last_stats_
Definition: server.hpp:182
const node::child_list & children(const char *name) const
Definition: simple_wml.hpp:265
void process_change_turns_wml(simple_wml::document &data, const socket_ptr user)
Handles incoming [change_turns_wml] data.
Definition: game.cpp:1118
#define MP_NAME_RESERVED_ERROR
void ungban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2832
boost::shared_ptr< boost::asio::ip::tcp::socket > socket_ptr
static lg::log_domain log_server("server")
std::string process_command(std::string cmd, std::string issuer_name)
Process commands from admins and users.
Definition: server.cpp:2251
const string_span & attr(const char *key) const
Definition: simple_wml.hpp:253
void mute_observer(const simple_wml::node &mute, const socket_ptr muter)
Mute an observer or give a message of all currently muted observers if no name is given...
Definition: game.cpp:656
void metrics_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2373
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
static bool read_config(config &src, config &dst)
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
void lobbymsg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2502
void pm_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definition: server.cpp:2450
void create_game(player_record &host, simple_wml::node &create_game)
Definition: server.cpp:1393
void unban_user(const simple_wml::node &unban, const socket_ptr unbanner)
Definition: game.cpp:805
const simple_wml::node * config_address() const
Definition: player.hpp:54
GLdouble s
Definition: glew.h:1358
socket_ptr const std::string & name
Definition: game.hpp:47
void send_server_message_to_lobby(const std::string &message, socket_ptr exclude=socket_ptr()) const
Definition: server.cpp:1928
const std::string version
Definition: game_config.cpp:48
GLenum query
Definition: glew.h:4185
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void mute_all_observers()
Definition: game.cpp:636
bool remove_player(const socket_ptr player, const bool disconnect=false, const bool destruct=false)
Removes a user from the game.
Definition: game.cpp:1234
void send_server_message(socket_ptr socket, const std::string &message)
Definition: server.cpp:1873
void set_termination_reason(const std::string &reason)
Definition: game.cpp:1575
boost::asio::streambuf admin_cmd_
Definition: server.hpp:201
#define SIGHUP
Definition: server.cpp:90
std::vector< std::string > disallowed_names_
Definition: server.hpp:147
#define DBG_SERVER
Definition: server.cpp:82
GLenum target
Definition: glew.h:5190
const string_span & attr(const char *key) const
Definition: simple_wml.hpp:124
#define MP_INVALID_CHARS_IN_NAME_ERROR
simple_wml::document & level()
The full scenario data.
Definition: game.hpp:216
static bool make_change_diff(const simple_wml::node &src, const char *gamelist, const char *type, const simple_wml::node *item, simple_wml::document &out)
Definition: server.cpp:301