addon/client.cpp

Go to the documentation of this file.
00001 /* $Id: client.cpp 54116 2012-05-07 00:15:14Z loonycyborg $ */
00002 /*
00003    Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
00004                  2008 - 2012 by Ignacio Riquelme Morelle <shadowm2006@gmail.com>
00005    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY.
00013 
00014    See the COPYING file for more details.
00015 */
00016 
00017 #include "addon/info.hpp"
00018 #include "addon/manager.hpp"
00019 #include "addon/validation.hpp"
00020 #include "cursor.hpp"
00021 #include "display.hpp"
00022 #include "formula_string_utils.hpp"
00023 #include "gettext.hpp"
00024 #include "gui/dialogs/message.hpp"
00025 #include "log.hpp"
00026 #include "serialization/parser.hpp"
00027 #include "serialization/string_utils.hpp"
00028 
00029 #include "addon/client.hpp"
00030 
00031 static lg::log_domain log_addons_client("addons-client");
00032 #define ERR_ADDONS LOG_STREAM(err ,  log_addons_client)
00033 #define WRN_ADDONS LOG_STREAM(warn,  log_addons_client)
00034 #define LOG_ADDONS LOG_STREAM(info,  log_addons_client)
00035 #define DBG_ADDONS LOG_STREAM(debug, log_addons_client)
00036 
00037 addons_client::addons_client(display& disp, const std::string& address)
00038     : disp_(disp)
00039     , addr_(address)
00040     , host_()
00041     , port_()
00042     , conn_(NULL)
00043     , stat_(NULL)
00044     , last_error_()
00045 {
00046     const std::vector<std::string>& address_components =
00047         utils::split(addr_, ':');
00048 
00049     if(address_components.empty()) {
00050         throw invalid_server_address();
00051     }
00052 
00053     // FIXME: this parsing will break IPv6 numeric addresses! */
00054     host_ = address_components[0];
00055     port_ = address_components.size() == 2 ?
00056         address_components[1] : str_cast(default_campaignd_port);
00057 
00058     LOG_ADDONS << "connecting to server " << host_ << " on port " << port_ << '\n';
00059 
00060     utils::string_map i18n_symbols;
00061     i18n_symbols["server_address"] = addr_;
00062 
00063     conn_ = new network_asio::connection(host_, port_);
00064 
00065     this->wait_for_transfer_done(
00066         vgettext("Connecting to $server_address|...", i18n_symbols));
00067 }
00068 
00069 bool addons_client::request_addons_list(config& cfg)
00070 {
00071     cfg.clear();
00072 
00073     config response_buf;
00074 
00075     /** @todo FIXME: get rid of this legacy "campaign"/"campaigns" silliness */
00076 
00077     this->send_simple_request("request_campaign_list", response_buf);
00078     this->wait_for_transfer_done(_("Downloading list of add-ons..."));
00079 
00080     cfg = response_buf.child("campaigns");
00081 
00082     return !this->update_last_error(response_buf);
00083 }
00084 
00085 bool addons_client::request_distribution_terms(std::string& terms)
00086 {
00087     terms.clear();
00088 
00089     config response_buf;
00090 
00091     this->send_simple_request("request_terms", response_buf);
00092     this->wait_for_transfer_done(_("Requesting distribution terms..."));
00093 
00094     if(const config& msg_cfg = response_buf.child("message")) {
00095         terms = msg_cfg["message"].str();
00096     }
00097 
00098     return !this->update_last_error(response_buf);
00099 }
00100 
00101 bool addons_client::upload_addon(const std::string& id, std::string& response_message)
00102 {
00103     LOG_ADDONS << "preparing to upload " << id << '\n';
00104 
00105     response_message.clear();
00106 
00107     config cfg;
00108     get_addon_pbl_info(id, cfg);
00109 
00110     utils::string_map i18n_symbols;
00111     i18n_symbols["addon_title"] = cfg["title"];
00112     if(i18n_symbols["addon_title"].empty()) {
00113         i18n_symbols["addon_title"] = make_addon_title(id);
00114     }
00115 
00116     std::string passphrase = cfg["passphrase"];
00117     // generate a random passphrase and write it to disk
00118     // if the .pbl file doesn't provide one already
00119     if(passphrase.empty()) {
00120         passphrase.resize(8);
00121         for(size_t n = 0; n != 8; ++n) {
00122             passphrase[n] = 'a' + (rand()%26);
00123         }
00124         cfg["passphrase"] = passphrase;
00125         set_addon_pbl_info(id, cfg);
00126 
00127         LOG_ADDONS << "automatically generated an initial passphrase for " << id << '\n';
00128     }
00129 
00130     cfg["name"] = id;
00131 
00132     config addon_data;
00133     archive_addon(id, addon_data);
00134 
00135     config request_buf, response_buf;
00136     request_buf.add_child("upload", cfg).add_child("data", addon_data);
00137 
00138     LOG_ADDONS << "sending " << id << '\n';
00139 
00140     this->send_request(request_buf, response_buf);
00141     this->wait_for_transfer_done(vgettext("Sending add-on <i>$addon_title</i>...", i18n_symbols
00142     ), true);
00143 
00144     if(const config& message_cfg = response_buf.child("message")) {
00145         response_message = message_cfg["message"].str();
00146         LOG_ADDONS << "server response: " << response_message << '\n';
00147     }
00148 
00149     return !this->update_last_error(response_buf);
00150 
00151 }
00152 
00153 bool addons_client::delete_remote_addon(const std::string& id, std::string& response_message)
00154 {
00155     response_message.clear();
00156 
00157     config cfg;
00158     get_addon_pbl_info(id, cfg);
00159 
00160     utils::string_map i18n_symbols;
00161     i18n_symbols["addon_title"] = cfg["title"];
00162     if(i18n_symbols["addon_title"].empty()) {
00163         i18n_symbols["addon_title"] = make_addon_title(id);
00164     }
00165 
00166     config request_buf, response_buf;
00167     config& request_body = request_buf.add_child("delete");
00168 
00169     request_body["name"] = id;
00170     request_body["passphrase"] = cfg["passphrase"];
00171 
00172     LOG_ADDONS << "requesting server to delete " << id << '\n';
00173 
00174     this->send_request(request_buf, response_buf);
00175     this->wait_for_transfer_done(vgettext("Removing add-on <i>$addon_title</i> from the server...", i18n_symbols
00176     ));
00177 
00178     if(const config& message_cfg = response_buf.child("message")) {
00179         response_message = message_cfg["message"].str();
00180         LOG_ADDONS << "server response: " << response_message << '\n';
00181     }
00182 
00183     return !this->update_last_error(response_buf);
00184 }
00185 
00186 bool addons_client::download_addon(config& archive_cfg, const std::string& id, const std::string& title)
00187 {
00188     archive_cfg.clear();
00189 
00190     config request_buf;
00191     request_buf.add_child("request_campaign")["name"] = id;
00192 
00193     utils::string_map i18n_symbols;
00194     i18n_symbols["addon_title"] = title;
00195 
00196     LOG_ADDONS << "downloading " << id << '\n';
00197 
00198     this->send_request(request_buf, archive_cfg);
00199     this->wait_for_transfer_done(vgettext("Downloading add-on <i>$addon_title</i>...", i18n_symbols));
00200 
00201     return !this->update_last_error(archive_cfg);
00202 }
00203 
00204 bool addons_client::install_addon(config& archive_cfg, const addon_info& info)
00205 {
00206     const cursor::setter cursor_setter(cursor::WAIT);
00207 
00208     utils::string_map i18n_symbols;
00209     i18n_symbols["addon_title"] = info.title;
00210 
00211     if(!check_names_legal(archive_cfg)) {
00212         gui2::show_error_message(disp_.video(),
00213             vgettext("The add-on <i>$addon_title</i> has an invalid file or directory "
00214                 "name and cannot be installed.", i18n_symbols));
00215         return false;
00216     }
00217 
00218     // Add local version information before unpacking
00219 
00220     config* maindir = &archive_cfg.find_child("dir", "name", info.id);
00221     if(!*maindir) {
00222         LOG_ADDONS << "downloaded add-on '" << info.id << "' is missing its directory in the archive; creating it\n";
00223         maindir = &archive_cfg.add_child("dir");
00224         (*maindir)["name"] = info.id;
00225     }
00226 
00227     LOG_ADDONS << "generating version info for add-on '" << info.id << "'\n";
00228 
00229     std::ostringstream info_contents;
00230     config wml;
00231 
00232     info_contents <<
00233         "#\n"
00234         "# File automatically generated by Wesnoth to keep track\n"
00235         "# of version information on installed add-ons. DO NOT EDIT!\n"
00236         "#\n";
00237 
00238     info.write_minimal(wml.add_child("info"));
00239     write(info_contents, wml);
00240 
00241     config file;
00242     file["name"] = "_info.cfg";
00243     file["contents"] = info_contents.str();
00244 
00245     maindir->add_child("file", file);
00246 
00247     LOG_ADDONS << "unpacking " << info.id << '\n';
00248 
00249     // Remove any previously installed versions
00250     if(!remove_local_addon(info.id)) {
00251         WRN_ADDONS << "failed to uninstall previous version of " << info.id << "; the add-on may not work properly!\n";
00252     }
00253 
00254     unarchive_addon(archive_cfg);
00255     LOG_ADDONS << "unpacking finished\n";
00256 
00257     return true;
00258 }
00259 
00260 bool addons_client::update_last_error(config& response_cfg)
00261 {
00262     if(config const &error = response_cfg.child("error")) {
00263         this->last_error_ = error["message"].str();
00264         ERR_ADDONS << "server error: " << error << '\n';
00265         return true;
00266     } else {
00267         this->last_error_.clear();
00268         return false;
00269     }
00270 }
00271 
00272 void addons_client::check_connected() const
00273 {
00274     assert(conn_ != NULL);
00275     if(conn_ == NULL) {
00276         ERR_ADDONS << "not connected to server\n";
00277         throw not_connected_to_server();
00278     }
00279 }
00280 
00281 void addons_client::send_request(const config& request, config& response)
00282 {
00283     check_connected();
00284 
00285     response.clear();
00286     this->conn_->transfer(request, response);
00287 }
00288 
00289 void addons_client::send_simple_request(const std::string& request_string, config& response)
00290 {
00291     config request;
00292     request.add_child(request_string);
00293     this->send_request(request, response);
00294 }
00295 
00296 void addons_client::wait_for_transfer_done(const std::string& status_message, bool track_upload)
00297 {
00298     check_connected();
00299 
00300     if(!stat_) {
00301         stat_ = new gui2::tnetwork_transmission(*conn_, _("Add-ons Manager"), status_message);
00302     } else {
00303         stat_->set_subtitle(status_message);
00304         stat_->set_track_upload(track_upload);
00305     }
00306 
00307     if(!stat_->show(disp_.video())) {
00308         // Notify the caller chain that the user aborted the operation.
00309         throw user_exit();
00310     }
00311 }
00312 
00313 addons_client::~addons_client()
00314 {
00315     delete stat_; // stat_ depends on conn_, so it must be destroyed first!
00316     delete conn_;
00317 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Tue May 22 2012 01:03:34 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs