campaign_server/campaign_server.cpp

Go to the documentation of this file.
00001 /* $Id: campaign_server.cpp 53151 2012-02-21 23:36:57Z ai0867 $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014    */
00015 
00016 /**
00017  * @file
00018  * Wesnoth addon server.
00019  * Expects a "server.cfg" config file in the current directory
00020  * and saves addons under data/.
00021  */
00022 
00023 #include "filesystem.hpp"
00024 #include "foreach.hpp"
00025 #include "log.hpp"
00026 #include "network_worker.hpp"
00027 #include "serialization/binary_or_text.hpp"
00028 #include "serialization/parser.hpp"
00029 #include "serialization/string_utils.hpp"
00030 #include "game_config.hpp"
00031 #include "addon/validation.hpp"
00032 #include "version.hpp"
00033 #include "server/input_stream.hpp"
00034 #include "util.hpp"
00035 
00036 #include <csignal>
00037 
00038 #include <boost/iostreams/filter/gzip.hpp>
00039 
00040 // the fork execute is unix specific only tested on Linux quite sure it won't
00041 // work on Windows not sure which other platforms have a problem with it.
00042 #if !(defined(_WIN32))
00043 #include <errno.h>
00044 #endif
00045 
00046 static lg::log_domain log_network("network");
00047 #define LOG_CS if (lg::err.dont_log(log_network)) ; else lg::err(log_network, false)
00048 
00049 //compatibility code for MS compilers
00050 #ifndef SIGHUP
00051 #define SIGHUP 20
00052 #endif
00053 /** @todo FIXME: should define SIGINT here too, but to what? */
00054 
00055 static void exit_sighup(int signal) {
00056     assert(signal == SIGHUP);
00057     LOG_CS << "SIGHUP caught, exiting without cleanup immediately.\n";
00058     exit(128 + SIGHUP);
00059 }
00060 
00061 static void exit_sigint(int signal) {
00062     assert(signal == SIGINT);
00063     LOG_CS << "SIGINT caught, exiting without cleanup immediately.\n";
00064     exit(0);
00065 }
00066 
00067 static void exit_sigterm(int signal) {
00068     assert(signal == SIGTERM);
00069     LOG_CS << "SIGTERM caught, exiting without cleanup immediately.\n";
00070     exit(128 + SIGTERM);
00071 }
00072 
00073 namespace {
00074     // Markup characters recognized by GUI1 code. These must be
00075     // the same as the constants defined in marked-up_text.cpp.
00076     const std::string illegal_markup_chars = "*`~{^}|@#<&";
00077 
00078     inline bool is_text_markup_char(char c)
00079     {
00080         return illegal_markup_chars.find(c) != std::string::npos;
00081     }
00082 
00083     config construct_message(const std::string& msg)
00084     {
00085         config cfg;
00086         cfg.add_child("message")["message"] = msg;
00087         return cfg;
00088     }
00089 
00090     config construct_error(const std::string& msg)
00091     {
00092         config cfg;
00093         cfg.add_child("error")["message"] = "#Error: " + msg;
00094         LOG_CS << "ERROR: "<<msg<<"\n";
00095         return cfg;
00096     }
00097 
00098     class campaign_server
00099     {
00100         public:
00101             explicit campaign_server(const std::string& cfgfile,size_t min_thread = 10,size_t max_thread = 0);
00102             void run();
00103             ~campaign_server()
00104             {
00105                 delete input_;
00106                 scoped_ostream cfgfile = ostream_file(file_);
00107                 write(*cfgfile, cfg_);
00108             }
00109         private:
00110             /**
00111              * Fires a script, if no script defined it will always return true
00112              * If a script is defined but can't be executed it will return false
00113              */
00114             void fire(const std::string& hook, const std::string& addon);
00115             void convert_binary_to_gzip();
00116             int load_config(); // return the server port
00117             const config &campaigns() const { return cfg_.child("campaigns"); }
00118             config &campaigns() { return cfg_.child("campaigns"); }
00119             config cfg_;
00120             const std::string file_;
00121             const network::manager net_manager_;
00122             std::map<std::string, std::string> hooks_;
00123             input_stream* input_;
00124             int compress_level_;
00125             const network::server_manager server_manager_;
00126 
00127     };
00128 
00129     void campaign_server::fire(const std::string& hook, const std::string& addon)
00130     {
00131         const std::map<std::string, std::string>::const_iterator itor = hooks_.find(hook);
00132         if(itor == hooks_.end()) return;
00133 
00134         const std::string& script = itor->second;
00135         if(script == "") return;
00136 
00137 #if (defined(_WIN32))
00138         LOG_CS << "ERROR: Tried to execute a script on an unsupported platform\n";
00139         return;
00140 #else
00141         pid_t childpid;
00142 
00143         if((childpid = fork()) == -1) {
00144             LOG_CS << "ERROR: fork failed while updating campaign " << addon << "\n";
00145             return;
00146         }
00147 
00148         if(childpid == 0) {
00149             /*** We're the child process ***/
00150 
00151             // execute the script, we run is a separate thread and share the
00152             // output which will make the logging look ugly.
00153             execlp(script.c_str(), script.c_str(), addon.c_str(), static_cast<char *>(NULL));
00154 
00155             // exec() and family never return; if they do, we have a problem
00156             std::cerr << "ERROR: exec failed with errno " << errno << " for addon " << addon
00157                       << '\n';
00158             exit(errno);
00159 
00160         } else {
00161             return;
00162         }
00163 
00164 #endif
00165     }
00166 
00167     int campaign_server::load_config()
00168     {
00169         scoped_istream stream = istream_file(file_);
00170         read(cfg_, *stream);
00171         bool network_use_system_sendfile = cfg_["network_use_system_sendfile"].to_bool();
00172         network_worker_pool::set_use_system_sendfile(network_use_system_sendfile);
00173         cfg_["network_use_system_sendfile"] = network_use_system_sendfile;
00174         /** Seems like compression level above 6 is waste of cpu cycle */
00175         compress_level_ = cfg_["compress_level"].to_int(6);
00176         cfg_["compress_level"] = compress_level_;
00177         return cfg_["port"].to_int(default_campaignd_port);
00178     }
00179 
00180     campaign_server::campaign_server(const std::string& cfgfile,
00181             size_t min_thread, size_t max_thread) :
00182         cfg_(),
00183         file_(cfgfile),
00184         net_manager_(min_thread,max_thread),
00185         hooks_(),
00186         input_(0),
00187         compress_level_(0), // Will be properly set by load_config()
00188         server_manager_(load_config())
00189     {
00190 #ifndef _MSC_VER
00191         signal(SIGHUP, exit_sighup);
00192 #endif
00193         signal(SIGINT, exit_sigint);
00194         signal(SIGTERM, exit_sigterm);
00195 
00196         cfg_.child_or_add("campaigns");
00197 
00198         // load the hooks
00199         hooks_.insert(std::make_pair(std::string("hook_post_upload"), cfg_["hook_post_upload"]));
00200         hooks_.insert(std::make_pair(std::string("hook_post_erase"), cfg_["hook_post_erase"]));
00201     }
00202 
00203     void find_translations(const config& cfg, config& campaign)
00204     {
00205         foreach (const config &dir, cfg.child_range("dir"))
00206         {
00207             if (dir["name"] == "LC_MESSAGES") {
00208                 config &language = campaign.add_child("translation");
00209                 language["language"] = cfg["name"];
00210             } else {
00211                 find_translations(dir, campaign);
00212             }
00213         }
00214     }
00215 
00216     // Add a file COPYING.txt with the GPL to an uploaded campaign.
00217     void add_license(config &data)
00218     {
00219         config &dir = data.find_child("dir", "name", data["campaign_name"]);
00220         // No top-level directory? Hm..
00221         if (!dir) return;
00222 
00223         // Don't add if it already exists.
00224         if (dir.find_child("file", "name", "COPYING.txt")) return;
00225         if (dir.find_child("file", "name", "COPYING")) return;
00226         if (dir.find_child("file", "name", "copying.txt")) return;
00227         if (dir.find_child("file", "name", "Copying.txt")) return;
00228         if (dir.find_child("file", "name", "COPYING.TXT")) return;
00229 
00230         // Copy over COPYING.txt
00231         std::string contents = read_file("COPYING.txt");
00232         if (contents.empty()) {
00233             LOG_CS << "Could not find COPYING.txt, path is \""
00234                 << game_config::path << "\"\n";
00235             return;
00236         }
00237         config &copying = dir.add_child("file");
00238         copying["name"] = "COPYING.txt";
00239         copying["contents"] = contents;
00240 
00241     }
00242     /// @todo Check if this function has any purpose left
00243     void campaign_server::convert_binary_to_gzip()
00244     {
00245         if (!cfg_["encoded"].to_bool())
00246         {
00247             // Convert all addons to gzip
00248             config::const_child_itors camps = campaigns().child_range("campaign");
00249             LOG_CS << "Encoding all stored addons. Number of addons: "
00250                 << std::distance(camps.first, camps.second) << '\n';
00251 
00252             foreach (const config &cm, camps)
00253             {
00254                 LOG_CS << "Encoding " << cm["name"] << '\n';
00255                 std::string filename = cm["filename"], newfilename = filename + ".new";
00256 
00257                 {
00258                     scoped_istream in_file = istream_file(filename);
00259                     boost::iostreams::filtering_stream<boost::iostreams::input> in_filter;
00260                     in_filter.push(boost::iostreams::gzip_decompressor());
00261                     in_filter.push(*in_file);
00262 
00263                     scoped_ostream out_file = ostream_file(newfilename);
00264                     boost::iostreams::filtering_stream<boost::iostreams::output> out_filter;
00265                     out_filter.push(boost::iostreams::gzip_compressor(boost::iostreams::gzip_params(compress_level_)));
00266                     out_filter.push(*out_file);
00267 
00268                     unsigned char c = in_filter.get();
00269                     while( in_filter.good())
00270                     {
00271                         if (needs_escaping(c) && c != '\x01')
00272                         {
00273                             out_filter.put('\x01');
00274                             out_filter.put(c+1);
00275                         } else {
00276                             out_filter.put(c);
00277                         }
00278                         c = in_filter.get();
00279                     }
00280                 }
00281 
00282                 std::remove(filename.c_str());
00283                 std::rename(newfilename.c_str(), filename.c_str());
00284             }
00285 
00286             cfg_["encoded"] = true;
00287         }
00288     }
00289 
00290     void campaign_server::run()
00291     {
00292         convert_binary_to_gzip();
00293 
00294         if (!cfg_["control_socket"].empty())
00295             input_ = new input_stream(cfg_["control_socket"]);
00296         network::connection sock = 0;
00297         for(int increment = 0; ; ++increment) {
00298             try {
00299                 std::string admin_cmd;
00300                 if (input_ && input_->read_line(admin_cmd))
00301                 {
00302                     // process command
00303                     if (admin_cmd == "shut_down")
00304                     {
00305                         break;
00306                     }
00307                 }
00308             //write config to disk every ten minutes
00309                 if((increment%(60*10*50)) == 0) {
00310                     scoped_ostream cfgfile = ostream_file(file_);
00311                     write(*cfgfile, cfg_);
00312                 }
00313 
00314                 network::process_send_queue();
00315 
00316                 sock = network::accept_connection();
00317                 if(sock) {
00318                     LOG_CS << "received connection from " << network::ip_address(sock) << "\n";
00319                 }
00320 
00321                 config data;
00322                 while((sock = network::receive_data(data, 0)) != network::null_connection) {
00323                     if (const config &req = data.child("request_campaign_list"))
00324                     {
00325                         LOG_CS << "sending campaign list to " << network::ip_address(sock) << " using gzip";
00326                         time_t epoch = time(NULL);
00327                         config campaign_list;
00328                         campaign_list["timestamp"] = lexical_cast<std::string>(epoch);
00329                         if (req["times_relative_to"] != "now") {
00330                             epoch = 0;
00331                         }
00332 
00333                         bool before_flag = false;
00334                         time_t before = epoch;
00335                         try {
00336                             before = before + lexical_cast<time_t>(req["before"]);
00337                             before_flag = true;
00338                         } catch(bad_lexical_cast) {}
00339 
00340                         bool after_flag = false;
00341                         time_t after = epoch;
00342                         try {
00343                             after = after + lexical_cast<time_t>(req["after"]);
00344                             after_flag = true;
00345                         } catch(bad_lexical_cast) {}
00346 
00347                         std::string name = req["name"], lang = req["language"];
00348                         foreach (const config &i, campaigns().child_range("campaign"))
00349                         {
00350                             if (!name.empty() && name != i["name"]) continue;
00351                             std::string tm = i["timestamp"];
00352                             if (before_flag && (tm.empty() || lexical_cast_default<time_t>(tm, 0) >= before)) continue;
00353                             if (after_flag && (tm.empty() || lexical_cast_default<time_t>(tm, 0) <= after)) continue;
00354                             if (!lang.empty()) {
00355                                 bool found = false;
00356                                 foreach (const config &j, i.child_range("translation")) {
00357                                     if (j["language"] == lang) {
00358                                         found = true;
00359                                         break;
00360                                     }
00361                                 }
00362                                 if (!found) continue;
00363                             }
00364                             campaign_list.add_child("campaign", i);
00365                         }
00366 
00367                         foreach (config &j, campaign_list.child_range("campaign")) {
00368                             j["passphrase"] = t_string();
00369                             j["upload_ip"] = t_string();
00370                             j["email"] = t_string();
00371                         }
00372 
00373                         config response;
00374                         response.add_child("campaigns",campaign_list);
00375                         std::cerr << " size: " << (network::send_data(response, sock)/1024) << "KiB\n";
00376                     }
00377                     else if (const config &req = data.child("request_campaign"))
00378                     {
00379                         LOG_CS << "sending campaign '" << req["name"] << "' to " << network::ip_address(sock)  << " using gzip";
00380                         config &campaign = campaigns().find_child("campaign", "name", req["name"]);
00381                         if (!campaign) {
00382                             network::send_data(construct_error("Add-on '" + req["name"].str() + "'not found."), sock);
00383                         } else {
00384                             std::cerr << " size: " << (file_size(campaign["filename"])/1024) << "KiB\n";
00385                             network::send_file(campaign["filename"], sock);
00386                             int downloads = campaign["downloads"].to_int() + 1;
00387                             campaign["downloads"] = downloads;
00388                         }
00389 
00390                     }
00391                     else if (data.child("request_terms"))
00392                     {
00393                         LOG_CS << "sending terms " << network::ip_address(sock) << "\n";
00394                         network::send_data(construct_message("All add-ons uploaded to this server must be licensed under the terms of the GNU General Public License (GPL). By uploading content to this server, you certify that you have the right to place the content under the conditions of the GPL, and choose to do so."), sock);
00395                         LOG_CS << " Done\n";
00396                     }
00397                     else if (config &upload = data.child("upload"))
00398                     {
00399                         LOG_CS << "uploading campaign '" << upload["name"] << "' from " << network::ip_address(sock) << ".\n";
00400                         config &data = upload.child("data");
00401                         const std::string& name = upload["name"];
00402                         std::string lc_name(name.size(), ' ');
00403                         std::transform(name.begin(), name.end(), lc_name.begin(), tolower);
00404                         config *campaign = NULL;
00405                         foreach (config &c, campaigns().child_range("campaign")) {
00406                             if (utils::lowercase(c["name"]) == lc_name) {
00407                                 campaign = &c;
00408                                 break;
00409                             }
00410                         }
00411                         if (!data) {
00412                             LOG_CS << "Upload aborted - no add-on data.\n";
00413                             network::send_data(construct_error("Add-on rejected: No add-on data was supplied."), sock);
00414                         } else if (!addon_name_legal(upload["name"])) {
00415                             LOG_CS << "Upload aborted - invalid add-on name.\n";
00416                             network::send_data(construct_error("Add-on rejected: The name of the add-on is invalid."), sock);
00417                         } else if (is_text_markup_char(upload["name"].str()[0])) {
00418                             LOG_CS << "Upload aborted - add-on name starts with an illegal formatting character.\n";
00419                             network::send_data(construct_error("Add-on rejected: The name of the add-on starts with an illegal formatting character."), sock);
00420                         } else if (upload["title"].empty()) {
00421                             LOG_CS << "Upload aborted - no add-on title specified.\n";
00422                             network::send_data(construct_error("Add-on rejected: You did not specify the title of the add-on in the pbl file!"), sock);
00423                         } else if (is_text_markup_char(upload["title"].str()[0])) {
00424                             LOG_CS << "Upload aborted - add-on title starts with an illegal formatting character.\n";
00425                             network::send_data(construct_error("Add-on rejected: The title of the add-on starts with an illegal formatting character."), sock);
00426                         } else if (get_addon_type(upload["type"]) == ADDON_UNKNOWN) {
00427                             LOG_CS << "Upload aborted - unknown add-on type specified.\n";
00428                             network::send_data(construct_error("Add-on rejected: You did not specify a known type for the add-on in the pbl file! (See PblWML: wiki.wesnoth.org/PblWML)"), sock);
00429                         } else if (upload["author"].empty()) {
00430                             LOG_CS << "Upload aborted - no add-on author specified.\n";
00431                             network::send_data(construct_error("Add-on rejected: You did not specify the author(s) of the add-on in the pbl file!"), sock);
00432                         } else if (upload["version"].empty()) {
00433                             LOG_CS << "Upload aborted - no add-on version specified.\n";
00434                             network::send_data(construct_error("Add-on rejected: You did not specify the version of the add-on in the pbl file!"), sock);
00435                         } else if (upload["description"].empty()) {
00436                             LOG_CS << "Upload aborted - no add-on description specified.\n";
00437                             network::send_data(construct_error("Add-on rejected: You did not specify a description of the add-on in the pbl file!"), sock);
00438                         } else if (upload["email"].empty()) {
00439                             LOG_CS << "Upload aborted - no add-on email specified.\n";
00440                             network::send_data(construct_error("Add-on rejected: You did not specify your email address in the pbl file!"), sock);
00441                         } else if (!check_names_legal(data)) {
00442                             LOG_CS << "Upload aborted - invalid file names in add-on data.\n";
00443                             network::send_data(construct_error("Add-on rejected: The add-on contains an illegal file or directory name."
00444                                     " File or directory names may not contain any of the following characters: '/ \\ : ~'"), sock);
00445                         } else if (campaign && (*campaign)["passphrase"].str() != upload["passphrase"]) {
00446                             LOG_CS << "Upload aborted - incorrect passphrase.\n";
00447                             network::send_data(construct_error("Add-on rejected: The add-on already exists, and your passphrase was incorrect."), sock);
00448                         } else {
00449                             LOG_CS << "Upload is owner upload.\n";
00450                             std::string message = "Add-on accepted.";
00451 
00452                             if (!version_info(upload["version"]).good()) {
00453                                 message += "\n<255,255,0>Note: The version you specified is invalid. This add-on will be ignored for automatic update checks.";
00454                             }
00455 
00456                             if(campaign == NULL) {
00457                                 campaign = &campaigns().add_child("campaign");
00458                             }
00459 
00460                             (*campaign)["title"] = upload["title"];
00461                             (*campaign)["name"] = upload["name"];
00462                             (*campaign)["filename"] = "data/" + upload["name"].str();
00463                             (*campaign)["passphrase"] = upload["passphrase"];
00464                             (*campaign)["author"] = upload["author"];
00465                             (*campaign)["description"] = upload["description"];
00466                             (*campaign)["version"] = upload["version"];
00467                             (*campaign)["icon"] = upload["icon"];
00468                             (*campaign)["translate"] = upload["translate"];
00469                             (*campaign)["dependencies"] = upload["dependencies"];
00470                             (*campaign)["upload_ip"] = network::ip_address(sock);
00471                             (*campaign)["type"] = upload["type"];
00472                             (*campaign)["email"] = upload["email"];
00473 
00474                             if((*campaign)["downloads"].empty()) {
00475                                 (*campaign)["downloads"] = 0;
00476                             }
00477                             (*campaign)["timestamp"] = lexical_cast<std::string>(time(NULL));
00478 
00479                             int uploads = (*campaign)["uploads"].to_int() + 1;
00480                             (*campaign)["uploads"] = uploads;
00481 
00482                             std::string filename = (*campaign)["filename"];
00483                             data["title"] = (*campaign)["title"];
00484                             data["name"] = "";
00485                             data["campaign_name"] = (*campaign)["name"];
00486                             data["author"] = (*campaign)["author"];
00487                             data["description"] = (*campaign)["description"];
00488                             data["version"] = (*campaign)["version"];
00489                             data["timestamp"] = (*campaign)["timestamp"];
00490                             data["icon"] = (*campaign)["icon"];
00491                             data["type"] = (*campaign)["type"];
00492                             (*campaign).clear_children("translation");
00493                             find_translations(data, *campaign);
00494 
00495                             add_license(data);
00496 
00497                             {
00498                                 scoped_ostream campaign_file = ostream_file(filename);
00499                                 config_writer writer(*campaign_file, true, compress_level_);
00500                                 writer.write(data);
00501                             }
00502 //                          write_compressed(*campaign_file, *data);
00503 
00504                             (*campaign)["size"] = lexical_cast<std::string>(
00505                                     file_size(filename));
00506                             scoped_ostream cfgfile = ostream_file(file_);
00507                             write(*cfgfile, cfg_);
00508                             network::send_data(construct_message(message), sock);
00509 
00510                             fire("hook_post_upload", upload["name"]);
00511                         }
00512                     }
00513                     else if (const config &erase = data.child("delete"))
00514                     {
00515                         LOG_CS << "deleting campaign '" << erase["name"] << "' requested from " << network::ip_address(sock) << "\n";
00516                         const config &campaign = campaigns().find_child("campaign", "name", erase["name"]);
00517                         if (!campaign) {
00518                             network::send_data(construct_error("The add-on does not exist."), sock);
00519                             continue;
00520                         }
00521 
00522                         if (campaign["passphrase"] != erase["passphrase"]
00523                                 && (campaigns()["master_password"].empty()
00524                                 || campaigns()["master_password"] != erase["passphrase"]))
00525                         {
00526                             network::send_data(construct_error("The passphrase is incorrect."), sock);
00527                             continue;
00528                         }
00529 
00530                         //erase the campaign
00531                         write_file(campaign["filename"], std::string());
00532                         remove(campaign["filename"].str().c_str());
00533 
00534                         config::child_itors itors = campaigns().child_range("campaign");
00535                         for (size_t index = 0; itors.first != itors.second;
00536                              ++index, ++itors.first)
00537                         {
00538                             if (&campaign == &*itors.first) {
00539                                 campaigns().remove_child("campaign", index);
00540                                 break;
00541                             }
00542                         }
00543                         scoped_ostream cfgfile = ostream_file(file_);
00544                         write(*cfgfile, cfg_);
00545                         network::send_data(construct_message("Add-on deleted."), sock);
00546 
00547                         fire("hook_post_erase", erase["name"]);
00548                     }
00549                     else if (const config &cpass = data.child("change_passphrase"))
00550                     {
00551                         config &campaign = campaigns().find_child("campaign", "name", cpass["name"]);
00552                         if (!campaign) {
00553                             network::send_data(construct_error("No add-on with that name exists."), sock);
00554                         } else if (campaign["passphrase"] != cpass["passphrase"]) {
00555                             network::send_data(construct_error("Your old passphrase was incorrect."), sock);
00556                         } else if (cpass["new_passphrase"].empty()) {
00557                             network::send_data(construct_error("No new passphrase was supplied."), sock);
00558                         } else {
00559                             campaign["passphrase"] = cpass["new_passphrase"];
00560                             scoped_ostream cfgfile = ostream_file(file_);
00561                             write(*cfgfile, cfg_);
00562                             network::send_data(construct_message("Passphrase changed."), sock);
00563                         }
00564                     }
00565                 }
00566             } catch(network::error& e) {
00567                 if(!e.socket) {
00568                     LOG_CS << "fatal network error: " << e.message << "\n";
00569                     throw;
00570                 } else {
00571                     LOG_CS << "client disconnect: " << e.message << " " << network::ip_address(e.socket) << "\n";
00572                     e.disconnect();
00573                 }
00574             } catch(config::error& /*e*/) {
00575                 LOG_CS << "error in receiving data...\n";
00576                 network::disconnect(sock);
00577             }
00578 
00579             SDL_Delay(20);
00580         }
00581     }
00582 
00583 }
00584 
00585 int main(int argc, char**argv)
00586 {
00587     game_config::path = get_cwd();
00588     lg::timestamps(true);
00589     try {
00590         printf("argc %d argv[0] %s 1 %s\n",argc,argv[0],argv[1]);
00591         std::string cfg_path = normalize_path("server.cfg");
00592         if(argc >= 2 && atoi(argv[1])){
00593             campaign_server(cfg_path, atoi(argv[1])).run();
00594         }else {
00595             campaign_server(cfg_path).run();
00596         }
00597     } catch(config::error& /*e*/) {
00598         std::cerr << "Could not parse config file\n";
00599         return 1;
00600     } catch(io_exception& /*e*/) {
00601         std::cerr << "File I/O error\n";
00602         return 2;
00603     } catch(network::error& e) {
00604         std::cerr << "Aborted with network error: " << e.message << '\n';
00605         return 3;
00606     }
00607     return 0;
00608 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Wed May 23 2012 01:02:34 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs