00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
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
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
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
00118
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
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
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
00309 throw user_exit();
00310 }
00311 }
00312
00313 addons_client::~addons_client()
00314 {
00315 delete stat_;
00316 delete conn_;
00317 }