52 #include <boost/scoped_ptr.hpp>
53 #include <boost/scoped_array.hpp>
54 #include <boost/make_shared.hpp>
55 #include <boost/utility.hpp>
75 #define ERR_SERVER LOG_STREAM(err, log_server)
78 #define WRN_SERVER LOG_STREAM(warn, log_server)
81 #define LOG_SERVER LOG_STREAM(info, log_server)
82 #define DBG_SERVER LOG_STREAM(debug, log_server)
85 #define ERR_CONFIG LOG_STREAM(err, log_config)
86 #define WRN_CONFIG LOG_STREAM(warn, log_config)
110 boost::system::error_code
error;
113 return "<unknown address>";
130 template<
typename Handler,
typename ErrorHandler>
145 handler(handler), error_handler(error_handler), socket(socket), data_size(new
DataSize), doc(doc)
147 data_size->size = htonl(size);
150 handler(handler), error_handler(error_handler), socket(socket), data_size(new
DataSize)
163 template<
typename Handler,
typename ErrorHandler>
169 std::vector<boost::asio::const_buffer>
buffers;
174 async_write(*socket, buffers, handle_send_doc);
184 template<
typename Handler>
195 template<
typename Handler,
typename ErrorHandler>
200 handle_doc<Handler, ErrorHandler>(socket, handler, error_handler)
221 "\tsimple_wml error in received data: " << e.
message << std::endl;
231 template<
typename Handler,
typename ErrorHandler>
238 template<
typename Handler>
248 if (!out.
child(
"gamelist_diff")) {
261 assert(!children.empty());
263 index = children.size() - 1;
266 assert(
index < static_cast<int>(children.size()));
272 const char* gamelist,
277 if (!out.
child(
"gamelist_diff")) {
289 const simple_wml::node::child_list::const_iterator
itor =
290 std::find(children.begin(), children.end(),
remove);
291 if(itor == children.end()) {
294 const int index = itor - children.begin();
302 const char* gamelist,
307 if (!out.
child(
"gamelist_diff")) {
318 const simple_wml::node::child_list::const_iterator
itor =
319 std::find(children.begin(), children.end(), item);
320 if(itor == children.end()) {
326 const int index = itor - children.begin();
339 std::ostringstream out;
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.";
366 acceptor_(io_service_),
370 user_handler_(nullptr),
375 config_file_(config_file),
377 accepted_versions_(),
378 redirected_versions_(),
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)),
392 deny_unregistered_login_(false),
393 save_replays_(false),
395 allow_remote_shutdown_(false),
397 failed_login_limit_(),
399 failed_login_buffer_size_(),
405 last_ping_(time(nullptr)),
406 last_stats_(last_ping_),
407 last_uh_clean_(last_ping_),
410 sighup_(io_service_,
SIGHUP),
412 sigs_(io_service_, SIGINT, SIGTERM),
415 boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), port);
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));
439 WRN_SERVER <<
"SIGHUP caught, reloading config\n";
453 if(signal_number == SIGINT) signame =
"SIGINT";
454 else if(signal_number == SIGTERM) signame =
"SIGTERM";
456 LOG_SERVER << signame <<
" caught, exiting without cleanup immediately.\n";
457 exit(128 + signal_number);
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");
468 timer_.expires_from_now(boost::posix_time::seconds(1));
476 if(res != 0 && errno != EEXIST) {
480 int fifo = open(
input_path_.c_str(), O_RDWR|O_NONBLOCK);
482 LOG_SERVER <<
"opened fifo at '" <<
input_path_ <<
"'. Server commands may be written to this file.\n";
496 std::cout << error.message() << std::endl;
502 std::getline(is, cmd);
504 LOG_SERVER <<
"Admin Command: type: " << cmd <<
"\n";
507 if (cmd.at(0) ==
'+') {
508 LOG_SERVER <<
"[admin_command_response]\n" << res <<
"\n" <<
"[/admin_command_response]\n";
558 if(*error_code !=
'\0') {
569 if(*warning_code !=
'\0') {
570 doc.
child(
"warning")->
set_attr(
"warning_code", warning_code);
581 read(configuration, *stream);
585 ERR_CONFIG <<
"ERROR: Could not read configuration file: '"
588 return configuration;
594 # pragma message ("No FIFODIR set")
595 # define FIFODIR "d:/"
598 # define FIFODIR "d:/"
600 # warning "No FIFODIR set"
601 # define FIFODIR "/var/run/wesnothd"
623 uh_name_ = cfg_[
"user_handler"].str();
630 if (cfg_[
"disallow_names"] ==
"") {
658 const std::string& versions = cfg_[
"versions_accepted"];
659 if (versions.empty() ==
false) {
688 if(uh_name_ ==
"sample") {
692 else if(uh_name_ ==
"forum" || uh_name_.empty()) {
712 <<
"\tnumber_of_games = " <<
games().size()
735 ERR_SERVER <<
"Accept failed: " << error.message() <<
"\n";
742 if (!reason.empty()) {
743 LOG_SERVER << ip <<
"\trejected banned user. Reason: " << reason <<
"\n";
752 DBG_SERVER << ip <<
"\tnew connection accepted\n";
760 boost::shared_array<char> handshake(
new char[4]);
772 if(strcmp(handshake.get(),
"\0\0\0\0") != 0) {
804 version_str_span.
end());
805 std::vector<std::string>::const_iterator accepted_it;
811 <<
"\tplayer joined using accepted version " << version_str
812 <<
":\ttelling them to log in.\n";
823 <<
"\tplayer joined using version " << version_str
824 <<
":\tredirecting them to " << redirect_version.second[
"host"]
825 <<
":" << redirect_version.second[
"port"] <<
"\n";
827 for(
const auto& attr : redirect_version.second.attribute_range()) {
828 redirect.
set_attr(attr.first.c_str(), attr.second.str().c_str());
836 <<
"\tplayer joined using unknown version " << version_str
837 <<
":\trejecting them\n";
847 <<
"\tclient didn't send its version: rejecting\n";
862 std::string username = (*login)[
"username"].to_string();
864 async_send_error(socket,
"The nickname '" + username +
"' contains invalid "
865 "characters. Only alpha-numeric characters, underscores and hyphens"
870 if (username.size() > 20) {
871 async_send_error(socket,
"The nickname '" + username +
"' is too long. Nicks must be 20 characters or less.",
883 async_send_error(socket,
"The nickname '" + username +
"' is reserved and cannot be used by players",
892 std::string password_reminder = (*login)[
"password_reminder"].to_string();
893 if(password_reminder ==
"yes") {
898 async_send_error(socket,
"There was an error sending your password reminder email. The error message was: " +
921 bool registered =
false;
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);
933 if(password.empty()) {
939 "\n\nWARNING: There is already a client using this username, "
940 "logging in will cause that client to be kicked!",
948 if(
seeds_[reinterpret_cast<long int>(socket.get())].empty()) {
953 else if(!(
user_handler_->login(username, password,
seeds_[reinterpret_cast<unsigned long>(socket.get())]))) {
954 const time_t now = time(NULL);
957 seeds_.erase(reinterpret_cast<unsigned long>(socket.get()));
990 <<
"Login attempt with incorrect password for nickname '" << username <<
"'.\n";
996 seeds_.erase(reinterpret_cast<long int>(socket.get()));
1003 async_send_error(socket,
"The nickname '" + username +
"' is not registered. "
1011 process_command(
"kick " +
p->info().name() +
" autokick by registered user", username);
1028 <<
"\thas logged on" << (registered ?
" to a registered account" :
"") <<
"\n";
1031 LOG_SERVER <<
"Admin automatically recognized: IP: "
1033 << username << std::endl;
1036 "If you no longer want to be automatically authenticated use '/query signout'.");
1052 const std::string& user,
const char* error_code,
bool force_confirmation)
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.");
1065 seeds_[
reinterpret_cast<long int>(socket.get())] = salt;
1070 e.
set_attr(
"password_request",
"yes");
1073 e.
set_attr(
"force_confirmation", force_confirmation ?
"yes" :
"no");
1074 if(*error_code !=
'\0') {
1075 e.
set_attr(
"error_code", error_code);
1086 boost::tie(boost::tuples::ignore, inserted) =
player_connections_.insert(player_connections::value_type(socket, player));
1115 if(doc->child(
"refresh_lobby")) {
1152 if((whisper[
"receiver"] ==
"") || (whisper[
"message"] ==
"")) {
1155 "message=\"Invalid number of arguments\"\n"
1156 "sender=\"server\"\n"
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.";
1186 if (command ==
"status") {
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")
1202 if (command ==
"signout") {
1205 << player.
name() << std::endl;
1208 response <<
"You are no longer recognized as an administrator.";
1213 LOG_SERVER <<
"Admin Command: type: " << command
1215 <<
"\tnick: "<< player.
name() << std::endl;
1219 }
else if (command ==
"help" || command.empty()) {
1221 }
else if (command ==
"admin" || command.find(
"admin ") == 0) {
1227 if (command.size() >= 6) passwd = command.substr(6);
1231 << player.
name() << std::endl;
1234 response <<
"You are now recognized as an administrator.";
1239 WRN_SERVER <<
"FAILED Admin attempt with password: '" << passwd <<
"'\tIP: "
1241 << player.
name() << std::endl;
1242 response <<
"Error: wrong password";
1245 response <<
"Error: unrecognized query: '" << command <<
"'\n" <<
help_msg;
1258 if(nickserv.
child(
"register")) {
1261 (*nickserv.
child(
"register"))[
"password"].to_string()));
1263 std::stringstream
msg;
1264 msg <<
"Your username has been registered." <<
1266 ((*nickserv.
child(
"register"))[
"mail"].empty() ?
1267 " It is recommended that you provide an email address for password recovery." :
"");
1280 send_server_message(socket,
"There was an error registering your username. The error message was: "
1287 if(nickserv.
child(
"set")) {
1301 send_server_message(socket,
"There was an error updating your details. The error message was: "
1309 if(nickserv.
child(
"details")) {
1316 if(nickserv.
child(
"info")) {
1322 (*nickserv.
child(
"info"))[
"name"].to_string() +
"'. " +
" The error message was: "
1329 if(nickserv.
child(
"drop")) {
1356 send_server_message(socket,
"There was an error dropping your username. The error message was: "
1375 send_server_message(socket,
"This server is shutting down. You aren't allowed to make new games. Please reconnect to the new server.");
1395 const std::string game_name = create_game[
"name"].to_string();
1396 const std::string game_password = create_game[
"password"].to_string();
1399 <<
"\tcreates a new game: \"" << game_name <<
"\".\n";
1407 if(game_password.empty() ==
false) {
1419 assert(gamelist != NULL);
1430 const simple_wml::node::child_list::const_iterator
g =
1432 if (g != games.end()) {
1433 const size_t index = g - games.begin();
1437 LOG_SERVER <<
"Could not find game (" << game_ptr->
id()
1438 <<
") to delete in games_and_users_list_.\n";
1446 int game_id = join[
"id"].to_int();
1454 <<
"\tattempted to join unknown game:\t" << game_id <<
".\n";
1459 }
else if (!g->level_init()) {
1461 <<
"\tattempted to join uninitialized game:\t\"" << g->name()
1462 <<
"\" (" << game_id <<
").\n";
1469 }
else if (g->registered_users_only() && !
player_connections_.find(socket)->info().registered()) {
1474 }
else if (g->player_is_banned(socket)) {
1477 <<
"\" (" << game_id <<
").\n";
1482 }
else if(!observer && !g->password_matches(password)) {
1484 <<
"\tattempted to join game:\t\"" << g->name() <<
"\" ("
1485 << game_id <<
") with bad password\n";
1491 bool joined = g->add_player(socket, observer);
1494 <<
"\tattempted to observe game:\t\"" << g->name() <<
"\" ("
1495 << game_id <<
") which doesn't allow observers.\n";
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.)");
1504 g->describe_slots();
1509 "gamelist",
"game", g->description(), diff);
1512 if (diff1 || diff2) {
1522 game&
g = *(
p->get_game());
1527 if (doc->child(
"snapshot") || doc->child(
"scenario")) {
1539 <<
"\tcreated game:\t\"" << g.
name() <<
"\" ("
1540 << g.
id() <<
").\n";
1544 assert(gamelist != NULL);
1551 <<
"\tsent scenario data in game:\t\"" << g.
name() <<
"\" ("
1552 << g.
id() <<
") without a 'multiplayer' child.\n";
1556 send_server_message(socket,
"The scenario data is missing the [multiplayer] tag which contains the game settings. Game aborted.");
1561 desc.
set_attr_dup(
"id", lexical_cast_default<std::string>(g.
id()).c_str());
1564 <<
"\tsent scenario data in game:\t\"" << g.
name() <<
"\" ("
1565 << g.
id() <<
") although it's already initialized.\n";
1575 if (!data[
"mp_shroud"].to_bool()) {
1579 if (!
e->attr(
"require_era").to_bool(
true)) {
1580 desc.
set_attr(
"require_era",
"no");
1584 if (data.
attr(
"require_scenario").
to_bool(
false)) {
1585 desc.
set_attr(
"require_scenario",
"yes");
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");
1615 << player.
name() <<
" (socket:" << socket
1616 <<
") while the scenario wasn't yet initialized.\n" << data.
output();
1623 << player.
name() <<
"\tsent [store_next_scenario] in game:\t\""
1624 << g.
name() <<
"\" (" << g.
id()
1625 <<
") while the scenario is not yet initialized.";
1636 << g.
name() <<
"\" (" << g.
id()
1637 <<
") is initialized but has no description_.\n";
1646 <<
"\tsent scenario data in game:\t\"" << g.
name() <<
"\" ("
1647 << g.
id() <<
") without a 'multiplayer' child.\n";
1649 send_server_message(socket,
"The scenario data is missing the [multiplayer] tag which contains the game settings. Game aborted.");
1656 desc.
set_attr_dup(
"map_data", s[
"mp_shroud"].to_bool() ?
"" :
1659 if (!
e->attr(
"require_era").to_bool(
true)) {
1660 desc.
set_attr(
"require_era",
"no");
1664 if (data.
attr(
"require_scenario").
to_bool(
false)) {
1665 desc.
set_attr(
"require_scenario",
"yes");
1670 "[notify_next_scenario]\n[/notify_next_scenario]\n",
1672 g.
send_data(notify_next_scenario, socket);
1679 }
else if (data.
child(
"load_next_scenario")) {
1682 }
else if (data.
child(
"start_game")) {
1695 }
else if (data.
child(
"update_game")) {
1699 }
else if (data.
child(
"leave_game")) {
1717 if (diff1 || diff2) {
1743 }
else if (data.
child(
"change_faction")) {
1754 }
else if (data.
child(
"muteall")) {
1770 }
else if (data.
child(
"kick") || data.
child(
"ban")) {
1771 bool ban = (data.
child(
"ban") != NULL);
1797 if ((*
info)[
"type"] ==
"termination") {
1799 if ((*
info)[
"condition"].to_string() ==
"out of sync") {
1804 }
else if (data.
child(
"turn")) {
1812 }
else if (data.
child(
"whiteboard")) {
1815 }
else if (data.
child(
"change_turns_wml")) {
1822 }
else if (data.
child(
"message")) {
1825 }
else if (data.
child(
"stop_updates")) {
1829 }
else if (data.
child(
"error")
1830 || data.
child(
"side_secured")
1838 << player.
name() <<
" (socket:" << socket <<
") in game: \""
1839 << g.
name() <<
"\" (" << g.
id() <<
")\n" << data.
output();
1842 typedef std::map<socket_ptr, std::deque<boost::shared_ptr<simple_wml::document> > >
SendQueue;
1849 if(iter == send_queue.end()) {
1862 if(send_queue[socket].empty()) {
1863 send_queue.erase(socket);
1869 send_queue[socket].pop_front();
1892 g->remove_player(socket,
true,
false);
1895 const size_t index =
std::find(users.begin(), users.end(), iter->info().config_address()) - users.begin();
1900 iter->info().config_address(), diff)) {
1905 LOG_SERVER << ip <<
"\t" << iter->info().name()
1906 <<
"\twas logged off" <<
"\n";
1912 i->log_off = time(
nullptr);
1917 if(socket->is_open())
1924 if(
player.socket() != exclude)
1931 if(
player.socket() != exclude)
1939 if(
player.socket() != exclude)
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";
2254 if (issuer_name ==
"*socket*" && query.at(0) ==
'+') {
2259 std::find(query.begin(), query.end(),
':');
2260 std::string issuer(query.begin() + 1, issuer_end);
2261 if (!issuer.empty()) {
2262 issuer_name =
"+" + issuer +
"+";
2276 std::ostringstream out;
2279 out <<
"Command '" << command <<
"' is not recognized.\n" <<
help_msg;
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";
2287 out <<
"An internal server error occurred (std::bad_function_call) while executing '" << command <<
"'\n";
2294 std::string msg =
"While handling a command, caught an invalid utf8 exception: ";
2297 return (msg +
'\n');
2303 assert(out != NULL);
2309 if (parameters ==
"now") {
2310 throw server_shutdown(
"shut down by admin command");
2315 timer_.expires_from_now(boost::posix_time::seconds(10));
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.";
2323 assert(out != NULL);
2331 *out <<
"No restart_command configured! Not restarting.";
2335 timer_.expires_from_now(boost::posix_time::seconds(10));
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.";
2344 assert(out != NULL);
2346 if (parameters.empty()) {
2349 }
else if (issuer_name !=
"*socket*") {
2353 request_sample_frequency = atoi(parameters.c_str());
2354 if (request_sample_frequency <= 0) {
2355 *out <<
"Sampling turned off.";
2357 *out <<
"Sampling every " << request_sample_frequency <<
" requests.";
2362 assert(out != NULL);
2367 assert(out != NULL);
2369 *out <<
"Number of games = " <<
games().size()
2374 assert(out != NULL);
2379 assert(out != NULL);
2384 assert(out != NULL);
2389 assert(out != NULL);
2417 assert(out != NULL);
2419 if (parameters ==
"") {
2420 *out <<
"You must type a message.";
2426 LOG_SERVER <<
"Admin message: <" << sender << (message.find(
"/me ") == 0
2427 ?
std::string(message.begin() + 3, message.end()) +
">"
2428 :
"> " + message) <<
"\n";
2432 msg.
set_attr_dup(
"sender", (
"admin message from " + sender).c_str());
2443 *out <<
"Sorry, no admin available right now. But your message got logged.";
2447 *out <<
"Message sent to " << n <<
" admins.";
2451 assert(out != NULL);
2454 if (first_space == parameters.end()) {
2455 *out <<
"You must name a receiver.";
2460 const std::string receiver(parameters.begin(), first_space);
2464 *out <<
"You must type a message.";
2471 msg.
set_attr_dup(
"sender", (
"server message from " + sender).c_str());
2474 if (receiver !=
player.info().
name().c_str()) {
2478 *out <<
"Message to " << receiver <<
" successfully sent.";
2482 *out <<
"No such nick: " << receiver;
2486 assert(out !=
nullptr);
2488 if (parameters ==
"") {
2489 *out <<
"You must type a message.";
2495 LOG_SERVER <<
"<server" << (parameters.find(
"/me ") == 0
2496 ?
std::string(parameters.begin() + 3, parameters.end()) +
">"
2497 :
"> " + parameters) <<
"\n";
2499 *out <<
"message '" << parameters <<
"' relayed to players";
2503 assert(out !=
nullptr);
2505 if (parameters ==
"") {
2506 *out <<
"You must type a message.";
2511 LOG_SERVER <<
"<server" << (parameters.find(
"/me ") == 0
2512 ?
std::string(parameters.begin() + 3, parameters.end()) +
">"
2513 :
"> " + parameters) <<
"\n";
2515 *out <<
"message '" << parameters <<
"' relayed to players";
2519 assert(out != NULL);
2521 *out <<
"STATUS REPORT for '" << parameters <<
"'";
2522 bool found_something =
false;
2528 found_something =
true;
2532 if (!found_something) {
2539 const bool match_ip = (
std::count(parameters.begin(), parameters.end(),
'.') >= 1);
2541 if (parameters ==
"" || parameters ==
"*"
2544 found_something =
true;
2548 if (!found_something) *out <<
"\nNo match found. You may want to check with 'searchlog'.";
2552 assert(out != NULL);
2554 *out <<
"CLONES STATUS REPORT";
2555 std::set<std::string> clones;
2557 if (clones.find(
client_address(it->socket())) != clones.end())
continue;
2570 if (clones.empty()) {
2571 *out <<
"No clones found.";
2576 assert(out !=
nullptr);
2581 if (parameters.empty()) {
2593 ERR_SERVER <<
"While handling bans, caught an invalid utf8 exception: " << e.what() << std::endl;
2598 assert(out != NULL);
2603 if (first_space == parameters.end()) {
2611 const std::string duration(first_space + 1, second_space);
2612 time_t parsed_time = time(NULL);
2614 *out <<
"Failed to parse the ban duration: '" << duration <<
"'\n"
2619 if (second_space == parameters.end()) {
2622 std::string reason(second_space + 1, parameters.end());
2624 if (reason.empty()) {
2625 *out <<
"You need to give a reason for the ban.";
2641 if (banned) *out <<
"\n";
2662 *out <<
"Nickname mask '" <<
target <<
"' did not match, no bans set.";
2669 assert(out != NULL);
2673 if (first_space == parameters.end()) {
2679 const std::string duration(first_space + 1, second_space);
2680 time_t parsed_time = time(NULL);
2682 *out <<
"Failed to parse the ban duration: '" << duration <<
"'\n"
2687 if (second_space == parameters.end()) {
2690 std::string reason(second_space + 1, parameters.end());
2692 if (reason.empty()) {
2693 *out <<
"You need to give a reason for the ban.";
2698 std::vector<socket_ptr> users_to_kick;
2710 users_to_kick.push_back(
player.socket());
2717 if (banned) *out <<
"\n";
2720 users_to_kick.push_back(
player.socket());
2738 *out <<
"Nickname mask '" <<
target <<
"' did not match, no bans set.";
2743 for(
const auto& user : users_to_kick) {
2752 assert(out != NULL);
2756 if (first_space == parameters.end()) {
2764 first_space = second_space;
2765 second_space =
std::find(first_space + 1, parameters.end(),
' ');
2767 const std::string duration(first_space + 1, second_space);
2768 time_t parsed_time = time(NULL);
2770 *out <<
"Failed to parse the ban duration: '" << duration <<
"'\n"
2775 if (second_space == parameters.end()) {
2778 std::string reason(second_space + 1, parameters.end());
2780 if (reason.empty()) {
2781 *out <<
"You need to give a reason for the ban.";
2795 if (banned) *out <<
"\n";
2816 *out <<
"Nickname mask '" <<
target <<
"' did not match, no bans set.";
2823 assert(out != NULL);
2825 if (parameters ==
"") {
2826 *out <<
"You must enter an ipmask to unban.";
2833 assert(out != NULL);
2835 if (parameters ==
"") {
2836 *out <<
"You must enter an ipmask to ungban.";
2843 assert(out != NULL);
2845 if (parameters ==
"") {
2846 *out <<
"You must enter a mask to kick.";
2852 (i == parameters.end() ?
"You have been kicked."
2853 :
"You have been kicked. Reason: " +
std::string(i + 1, parameters.end()));
2854 bool kicked =
false;
2856 const bool match_ip = (
std::count(kick_mask.begin(), kick_mask.end(),
'.') >= 1);
2857 std::vector<socket_ptr> users_to_kick;
2862 users_to_kick.push_back(
player.socket());
2865 for(
const auto& socket : users_to_kick) {
2866 if (kicked) *out <<
"\n";
2868 *out <<
"Kicked " << player_connections_.find(socket)->
name() <<
" ("
2870 << kick_message <<
"'";
2874 if (!kicked) *out <<
"No user matched '" << kick_mask <<
"'.";
2878 assert(out != NULL);
2880 if (parameters ==
"") {
2882 *out <<
"Message of the day:\n" <<
motd_;
2885 *out <<
"No message of the day set.";
2891 *out <<
"Message of the day set to: " <<
motd_;
2895 assert(out != NULL);
2897 if (parameters.empty()) {
2898 *out <<
"You must enter a mask to search for.";
2901 *out <<
"IP/NICK LOG for '" << parameters <<
"'";
2903 bool found_something =
false;
2907 const bool match_ip = (
std::count(parameters.begin(), parameters.end(),
'.') >= 1);
2908 for (std::deque<connection_log>::const_iterator
i =
ip_log_.begin();
2914 found_something =
true;
2919 *out <<
"\n'" << username <<
"' @ " << ip <<
" last seen: " <<
lg::get_timestamp(
i->log_off,
"%H:%M:%S %d.%m.%Y");
2923 if (!found_something) *out <<
"\nNo match found.";
2927 assert(out != NULL);
2931 if (parameters ==
"") {
2939 ERR_SERVER <<
"While handling dul (deny unregistered logins), caught an invalid utf8 exception: " << e.what() << std::endl;
2948 user.info().mark_available();
2951 "user", user.info().config_address(), udiff)) {
2954 ERR_SERVER <<
"ERROR: delete_game(): Could not find user in players_. (socket: "
2955 << user.socket() <<
")\n";
2961 game_ptr->send_data(leave_game_doc);
2965 for(;iter != i_end; iter++)
2983 bool keep_alive =
false;
2984 size_t min_threads = 5;
2985 size_t max_threads = 0;
2987 srand(static_cast<unsigned>(time(
nullptr)));
2998 for (
int arg = 1; arg != argc; ++arg) {
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';
3021 std::cerr <<
"unknown debug level: " << s <<
'\n';
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));
3028 std::cerr <<
"unknown debug domain: " << s <<
'\n';
3033 }
else if ((val ==
"--port" || val ==
"-p") && arg+1 != argc) {
3034 port = atoi(argv[++arg]);
3035 }
else if (val ==
"--keepalive") {
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";
3053 }
else if (val ==
"--version" || val ==
"-V") {
3057 }
else if (val ==
"--daemon" || val ==
"-d") {
3059 ERR_SERVER <<
"Running as a daemon is not supported on this platform" << std::endl;
3062 const pid_t pid = fork();
3064 ERR_SERVER <<
"Could not fork and run as a daemon" << std::endl;
3066 }
else if (pid > 0) {
3067 std::cout <<
"Started wesnothd as a daemon with process id "
3074 }
else if ((val ==
"--threads" || val ==
"-t") && arg+1 != argc) {
3075 min_threads = atoi(argv[++arg]);
3076 if (min_threads > 30) {
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) {
3084 ERR_SERVER <<
"unknown option: " << val << std::endl;
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...
boost::shared_ptr< DataSize > data_size
node & add_child(const char *name)
config read_config() const
Read the server config from file 'config_file_'.
static void enter_lobby(player_record &)
child_itors child_range(const std::string &key)
std::deque< boost::shared_ptr< game > > games()
void start_game(const socket_ptr starter)
static thandler * handler
void async_send_error(socket_ptr socket, const std::string &msg, const char *error_code="")
std::ostream & games(std::ostream &out) const
void handle_nickserv(socket_ptr socket, simple_wml::node &nickserv)
node & set_attr(const char *key, const char *value)
void handle_whisper(socket_ptr socket, simple_wml::node &whisper)
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 *)
void serverside_handshake(socket_ptr socket)
simple_wml::document games_and_users_list_
std::deque< login_log >::size_type failed_login_buffer_size_
void set(CURSOR_TYPE type)
Use the default parameter to reset cursors.
void handle_read_from_player(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
void apply_diff(const node &diff)
void unmute_observer(const simple_wml::node &unmute, const socket_ptr unmuter)
const socket_ptr socket() const
void list_deleted_bans(std::ostringstream &out, const std::string &mask="*") const
void load_config(const config &)
std::string get_timestamp(const time_t &t, const std::string &format)
const std::string denied_msg
void handle_version(socket_ptr socket)
void status_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
boost::asio::posix::stream_descriptor input_
server socket/fifo.
handle_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler, boost::uint32_t size, boost::shared_ptr< simple_wml::document > doc)
void games_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
std::string ban(const std::string &, const time_t &, const std::string &, const std::string &, const std::string &, const std::string &="")
void send_server_message(const char *message, socket_ptr sock=socket_ptr(), simple_wml::document *doc=nullptr) const
handle_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
static std::string player_status(const wesnothd::player_record &player)
void handle_login(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
GLuint GLuint GLsizei GLenum type
static l_noret error(LoadState *S, const char *why)
#define MP_TOO_MANY_ATTEMPTS_ERROR
void update_side_data()
Resets the side configuration according to the scenario data.
void accept_connection(const boost::system::error_code &error, socket_ptr socket)
int main(int argc, char **argv)
void kickban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void handle_query(socket_ptr socket, simple_wml::node &query)
node & set_attr_int(const char *key, int value)
void help_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
#define WRN_SERVER
clients send wrong/unexpected data
node & set_attr(const char *key, const char *value)
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)
void wml_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
utf8::string lowercase(const utf8::string &s)
Returns a lowercased version of the string.
server(int port, bool keep_alive, const std::string &config_file, size_t min_threads, size_t max_threads)
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.
GLuint const GLfloat * val
boost::asio::signal_set sighup_
void process_message(simple_wml::document &data, const socket_ptr user)
void stats_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
size_t default_max_messages_
#define MP_NAME_TOO_LONG_ERROR
void handle_read_from_fifo(const boost::system::error_code &error, std::size_t bytes_transferred)
void update_game_in_lobby(const wesnothd::game &g, const socket_ptr &exclude=socket_ptr())
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)
void requests_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Define the errors the server may send during the login procedure.
std::vector< std::string > accepted_versions_
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
To lexical_cast(From value)
Lexical cast converts one type to another.
void ban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
Definitions for the interface to Wesnoth Markup Language (WML).
void handle_sighup(const boost::system::error_code &error, int signal_number)
boost::scoped_ptr< user_handler > user_handler_
void handle_create_game(socket_ptr socket, simple_wml::node &create_game)
An example of how to implement user_handler.
void handle_termination(const boost::system::error_code &error, int signal_number)
std::string is_ip_banned(const std::string &ip) const
bool deny_unregistered_login_
void handle_graceful_timeout(const boost::system::error_code &error)
void async_receive_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
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 *)
static bool check_error(const boost::system::error_code &error, socket_ptr socket)
bool is_player(const socket_ptr player) const
boost::asio::signal_set sigs_
GLdouble GLdouble GLdouble GLdouble q
node * child(const char *name)
std::ostream & requests(std::ostream &out) const
bool exists(const image::locator &i_locator)
returns true if the given image actually exists, without loading it.
node * child(const char *name)
socket_ptr kick_member(const simple_wml::node &kick, const socket_ptr kicker)
Kick a member by name.
bool set_log_domain_severity(std::string const &name, int severity)
void handle_choice(const simple_wml::node &data, const socket_ptr user)
simple_wml::document login_response_
#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 ...
A user_handler implementation to link the server with a phpbb3 forum.
bool allow_remote_shutdown_
std::map< std::string, config > proxy_versions_
const std::string & name() const
void unban_group(std::ostringstream &os, const std::string &group)
void msg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void send_data(simple_wml::document &data, const socket_ptr exclude=socket_ptr(), std::string packet_type="") const
void add_player(socket_ptr socket, const wesnothd::player &)
void remove_player(socket_ptr socket)
void handle_handshake(const boost::system::error_code &error, socket_ptr socket, boost::shared_array< char > buf)
simple_wml::node * description() const
std::string restart_command
void remove_child(const char *name, size_t index)
const std::string & termination_reason() const
bool describe_slots()
Set the description to the number of available slots.
socket_ptr ban_user(const simple_wml::node &ban, const socket_ptr banner)
Ban and kick a user by name.
void clones_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void handle_player_in_lobby(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
void read_version(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
static void set_game(player_record &, boost::shared_ptr< game >)
void handle_player_in_game(socket_ptr socket, boost::shared_ptr< simple_wml::document > doc)
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
boost::asio::deadline_timer timer_
void motd_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
GLenum GLuint GLsizei const char * buf
std::vector< std::string > tor_ip_list_
void bans_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void searchlog_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void request_version(const boost::system::error_code &error, socket_ptr socket)
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)
std::string is_ip_banned(const std::string &ip) const
boost::shared_array< char > buffer
simple_wml::document version_query_response_
Templates and utility-routines for strings and numbers.
void perform_controller_tweaks()
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
std::string replay_save_path_
void kick_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void clean_user_handler(const time_t &now)
GLuint GLuint GLsizei count
static void make_add_diff(const simple_wml::node &src, const char *gamelist, const char *type, simple_wml::document &out, int index=-1)
size_t concurrent_connections_
void operator()(const boost::system::error_code &error, std::size_t)
void send_to_player(socket_ptr socket, simple_wml::document &doc)
void netstats_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
size_t default_time_period_
void handle_send_to_player(socket_ptr socket)
bool player_is_in_game(socket_ptr socket) const
ErrorHandler error_handler
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
std::string client_address(socket_ptr socket)
void async_send_doc(socket_ptr socket, simple_wml::document &doc, Handler handler, ErrorHandler error_handler)
void operator()(const boost::system::error_code &error, std::size_t size)
boost::asio::ip::tcp::acceptor acceptor_
const std::string help_msg
const boost::shared_ptr< game > get_game() const
void cleanup_game(game *)
std::string server_message
static void async_send_warning(socket_ptr socket, const std::string &msg, const char *warning_code)
static simple_wml::node * starting_pos(simple_wml::node &data)
void set_moderator(bool moderator)
static std::string stats()
void game_terminated(const std::string &reason)
void sample_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
bool is_owner(const socket_ptr player) const
std::map< std::string, tfilter >::iterator itor
void dump_stats(const time_t &now)
Thrown by operations encountering invalid UTF-8 data.
node & set_attr_dup(const char *key, const char *value)
void unban(std::ostringstream &os, const std::string &ip)
void read_from_player(socket_ptr socket)
void process_whiteboard(simple_wml::document &data, const socket_ptr user)
Handles incoming [whiteboard] data.
void unban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
const std::string config_file_
boost::shared_ptr< simple_wml::document > doc
std::map< socket_ptr, std::deque< boost::shared_ptr< simple_wml::document > > > SendQueue
const std::string & parameters
std::vector< node * > child_list
std::function< void(server *, const std::string &, const std::string &, std::string &, std::ostringstream *)> cmd_handler
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
void copy_into(node &n) const
std::string admin_passwd_
const child_list & children(const char *name) const
void reset_last_synced_context_id()
wesnothd::ban_manager ban_manager_
std::deque< login_log > failed_logins_
void adminmsg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
#define ERR_SERVER
fatal and directly server related errors/warnings, ie not caused by erroneous client data ...
Declarations for File-IO.
void read(config &cfg, std::istream &in, abstract_validator *validator)
bool to_bool(bool default_value=false) const
bool is_moderator() const
void list_bans(std::ostringstream &out, const std::string &mask="*") const
static void null_handler(socket_ptr)
#define MP_PASSWORD_REQUEST
void load_next_scenario(const socket_ptr user)
A user (player only?) asks for the next scenario to advance to.
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...
#define MP_NAME_INACTIVE_WARNING
std::map< std::string, config > redirected_versions_
void set_password(const std::string &passwd)
void send_server_message_to_all(const char *message, socket_ptr exclude=socket_ptr()) const
const char * begin() const
void handle_message(socket_ptr socket, simple_wml::node &message)
int request_sample_frequency
void restart_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
#define MP_PASSWORD_REQUEST_FOR_LOGGED_IN_NAME
handle_receive_doc(socket_ptr socket, Handler handler, ErrorHandler error_handler)
#define MP_NAME_TAKEN_ERROR
std::istream * preprocess_file(std::string const &fname, preproc_map *defines)
const std::string & get_ban_help() const
boost::asio::io_service io_service_
std::map< long int, std::string > seeds_
const std::string & name() const
bool find(E event, F functor)
Tests whether an event handler is available.
void handle_join_game(socket_ptr socket, simple_wml::node &join)
Base class for all the errors encountered by the engine.
void send_to_lobby(simple_wml::document &data, socket_ptr exclude=socket_ptr()) const
std::map< std::string, cmd_handler > cmd_handlers_
#define LOG_SERVER
normal events
Standard logging facilities (interface).
void dul_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
static lg::log_domain log_config("config")
player_connections player_connections_
GLsizei GLenum GLuint GLuint GLsizei char * message
void load_config()
Parse the server config into local variables.
simple_wml::document join_lobby_response_
utf8::string & insert(utf8::string &str, const size_t pos, const utf8::string &insert)
Insert a UTF-8 string at the specified position.
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_
const node::child_list & children(const char *name) const
void process_change_turns_wml(simple_wml::document &data, const socket_ptr user)
Handles incoming [change_turns_wml] data.
#define MP_NAME_RESERVED_ERROR
void ungban_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
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.
const string_span & attr(const char *key) const
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...
void metrics_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
static bool read_config(config &src, config &dst)
A config object defines a single node in a WML file, with access to child nodes.
void lobbymsg_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void pm_handler(const std::string &, const std::string &, std::string &, std::ostringstream *)
void create_game(player_record &host, simple_wml::node &create_game)
void unban_user(const simple_wml::node &unban, const socket_ptr unbanner)
const simple_wml::node * config_address() const
socket_ptr const std::string & name
void send_server_message_to_lobby(const std::string &message, socket_ptr exclude=socket_ptr()) const
const std::string version
GLsizei const GLcharARB ** string
void mute_all_observers()
bool remove_player(const socket_ptr player, const bool disconnect=false, const bool destruct=false)
Removes a user from the game.
void send_server_message(socket_ptr socket, const std::string &message)
void set_termination_reason(const std::string &reason)
boost::asio::streambuf admin_cmd_
std::vector< std::string > disallowed_names_
const string_span & attr(const char *key) const
#define MP_INVALID_CHARS_IN_NAME_ERROR
simple_wml::document & level()
The full scenario data.
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)