campaign_server.cpp

Go to the documentation of this file.
00001 /* $Id: campaign_server.cpp 26795 2008-05-23 17:31:24Z suokko $ */
00002 /*
00003    Copyright (C) 2003 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
00013    */
00014 
00015 #include "config.hpp"
00016 #include "filesystem.hpp"
00017 #include "log.hpp"
00018 #include "network.hpp"
00019 #include "publish_campaign.hpp"
00020 #include "util.hpp"
00021 #include "serialization/binary_wml.hpp"
00022 #include "serialization/parser.hpp"
00023 #include "game_config.hpp"
00024 
00025 #include "SDL.h"
00026 
00027 #include <iostream>
00028 #include <map>
00029 #include <algorithm>    // Required for gcc 4.3.0
00030 
00031 // the fork execute is unix specific only tested on Linux quite sure it won't
00032 // work on Windows not sure which other platforms have a problem with it.
00033 #if !(defined(_WIN32))
00034 #include <errno.h>
00035 #include <stdio.h>
00036 #include <unistd.h>
00037 #include <sys/wait.h>
00038 #include <sys/types.h>
00039 #endif
00040 
00041 #define LOG_CS lg::err(lg::network, false)
00042 
00043 namespace {
00044 
00045     config construct_message(const std::string& msg)
00046     {
00047         config cfg;
00048         cfg.add_child("message")["message"] = msg;
00049         return cfg;
00050     }
00051 
00052     config construct_error(const std::string& msg)
00053     {
00054         config cfg;
00055         cfg.add_child("error")["message"] = msg;
00056         LOG_CS << "ERROR: "<<msg<<"\n";
00057         return cfg;
00058     }
00059 
00060     class campaign_server
00061     {
00062         public:
00063             explicit campaign_server(const std::string& cfgfile,size_t min_thread = 10,size_t max_thread = 0);
00064             void run();
00065         private:
00066             /**
00067              * Fires a script, if no script defined it will always return true
00068              * If a script is defined but can't be executed it will return false
00069              */
00070             void fire(const std::string& hook, const std::string& addon);
00071             int load_config(); // return the server port
00072             const config& campaigns() const { return *cfg_.child("campaigns"); }
00073             config& campaigns() { return *cfg_.child("campaigns"); }
00074             config cfg_;
00075             const std::string file_;
00076             const network::manager net_manager_;
00077             const network::server_manager server_manager_;
00078             std::map<std::string, std::string> hooks_;
00079 
00080     };
00081 
00082     void campaign_server::fire(const std::string& hook, const std::string& addon)
00083     {
00084         const std::map<std::string, std::string>::const_iterator itor = hooks_.find(hook);
00085         if(itor == hooks_.end()) return;
00086 
00087         const std::string& script = itor->second;
00088         if(script == "") return;
00089 
00090 #if (defined(_WIN32))
00091         LOG_CS << "ERROR: Tried to execute a script on a not supporting platform\n";
00092         return;
00093 #else
00094         pid_t childpid;
00095 
00096         if((childpid = fork()) == -1) {
00097             LOG_CS << "ERROR fork failed while updating campaign " << addon << "\n";
00098             return;
00099         }
00100 
00101         if(childpid == 0) {
00102             /*** We're the child process ***/
00103 
00104             // execute the script, we run is a separate thread and share the 
00105             // output which will make the logging look ugly.
00106             execlp(script.c_str(), script.c_str(), addon.c_str(), (char *)NULL);
00107 
00108             // exec() and family never return if they do we have a problem
00109             std::cerr << "ERROR exec failed for addon " << addon 
00110                 << " with errno = " << errno << "\n";
00111             exit(errno);
00112 
00113         } else {
00114             return;
00115         }
00116 
00117 #endif
00118     }
00119 
00120     int campaign_server::load_config()
00121     {
00122         scoped_istream stream = istream_file(file_);
00123         read(cfg_, *stream);
00124         return lexical_cast_default<int>(cfg_["port"], 15003);
00125     }
00126 
00127     campaign_server::campaign_server(const std::string& cfgfile,size_t min_thread,size_t max_thread)
00128         : file_(cfgfile), net_manager_(min_thread,max_thread), server_manager_(load_config())
00129     {
00130         if(cfg_.child("campaigns") == NULL) {
00131             cfg_.add_child("campaigns");
00132         }
00133 
00134         // load the hooks
00135         hooks_.insert(std::make_pair(std::string("hook_post_upload"), cfg_["hook_post_upload"]));
00136         hooks_.insert(std::make_pair(std::string("hook_post_erase"), cfg_["hook_post_erase"]));
00137     }
00138 
00139     void find_translations(const config& cfg, config& campaign)
00140     {
00141         const config::child_list& dirs = cfg.get_children("dir");
00142         for(config::child_list::const_iterator i = dirs.begin(); i != dirs.end(); ++i) {
00143             if((const t_string)(**i)["name"] == "LC_MESSAGES") {
00144                 config* language = &campaign.add_child("translation");
00145                 (*language)["language"] = cfg["name"];
00146             }
00147             else {
00148                 find_translations(**i, campaign);
00149             }
00150         }
00151     }
00152 
00153     // Given an uploaded campaign, rename all .py files to .py.unchecked, unless
00154     // the contents are the same as a .py file in an existing campaign.
00155     // This means, a .py.unchecked file can be approved by simply renaming it
00156     // on the CS, and it will remain approved as long as it is not changed (but
00157     // it can be moved/renamed). If the .py file changes, it needs to be approved
00158     // again.
00159     std::string check_python_scripts(config &data, std::string filename)
00160     {
00161         std::vector<config *> python_scripts = find_scripts(data, ".py");
00162         if (!python_scripts.empty()) {
00163             // Campaign contains python scripts.
00164             config old_campaign;
00165             scoped_istream stream = istream_file(filename);
00166             read_compressed(old_campaign, *stream);
00167             std::vector<config *> old_scripts = find_scripts(old_campaign, ".py");
00168             std::string script_names = "";
00169             std::vector<config *>::iterator i, j;
00170             // Go through all newly uploaded python scripts.
00171             for (i = python_scripts.begin(); i != python_scripts.end(); ++i) {
00172                 bool already = false;
00173                 // Compare to existing, approved scripts.
00174                 for (j = old_scripts.begin(); j != old_scripts.end(); ++j) {
00175                     if ((**i)["contents"] != (**j)["contents"]) continue;
00176                     already = true;
00177                     break;
00178                 }
00179                 if (!already) {
00180                     script_names += "\n" + (**i)["name"];
00181                     (**i)["name"] += ".unchecked";
00182                 }
00183             }
00184             if (script_names != "")
00185                 return "\nScripts awaiting approval:\n" + script_names;
00186         }
00187         return "";
00188     }
00189 
00190     // Go through all .py.unchecked files in the given campaign, and rename them to
00191     // .py. This is the opposite to check_python_scripts(), and while the latter is
00192     // done on campaign upload, this function is called for the validate_scripts
00193     // command.
00194     std::string validate_all_python_scripts(config &data)
00195     {
00196         std::vector<config *> python_scripts = find_scripts(data, ".py.unchecked");
00197         if (!python_scripts.empty()) {
00198             // Campaign contains unchecked python scripts.
00199             std::string script_names = "";
00200             std::vector<config *>::iterator i;
00201             // Go through all unchecked python scripts.
00202             for (i = python_scripts.begin(); i != python_scripts.end(); ++i) {
00203                 std::string name = (**i)["name"];
00204                 name.resize(name.length() - 10);
00205                 (**i)["name"] = name;
00206                 script_names += "\n" + name;
00207             }
00208             return script_names;
00209         }
00210         return "";
00211     }
00212 
00213     // Add a file COPYING.txt with the GPL to an uploaded campaign.
00214     void add_license(config &data)
00215     {
00216         config *dir = data.find_child("dir", "name", data["campaign_name"]);
00217         // No top-level directory? Hm..
00218         if (!dir) return;
00219 
00220         // Don't add if it already exists.
00221         if (dir->find_child("file", "name", "COPYING.txt")) return;
00222         if (dir->find_child("file", "name", "COPYING")) return;
00223         if (dir->find_child("file", "name", "copying.txt")) return;
00224         if (dir->find_child("file", "name", "Copying.txt")) return;
00225         if (dir->find_child("file", "name", "COPYING.TXT")) return;
00226 
00227         // Copy over COPYING.txt
00228         std::string contents = read_file("data/COPYING.txt");
00229         config &copying = dir->add_child("file");
00230         copying["name"] = "COPYING.txt";
00231         copying["contents"] = contents;
00232 
00233         if (contents.empty()) {
00234             std::cerr << "Could not find " << "data/COPYING.txt" <<
00235                 ", path is \"" << game_config::path << "\"\n";
00236         }
00237     }
00238 
00239     void campaign_server::run()
00240     {
00241         for(int increment = 0; ; ++increment) {
00242             try {
00243                 //write config to disk every ten minutes
00244                 if((increment%(60*10*2)) == 0) {
00245                     scoped_ostream cfgfile = ostream_file(file_);
00246                     write(*cfgfile, cfg_);
00247                 }
00248 
00249                 network::process_send_queue();
00250 
00251                 network::connection sock = network::accept_connection();
00252                 if(sock) {
00253                     LOG_CS << "received connection from " << network::ip_address(sock) << "\n";
00254                 }
00255 
00256                 config data;
00257                 while((sock = network::receive_data(data)) != network::null_connection) {
00258                     if(const config* req = data.child("request_campaign_list")) {
00259                         LOG_CS << "sending campaign list to " << network::ip_address(sock) << "\n";
00260                         time_t epoch = time(NULL);
00261                         config campaign_list;
00262                         (campaign_list)["timestamp"] = lexical_cast<std::string>(epoch);
00263                         if((const t_string)(*req)["times_relative_to"] != "now") {
00264                             epoch = 0;
00265                         }
00266                         int before_flag = 0;
00267                         time_t before = epoch;
00268                         if((const t_string)(*req)["before"] != "") {
00269                             try {
00270                                 before = before + lexical_cast<time_t>((*req)["before"]);
00271                                 before_flag = 1;
00272                             }
00273                             catch(bad_lexical_cast) {
00274                             }
00275                         }
00276                         int after_flag = 0;
00277                         time_t after = epoch;
00278                         if((const t_string)(*req)["after"] != "") {
00279                             try {
00280                                 after = after + lexical_cast<time_t>((*req)["after"]);
00281                                 after_flag = 1;
00282                             }
00283                             catch(bad_lexical_cast) {
00284                             }
00285                         }
00286                         config::child_list cmps = campaigns().get_children("campaign");
00287                         for(config::child_list::iterator i = cmps.begin(); i != cmps.end(); ++i) {
00288                             if((const t_string)(*req)["name"] != "" && (*req)["name"] != (**i)["name"]) continue;
00289                             if(before_flag && ((const t_string)(**i)["timestamp"] == "" || lexical_cast_default<time_t>((**i)["timestamp"],0) >= before)) continue;
00290                             if(after_flag && ((const t_string)(**i)["timestamp"] == "" || lexical_cast_default<time_t>((**i)["timestamp"],0) <= after)) continue;
00291                             int found = 1;
00292                             if((const t_string)(*req)["language"] != "") {
00293                                 found = 0;
00294                                 config::child_list translation = (**i).get_children("translation");
00295                                 for(config::child_list::iterator j = translation.begin(); j != translation.end(); ++j) {
00296                                     if((*req)["language"] == (**j)["language"]) {
00297                                         found = 1;
00298                                         break;
00299                                     }
00300                                 }
00301                             }
00302                             if(found == 0) continue;
00303                             campaign_list.add_child("campaign", (**i));
00304                         }
00305                         cmps = campaign_list.get_children("campaign");
00306                         for(config::child_list::iterator j = cmps.begin(); j != cmps.end(); ++j) {
00307                             (**j)["passphrase"] = "";
00308                             (**j)["upload_ip"] = "";
00309                         }
00310 
00311                         config response;
00312                         response.add_child("campaigns",campaign_list);
00313                         //! @todo, maybe we should let the server send gzipped data.
00314                         network::send_data(response, sock, true);
00315                     } else if(const config* req = data.child("request_campaign")) {
00316                         LOG_CS << "sending campaign '" << (*req)["name"] << "' to " << network::ip_address(sock) << "\n";
00317                         config* const campaign = campaigns().find_child("campaign","name",(*req)["name"]);
00318                         if(campaign == NULL) {
00319                             //! @todo, maybe we should let the server send gzipped data.
00320                             network::send_data(construct_error("Add-on '" + (*req)["name"] + "'not found."), sock, true);
00321                         } else {
00322                             config cfg;
00323                             scoped_istream stream = istream_file((*campaign)["filename"]);
00324                             read_compressed(cfg, *stream);
00325                             add_license(cfg);
00326                             //! @todo, maybe we should let the server send gzipped data.
00327                             network::send_data(cfg, sock, true);
00328 
00329                             const int downloads = lexical_cast_default<int>((*campaign)["downloads"],0)+1;
00330                             (*campaign)["downloads"] = lexical_cast<std::string>(downloads);
00331                         }
00332 
00333                     } else if(data.child("request_terms") != NULL) {
00334                         LOG_CS << "sending terms " << network::ip_address(sock) << "\n";
00335                         //! @todo, maybe we should let the server send gzipped data.
00336                         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, true);
00337                         LOG_CS << " Done\n";
00338                     } else if(config* upload = data.child("upload")) {
00339                         LOG_CS << "uploading campaign '" << (*upload)["name"] << "' from " << network::ip_address(sock) << ".\n";
00340                         config* data = upload->child("data");
00341                         config* campaign = campaigns().find_child("campaign","name",(*upload)["name"]);
00342                         if(data == NULL) {
00343                             //! @todo, maybe we should let the server send gzipped data.
00344                             LOG_CS << "Upload aborted no data.\n";
00345                             network::send_data(construct_error("No add-on data was supplied."), sock, true);
00346                         } else if(campaign_name_legal((*upload)["name"]) == false) {
00347                             //! @todo, maybe we should let the server send gzipped data.
00348                             LOG_CS << "Upload aborted invalid name.\n";
00349                             network::send_data(construct_error("The name of the add-on is invalid"), sock, true);
00350                         } else if(check_names_legal(*data) == false) {
00351                             //! @todo, maybe we should let the server send gzipped data.
00352                             LOG_CS << "Upload aborted invalid file name.\n";
00353                             network::send_data(construct_error("The add-on contains an illegal file or directory name."), sock, true);
00354                         } else if(campaign != NULL && (*campaign)["passphrase"] != (*upload)["passphrase"]) {
00355                             // the user password failed, now test for the master password, in master password
00356                             // mode the upload behaves different since it's only intended to update translations.
00357                             // In a later version the translations will be separated from the addon.
00358                             LOG_CS << "Upload is admin upload.\n";
00359                             if(campaigns()["master_password"] != ""
00360                                     && campaigns()["master_password"] == (*upload)["passphrase"]) {
00361 
00362                                 std::string message = "Add-on accepted.";
00363 
00364                                 std::string filename = (*campaign)["filename"];
00365                                 (*data)["title"] = (*campaign)["title"];
00366                                 (*data)["name"] = "";
00367                                 (*data)["campaign_name"] = (*campaign)["name"];
00368                                 (*data)["author"] = (*campaign)["author"];
00369                                 (*data)["description"] = (*campaign)["description"];
00370                                 (*data)["version"] = (*campaign)["version"];
00371                                 (*data)["timestamp"] = (*campaign)["timestamp"];
00372                                 (*data)["icon"] = (*campaign)["icon"];
00373                                 (*data)["translate"] = (*campaign)["translate"];
00374                                 (*campaign).clear_children("translation");
00375                                 find_translations(*data, *campaign);
00376 
00377                                 scoped_ostream campaign_file = ostream_file(filename);
00378                                 write_compressed(*campaign_file, *data);
00379 
00380                                 (*campaign)["size"] = lexical_cast<std::string>(
00381                                         file_size(filename));
00382                                 scoped_ostream cfgfile = ostream_file(file_);
00383                                 write(*cfgfile, cfg_);
00384                                 //! @todo, maybe we should let the server send gzipped data.
00385                                 network::send_data(construct_message(message), sock, true);
00386 
00387                             } else {
00388                                 //! @todo, maybe we should let the server send gzipped data.
00389                                 LOG_CS << "Upload aborted invalid passphrase.\n";
00390                                 network::send_data(construct_error("The add-on already exists, and your passphrase was incorrect."), sock, true);
00391                             }
00392                         } else {
00393                             LOG_CS << "Upload is owner upload.\n";
00394                             std::string message = "Add-on accepted.";
00395                             if(campaign == NULL) {
00396                                 campaign = &campaigns().add_child("campaign");
00397                             }
00398 
00399                             (*campaign)["title"] = (*upload)["title"];
00400                             (*campaign)["name"] = (*upload)["name"];
00401                             (*campaign)["filename"] = (*upload)["name"];
00402                             (*campaign)["passphrase"] = (*upload)["passphrase"];
00403                             (*campaign)["author"] = (*upload)["author"];
00404                             (*campaign)["description"] = (*upload)["description"];
00405                             (*campaign)["version"] = (*upload)["version"];
00406                             (*campaign)["icon"] = (*upload)["icon"];
00407                             (*campaign)["translate"] = (*upload)["translate"];
00408                             (*campaign)["dependencies"] = (*upload)["dependencies"];
00409                             (*campaign)["upload_ip"] = network::ip_address(sock);
00410 
00411                             if((*campaign)["downloads"].empty()) {
00412                                 (*campaign)["downloads"] = "0";
00413                             }
00414                             (*campaign)["timestamp"] = lexical_cast<std::string>(time(NULL));
00415 
00416                             const int uploads = lexical_cast_default<int>((*campaign)["uploads"],0) + 1;
00417                             (*campaign)["uploads"] = lexical_cast<std::string>(uploads);
00418 
00419                             std::string filename = (*campaign)["filename"];
00420                             (*data)["title"] = (*campaign)["title"];
00421                             (*data)["name"] = "";
00422                             (*data)["campaign_name"] = (*campaign)["name"];
00423                             (*data)["author"] = (*campaign)["author"];
00424                             (*data)["description"] = (*campaign)["description"];
00425                             (*data)["version"] = (*campaign)["version"];
00426                             (*data)["timestamp"] = (*campaign)["timestamp"];
00427                             (*data)["icon"] = (*campaign)["icon"];
00428                             (*campaign).clear_children("translation");
00429                             find_translations(*data, *campaign);
00430 
00431                             // Campaigns which have python="allowed" are not checked
00432                             if ((*campaign)["python"] != "allowed") {
00433                                 message += check_python_scripts(*data, filename);
00434                             }
00435                             scoped_ostream campaign_file = ostream_file(filename);
00436                             write_compressed(*campaign_file, *data);
00437 
00438                             (*campaign)["size"] = lexical_cast<std::string>(
00439                                     file_size(filename));
00440                             scoped_ostream cfgfile = ostream_file(file_);
00441                             write(*cfgfile, cfg_);
00442                             //! @todo, maybe we should let the server send gzipped data.
00443                             network::send_data(construct_message(message), sock, true);
00444 
00445                             fire("hook_post_upload", (*upload)["name"]);
00446                         }
00447                     } else if(const config* erase = data.child("delete")) {
00448                         LOG_CS << "deleting campaign '" << (*erase)["name"] << "' requested from " << network::ip_address(sock) << "\n";
00449                         config* const campaign = campaigns().find_child("campaign","name",(*erase)["name"]);
00450                         if(campaign == NULL) {
00451                             //! @todo, maybe we should let the server send gzipped data.
00452                             network::send_data(construct_error("The add-on does not exist."), sock, true);
00453                             continue;
00454                         }
00455 
00456                         if((*campaign)["passphrase"] != (*erase)["passphrase"] 
00457                                 && (campaigns()["master_password"] == ""
00458                                 || campaigns()["master_password"] != (*erase)["passphrase"])) {
00459 
00460                             //! @todo, maybe we should let the server send gzipped data.
00461                             network::send_data(construct_error("The passphrase is incorrect."), sock, true);
00462                             continue;
00463                         }
00464 
00465                         //erase the campaign
00466                         write_file((*campaign)["filename"],"");
00467                         remove((*campaign)["filename"].c_str());
00468 
00469                         const config::child_list& campaigns_list = campaigns().get_children("campaign");
00470                         const size_t index = std::find(campaigns_list.begin(),campaigns_list.end(),campaign) - campaigns_list.begin();
00471                         campaigns().remove_child("campaign",index);
00472                         scoped_ostream cfgfile = ostream_file(file_);
00473                         write(*cfgfile, cfg_);
00474                         //! @todo, maybe we should let the server send gzipped data.
00475                         network::send_data(construct_message("Add-on deleted."), sock, true);
00476 
00477                         fire("hook_post_erase", (*erase)["name"]);
00478 
00479                     } else if(const config* cpass = data.child("change_passphrase")) {
00480                         config* campaign = campaigns().find_child("campaign","name",(*cpass)["name"]);
00481                         if(campaign == NULL) {
00482                             //! @todo, maybe we should let the server send gzipped data.
00483                             network::send_data(construct_error("No add-on with that name exists."), sock, true);
00484                         } else if((*campaign)["passphrase"] != (*cpass)["passphrase"]) {
00485                             //! @todo, maybe we should let the server send gzipped data.
00486                             network::send_data(construct_error("Your old passphrase was incorrect."), sock, true);
00487                         } else if((const t_string)(*cpass)["new_passphrase"] == "") {
00488                             //! @todo, maybe we should let the server send gzipped data.
00489                             network::send_data(construct_error("No new passphrase was supplied."), sock, true);
00490                         } else {
00491                             (*campaign)["passphrase"] = (*cpass)["new_passphrase"];
00492                             scoped_ostream cfgfile = ostream_file(file_);
00493                             write(*cfgfile, cfg_);
00494                             //! @todo, maybe we should let the server send gzipped data.
00495                             network::send_data(construct_message("Passphrase changed."), sock, true);
00496                         }
00497                     } else if(const config* cvalidate = data.child("validate_scripts")) {
00498                         config* campaign = campaigns().find_child("campaign","name",(*cvalidate)["name"]);
00499                         if(campaign == NULL) {
00500                             //! @todo, maybe we should let the server send gzipped data.
00501                             network::send_data(construct_error(
00502                                         "No add-on with that name exists."), sock, true);
00503                         } else if(campaigns()["master_password"] == "") {
00504                             //! @todo, maybe we should let the server send gzipped data.
00505                             network::send_data(construct_error(
00506                                         "Sever does not allow scripts."), sock, true);
00507                         } else if (campaigns()["master_password"] != (*cvalidate)["master_password"]) {
00508                             //! @todo, maybe we should let the server send gzipped data.
00509                             network::send_data(construct_error(
00510                                         "Password was incorrect."), sock, true);
00511                         } else {
00512                             // Read the campaign from disk.
00513                             config campaign_file;
00514                             scoped_istream stream = istream_file((*campaign)["filename"]);
00515                             read_compressed(campaign_file, *stream);
00516                             std::string scripts = validate_all_python_scripts(campaign_file);
00517                             if (!scripts.empty()) {
00518                                 // Write the campaign with changed filenames back to disk
00519                                 scoped_ostream ostream = ostream_file((*campaign)["filename"]);
00520                                 write_compressed(*ostream, campaign_file);
00521 
00522                                 //! @todo, maybe we should let the server send gzipped data.
00523                                 network::send_data(construct_message("The following scripts have been validated: " +
00524                                             scripts), sock, true);
00525                             } else {
00526                                 //! @todo, maybe we should let the server send gzipped data.
00527                                 network::send_data(construct_message("No unchecked scripts found!"), sock, true);
00528                             }
00529                         }
00530                     }
00531                 }
00532             } catch(network::error& e) {
00533                 if(!e.socket) {
00534                     LOG_CS << "fatal network error\n";
00535                     break;
00536                 } else {
00537                     LOG_CS <<"client disconnect : "<<e.message<<" " << network::ip_address(e.socket) << "\n";
00538                     e.disconnect();
00539                 }
00540             } catch(config::error& /*e*/) {
00541                 LOG_CS << "error in receiving data...\n";
00542             }
00543 
00544             SDL_Delay(20);
00545         }
00546     }
00547 
00548 }
00549 
00550 int main(int argc, char**argv)
00551 {
00552     lg::timestamps(true);
00553     try {
00554         printf("argc %d argv[0] %s 1 %s\n",argc,argv[0],argv[1]);
00555         if(argc >= 2 && atoi(argv[1])){
00556             campaign_server("server.cfg",atoi(argv[1])).run();
00557         }else {
00558             campaign_server("server.cfg").run();
00559         }
00560     } catch(config::error& /*e*/) {
00561         std::cerr << "Could not parse config file\n";
00562         return 1;
00563     } catch(io_exception& /*e*/) {
00564         std::cerr << "File I/O error\n";
00565         return 2;
00566     } catch(network::error& e) {
00567         std::cerr << "Aborted with network error: " << e.message << '\n';
00568         return 3;
00569     }
00570     return 0;
00571 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs