The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
client.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2008 by David White <[email protected]>
3  2008 - 2015 by Ignacio Riquelme Morelle <[email protected]>
4  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "addon/info.hpp"
17 #include "addon/manager.hpp"
18 #include "addon/validation.hpp"
19 #include "cursor.hpp"
20 #include "formula/string_utils.hpp"
21 #include "gettext.hpp"
22 #include "gui/dialogs/message.hpp"
23 #include "log.hpp"
24 #include "serialization/parser.hpp"
26 
27 #include "addon/client.hpp"
28 
29 static lg::log_domain log_addons_client("addons-client");
30 #define ERR_ADDONS LOG_STREAM(err , log_addons_client)
31 #define WRN_ADDONS LOG_STREAM(warn, log_addons_client)
32 #define LOG_ADDONS LOG_STREAM(info, log_addons_client)
33 #define DBG_ADDONS LOG_STREAM(debug, log_addons_client)
34 
36  : v_(v)
37  , addr_(address)
38  , host_()
39  , port_()
40  , conn_(nullptr)
41  , stat_(nullptr)
42  , last_error_()
43 {
44  const std::vector<std::string>& address_components =
45  utils::split(addr_, ':');
46 
47  if(address_components.empty()) {
48  throw invalid_server_address();
49  }
50 
51  // FIXME: this parsing will break IPv6 numeric addresses! */
52  host_ = address_components[0];
53  port_ = address_components.size() == 2 ?
54  address_components[1] : std::to_string(default_campaignd_port);
55 }
56 
58 {
59  LOG_ADDONS << "connecting to server " << host_ << " on port " << port_ << '\n';
60 
61  utils::string_map i18n_symbols;
62  i18n_symbols["server_address"] = addr_;
63 
64  conn_ = new network_asio::connection(host_, port_);
65 
67  vgettext("Connecting to $server_address|...", i18n_symbols));
68 }
69 
71 {
72  cfg.clear();
73 
74  config response_buf;
75 
76  /** @todo FIXME: get rid of this legacy "campaign"/"campaigns" silliness */
77 
78  this->send_simple_request("request_campaign_list", response_buf);
79  this->wait_for_transfer_done(_("Downloading list of add-ons..."));
80 
81  cfg = response_buf.child("campaigns");
82 
83  return !this->update_last_error(response_buf);
84 }
85 
87 {
88  terms.clear();
89 
90  config response_buf;
91 
92  this->send_simple_request("request_terms", response_buf);
93  this->wait_for_transfer_done(_("Requesting distribution terms..."));
94 
95  if(const config& msg_cfg = response_buf.child("message")) {
96  terms = msg_cfg["message"].str();
97  }
98 
99  return !this->update_last_error(response_buf);
100 }
101 
102 bool addons_client::upload_addon(const std::string& id, std::string& response_message, config& cfg)
103 {
104  LOG_ADDONS << "preparing to upload " << id << '\n';
105 
106  response_message.clear();
107 
108  utils::string_map i18n_symbols;
109  i18n_symbols["addon_title"] = cfg["title"];
110  if(i18n_symbols["addon_title"].empty()) {
111  i18n_symbols["addon_title"] = make_addon_title(id);
112  }
113 
114  std::string passphrase = cfg["passphrase"];
115  // generate a random passphrase and write it to disk
116  // if the .pbl file doesn't provide one already
117  if(passphrase.empty()) {
118  passphrase.resize(8);
119  for(size_t n = 0; n != 8; ++n) {
120  passphrase[n] = 'a' + (rand()%26);
121  }
122  cfg["passphrase"] = passphrase;
123  set_addon_pbl_info(id, cfg);
124 
125  LOG_ADDONS << "automatically generated an initial passphrase for " << id << '\n';
126  }
127 
128  cfg["name"] = id;
129 
130  config addon_data;
131  archive_addon(id, addon_data);
132 
133  config request_buf, response_buf;
134  request_buf.add_child("upload", cfg).add_child("data", addon_data);
135 
136  LOG_ADDONS << "sending " << id << '\n';
137 
138  this->send_request(request_buf, response_buf);
139  this->wait_for_transfer_done(vgettext("Sending add-on <i>$addon_title</i>...", i18n_symbols
140  ), true);
141 
142  if(const config& message_cfg = response_buf.child("message")) {
143  response_message = message_cfg["message"].str();
144  LOG_ADDONS << "server response: " << response_message << '\n';
145  }
146 
147  return !this->update_last_error(response_buf);
148 
149 }
150 
152 {
153  response_message.clear();
154 
155  config cfg;
156  get_addon_pbl_info(id, cfg);
157 
158  utils::string_map i18n_symbols;
159  i18n_symbols["addon_title"] = cfg["title"];
160  if(i18n_symbols["addon_title"].empty()) {
161  i18n_symbols["addon_title"] = make_addon_title(id);
162  }
163 
164  config request_buf, response_buf;
165  config& request_body = request_buf.add_child("delete");
166 
167  request_body["name"] = id;
168  request_body["passphrase"] = cfg["passphrase"];
169 
170  LOG_ADDONS << "requesting server to delete " << id << '\n';
171 
172  this->send_request(request_buf, response_buf);
173  this->wait_for_transfer_done(vgettext("Removing add-on <i>$addon_title</i> from the server...", i18n_symbols
174  ));
175 
176  if(const config& message_cfg = response_buf.child("message")) {
177  response_message = message_cfg["message"].str();
178  LOG_ADDONS << "server response: " << response_message << '\n';
179  }
180 
181  return !this->update_last_error(response_buf);
182 }
183 
184 bool addons_client::download_addon(config& archive_cfg, const std::string& id, const std::string& title, bool increase_downloads)
185 {
186  archive_cfg.clear();
187 
188  config request_buf;
189  config& request_body = request_buf.add_child("request_campaign");
190 
191  request_body["name"] = id;
192  request_body["increase_downloads"] = increase_downloads;
193 
194  utils::string_map i18n_symbols;
195  i18n_symbols["addon_title"] = title;
196 
197  LOG_ADDONS << "downloading " << id << '\n';
198 
199  this->send_request(request_buf, archive_cfg);
200  this->wait_for_transfer_done(vgettext("Downloading add-on <i>$addon_title</i>...", i18n_symbols));
201 
202  return !this->update_last_error(archive_cfg);
203 }
204 
206 {
207  const cursor::setter cursor_setter(cursor::WAIT);
208 
209  utils::string_map i18n_symbols;
210  i18n_symbols["addon_title"] = info.title;
211 
212  if(!check_names_legal(archive_cfg)) {
214  vgettext("The add-on <i>$addon_title</i> has an invalid file or directory "
215  "name and cannot be installed.", i18n_symbols));
216  return false;
217  }
218 
219  // Add local version information before unpacking
220 
221  config* maindir = &archive_cfg.find_child("dir", "name", info.id);
222  if(!*maindir) {
223  LOG_ADDONS << "downloaded add-on '" << info.id << "' is missing its directory in the archive; creating it\n";
224  maindir = &archive_cfg.add_child("dir");
225  (*maindir)["name"] = info.id;
226  }
227 
228  LOG_ADDONS << "generating version info for add-on '" << info.id << "'\n";
229 
230  std::ostringstream info_contents;
231  config wml;
232 
233  info_contents <<
234  "#\n"
235  "# File automatically generated by Wesnoth to keep track\n"
236  "# of version information on installed add-ons. DO NOT EDIT!\n"
237  "#\n";
238 
239  info.write_minimal(wml.add_child("info"));
240  write(info_contents, wml);
241 
242  config file;
243  file["name"] = "_info.cfg";
244  file["contents"] = info_contents.str();
245 
246  maindir->add_child("file", file);
247 
248  LOG_ADDONS << "unpacking " << info.id << '\n';
249 
250  // Remove any previously installed versions
251  if(!remove_local_addon(info.id)) {
252  WRN_ADDONS << "failed to uninstall previous version of " << info.id << "; the add-on may not work properly!" << std::endl;
253  }
254 
255  unarchive_addon(archive_cfg);
256  LOG_ADDONS << "unpacking finished\n";
257 
258  return true;
259 }
260 
262 {
263  if(config const &error = response_cfg.child("error")) {
264  this->last_error_ = error["message"].str();
265  ERR_ADDONS << "server error: " << error << '\n';
266  return true;
267  } else {
268  this->last_error_.clear();
269  return false;
270  }
271 }
272 
274 {
275  assert(conn_ != nullptr);
276  if(conn_ == nullptr) {
277  ERR_ADDONS << "not connected to server" << std::endl;
278  throw not_connected_to_server();
279  }
280 }
281 
282 void addons_client::send_request(const config& request, config& response)
283 {
284  check_connected();
285 
286  response.clear();
287  this->conn_->transfer(request, response);
288 }
289 
290 void addons_client::send_simple_request(const std::string& request_string, config& response)
291 {
292  config request;
293  request.add_child(request_string);
294  this->send_request(request, response);
295 }
297 {
299  size_t total() override { return conn_.bytes_to_read(); }
300  virtual size_t current() override { return conn_.bytes_read(); }
301  virtual bool finished() override { return conn_.done(); }
302  virtual void cancel() override { return conn_.cancel(); }
303  virtual void poll() override { conn_.poll(); }
305 };
307 {
309  size_t total() override { return conn_.bytes_to_write(); }
310  virtual size_t current() override { return conn_.bytes_written(); }
311  virtual bool finished() override { return conn_.done(); }
312  virtual void cancel() override { return conn_.cancel(); }
313  virtual void poll() override { conn_.poll(); }
315 };
316 void addons_client::wait_for_transfer_done(const std::string& status_message, bool track_upload)
317 {
318  check_connected();
319  std::unique_ptr<gui2::tnetwork_transmission::connection_data> cd;
320  if(track_upload)
321  cd.reset(new write_addon_connection_data{ *conn_ });
322  else
323  cd.reset(new read_addon_connection_data{ *conn_ });
324  if(!stat_) {
325  stat_ = new gui2::tnetwork_transmission(*cd, _("Add-ons Manager"), status_message);
326  } else {
327  stat_->set_subtitle(status_message);
329  }
330 
331  if(!stat_->show(v_)) {
332  // Notify the caller chain that the user aborted the operation.
333  throw user_exit();
334  }
335 }
336 
338 {
339  delete stat_; // stat_ depends on conn_, so it must be destroyed first!
340  delete conn_;
341 }
write_addon_connection_data(network_asio::connection &conn)
Definition: client.cpp:308
void show_error_message(CVideo &video, const std::string &message, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:198
std::string port_
Definition: client.hpp:140
virtual bool finished() override
Definition: client.cpp:311
Dialog that tracks network transmissions.
bool download_addon(config &archive_cfg, const std::string &id, const std::string &title, bool increase_downloads=true)
Downloads the specified add-on from the server.
Definition: client.cpp:184
std::string host_
Definition: client.hpp:139
virtual void poll() override
Definition: client.cpp:313
bool show(CVideo &video, const unsigned auto_close_time=0)
Shows the window.
Definition: dialog.cpp:34
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
logger & info()
Definition: log.cpp:91
network_asio::connection * conn_
Definition: client.hpp:141
Definition: video.hpp:58
bool update_last_error(config &response_cfg)
Definition: client.cpp:261
size_t total() override
Definition: client.cpp:299
addons_client(CVideo &v, const std::string &address)
Constructor.
Definition: client.cpp:35
#define ERR_ADDONS
Definition: client.cpp:30
virtual bool finished() override
Definition: client.cpp:301
std::string last_error_
Definition: client.hpp:143
bool install_addon(config &archive_cfg, const addon_info &info)
Installs the specified add-on using an archive received from the server.
Definition: client.cpp:205
void clear()
Definition: config.cpp:1055
std::size_t bytes_to_write() const
gui2::tnetwork_transmission * stat_
Definition: client.hpp:142
void write_minimal(config &cfg) const
Write only minimal WML used for state tracking (_info.cfg) files.
Definition: info.cpp:114
void send_request(const config &request, config &response)
Sends a request to the add-ons server.
Definition: client.cpp:282
std::size_t bytes_read() const
static lg::log_domain log_addons_client("addons-client")
#define LOG_ADDONS
Definition: client.cpp:32
virtual size_t current() override
Definition: client.cpp:300
virtual size_t current() override
Definition: client.cpp:310
virtual void poll() override
Definition: client.cpp:303
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
std::map< std::string, t_string > string_map
GLuint id
Definition: glew.h:1647
virtual void cancel() override
Definition: client.cpp:312
void check_connected() const
Makes sure the add-ons server connection is working.
Definition: client.cpp:273
void set_connection_data(connection_data &connection)
const GLdouble * v
Definition: glew.h:1359
std::string id
Definition: info.hpp:31
void set_addon_pbl_info(const std::string &addon_name, const config &cfg)
Definition: manager.cpp:96
config & add_child(const std::string &key)
Definition: config.cpp:743
int port_
Definition: network.cpp:375
A class that represents a TCP/IP connection.
CVideo & v_
Definition: client.hpp:137
network_asio::connection & conn_
Definition: client.cpp:304
std::size_t bytes_to_read() const
bool remove_local_addon(const std::string &addon)
Definition: manager.cpp:120
void connect()
Try to establish a connection to the add-ons server.
Definition: client.cpp:57
read_addon_connection_data(network_asio::connection &conn)
Definition: client.cpp:298
GLuint GLuint64EXT address
Definition: glew.h:11276
void transfer(const config &request, config &response)
std::string addr_
Definition: client.hpp:138
network_asio::connection & conn_
Definition: client.cpp:314
void wait_for_transfer_done(const std::string &status_message, bool track_upload=false)
Waits for a network transfer, displaying a status window.
Definition: client.cpp:316
void archive_addon(const std::string &addon_name, config &cfg)
Archives an add-on into a config object for campaignd transactions.
Definition: manager.cpp:294
bool done() const
True if connected and no high-level operation is in progress.
std::string host_
Definition: network.cpp:374
#define WRN_ADDONS
Definition: client.cpp:31
std::string vgettext(const char *msgid, const utils::string_map &symbols)
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:228
GLclampd n
Definition: glew.h:5903
void get_addon_pbl_info(const std::string &addon_name, config &cfg)
Gets the publish information for an add-on.
Definition: manager.cpp:85
const unsigned short default_campaignd_port
Default port number for the addon server.
Definition: validation.cpp:20
void unarchive_addon(const config &cfg)
Definition: manager.cpp:327
std::string title
Definition: info.hpp:32
config & child(const std::string &key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:658
config & find_child(const std::string &key, const std::string &name, const std::string &value)
Returns the first child of tag key with a name attribute containing value.
Definition: config.cpp:1010
bool upload_addon(const std::string &id, std::string &response_message, config &cfg)
Requests the specified add-on to be uploaded.
Definition: client.cpp:102
Standard logging facilities (interface).
void set_subtitle(const std::string &)
std::size_t poll()
Handle all pending asynchonous events and return.
int connection
Definition: network.hpp:74
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.
bool check_names_legal(const config &dir)
Probes an add-on archive for illegal names.
Definition: validation.cpp:70
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
bool delete_remote_addon(const std::string &id, std::string &response_message)
Requests the specified add-on to be removed from the server.
Definition: client.cpp:151
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
Definition: client.cpp:86
size_t total() override
Definition: client.cpp:309
virtual void cancel() override
Definition: client.cpp:302
void write(std::ostream &out, configr_of const &cfg, unsigned int level)
Definition: parser.cpp:621
std::size_t bytes_written() const
GLsizei const GLcharARB ** string
Definition: glew.h:4503
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
Definition: client.cpp:70
void send_simple_request(const std::string &request_string, config &response)
Sends a simple request message to the add-ons server.
Definition: client.cpp:290