The Battle for Wesnoth  1.15.9+dev
client.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2008 by David White <dave@whitevine.net>
3  Copyright (C) 2008 - 2020 by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://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/state.hpp"
19 #include "addon/validation.hpp"
20 #include "cursor.hpp"
21 #include "font/pango/escape.hpp"
22 #include "formula/string_utils.hpp"
23 #include "gettext.hpp"
25 #include "gui/dialogs/message.hpp"
26 #include "gui/widgets/retval.hpp"
27 #include "log.hpp"
28 #include "random.hpp"
29 #include "serialization/parser.hpp"
33 
34 #include <stdexcept>
35 
36 #include "addon/client.hpp"
37 
38 static lg::log_domain log_addons_client("addons-client");
39 #define ERR_ADDONS LOG_STREAM(err , log_addons_client)
40 #define WRN_ADDONS LOG_STREAM(warn, log_addons_client)
41 #define LOG_ADDONS LOG_STREAM(info, log_addons_client)
42 #define DBG_ADDONS LOG_STREAM(debug, log_addons_client)
43 
45 
46 addons_client::addons_client(const std::string& address)
47  : addr_(address)
48  , host_()
49  , port_()
50  , conn_(nullptr)
51  , last_error_()
52  , last_error_data_()
53  , server_version_()
54  , server_capabilities_()
55  , server_url_()
56  , license_notice_()
57 {
58  try {
59  std::tie(host_, port_) = parse_network_address(addr_, std::to_string(default_campaignd_port));
60  } catch(const std::runtime_error&) {
61  throw invalid_server_address();
62  }
63 }
64 
66 {
67  LOG_ADDONS << "connecting to server " << host_ << " on port " << port_ << '\n';
68 
69  utils::string_map i18n_symbols;
70  i18n_symbols["server_address"] = addr_;
71 
72  conn_.reset(new network_asio::connection(host_, port_));
73 
74  const auto& msg = VGETTEXT("Connecting to $server_address|...", i18n_symbols);
75 
77 
78  config response_buf;
79 
80  send_simple_request("server_id", response_buf);
82 
83  if(!update_last_error(response_buf)) {
84  if(const auto& info = response_buf.child("server_id")) {
85  server_version_ = info["version"].str();
86 
87  for(const auto& cap : utils::split(info["cap"].str())) {
88  server_capabilities_.insert(cap);
89  }
90 
91  server_url_ = info["url"].str();
92  license_notice_ = info["license_notice"].str();
93  }
94  } else {
96  }
97 
98  if(server_version_.empty()) {
99  LOG_ADDONS << "Server version 1.15.7 or earlier\n";
100  // An educated guess
101  server_capabilities_ = { "auth:legacy" };
102  } else {
103  LOG_ADDONS << "Server version " << server_version_ << '\n';
104  }
105 
106  LOG_ADDONS << "Server supports: " << utils::join(server_capabilities_, " ") << '\n';
107 }
108 
110 {
111  cfg.clear();
112 
113  config response_buf;
114 
115  /** @todo FIXME: get rid of this legacy "campaign"/"campaigns" silliness
116  */
117 
118  send_simple_request("request_campaign_list", response_buf);
119  wait_for_transfer_done(_("Downloading list of add-ons..."));
120 
121  std::swap(cfg, response_buf.child("campaigns"));
122 
123  return !update_last_error(response_buf);
124 }
125 
127 {
128  if(!license_notice_.empty()) {
129  // Server identification supported, we already know the terms so this
130  // operation always succeeds without going through the server.
131  terms = license_notice_;
132  return true;
133  }
134 
135  terms.clear();
136 
137  config response_buf;
138 
139  send_simple_request("request_terms", response_buf);
140  wait_for_transfer_done(_("Requesting distribution terms..."));
141 
142  if(const config& msg_cfg = response_buf.child("message")) {
143  terms = msg_cfg["message"].str();
144  }
145 
146  return !update_last_error(response_buf);
147 }
148 
149 bool addons_client::upload_addon(const std::string& id, std::string& response_message, config& cfg, bool local_only)
150 {
151  LOG_ADDONS << "preparing to upload " << id << '\n';
152 
153  response_message.clear();
154 
155  utils::string_map i18n_symbols;
156  i18n_symbols["addon_title"] = font::escape_text(cfg["title"]);
157  if(i18n_symbols["addon_title"].empty()) {
158  i18n_symbols["addon_title"] = font::escape_text(make_addon_title(id));
159  }
160 
161  if(!addon_name_legal(id)){
162  i18n_symbols["addon_id"] = font::escape_text(id);
163  last_error_ =
164  VGETTEXT("The add-on <i>$addon_title</i> has an invalid id '$addon_id' "
165  "and cannot be published.", i18n_symbols);
166  return false;
167  }
168 
169  std::string passphrase = cfg["passphrase"];
170  // generate a random passphrase and write it to disk
171  // if the .pbl file doesn't provide one already
172  if(passphrase.empty()) {
173  passphrase.resize(16);
174  for(std::size_t n = 0; n < passphrase.size(); ++n) {
175  passphrase[n] = randomness::generator->get_random_int('a', 'z');
176  }
177  cfg["passphrase"] = passphrase;
178  set_addon_pbl_info(id, cfg);
179 
180  LOG_ADDONS << "automatically generated an initial passphrase for " << id << '\n';
181  }
182 
183  cfg["name"] = id;
184 
185  config addon_data;
186  try {
187  archive_addon(id, addon_data);
188  } catch(const utf8::invalid_utf8_exception&){
189  last_error_ =
190  VGETTEXT("The add-on <i>$addon_title</i> has a file or directory "
191  "containing invalid characters and cannot be published.", i18n_symbols);
192  return false;
193  }
194 
195  std::vector<std::string> badnames;
196  if(!check_names_legal(addon_data, &badnames)){
197  last_error_ =
198  VGETTEXT("The add-on <i>$addon_title</i> has an invalid file or directory "
199  "name and cannot be published. "
200 
201  "File or directory names may not contain '..' or end with '.' or be longer than 255 characters. "
202  "It also may not contain whitespace, control characters, or any of the following characters:\n\n&quot; * / : &lt; &gt; ? \\ | ~"
203  , i18n_symbols);
205  return false;
206  }
207  if(!check_case_insensitive_duplicates(addon_data, &badnames)){
208  last_error_ =
209  VGETTEXT("The add-on <i>$addon_title</i> contains files or directories with case conflicts. "
210  "File or directory names may not be differently-cased versions of the same string.", i18n_symbols);
212  return false;
213  }
214 
215  if(!local_only) {
216  // Try to make an upload pack if it's avaible on the server
217  config hashlist, hash_request;
218  config& request_body = hash_request.add_child("request_campaign_hash");
219  // We're requesting the latest version of an addon, so we may not specify it
220  // #TODO: Make a selection of the base version for the update ?
221  request_body["name"] = cfg["name"];
222  // request_body["from"] = ???
223  send_request(hash_request, hashlist);
224  wait_for_transfer_done(_("Requesting file index..."));
225 
226  // A silent error check
227  if(!hashlist.child("error")) {
228  if(!contains_hashlist(addon_data, hashlist) || !contains_hashlist(hashlist, addon_data)) {
229  LOG_ADDONS << "making an update pack for the add-on " << id << '\n';
230  config updatepack;
231  // The client shouldn't send the pack if the server is old due to the previous check,
232  // so the server should handle the new format in the `upload` request
233  make_updatepack(updatepack, hashlist, addon_data);
234 
235  config request_buf, response_buf;
236  request_buf.add_child("upload", cfg).append(std::move(updatepack));
237  // #TODO: Make a selection of the base version for the update ? ,
238  // For now, if it's unspecified we'll use the latest avaible before the upload version
239  send_request(request_buf, response_buf);
240  wait_for_transfer_done(VGETTEXT("Sending an update pack for the add-on <i>$addon_title</i>...", i18n_symbols
242 
243  if(const config& message_cfg = response_buf.child("message")) {
244  response_message = message_cfg["message"].str();
245  LOG_ADDONS << "server response: " << response_message << '\n';
246  }
247 
248  if(!update_last_error(response_buf))
249  return true;
250  }
251  }
252  }
253  // If there is an error including an unrecognised request for old servers or no hash data for new uploads we'll just send a full pack
254 
255  config request_buf, response_buf;
256  request_buf.add_child("upload", cfg).add_child("data", std::move(addon_data));
257 
258  LOG_ADDONS << "sending " << id << '\n';
259 
260  send_request(request_buf, response_buf);
261  wait_for_transfer_done(VGETTEXT("Sending add-on <i>$addon_title</i>...", i18n_symbols
263 
264  if(const config& message_cfg = response_buf.child("message")) {
265  response_message = message_cfg["message"].str();
266  LOG_ADDONS << "server response: " << response_message << '\n';
267  }
268 
269  return !update_last_error(response_buf);
270 
271 }
272 
273 bool addons_client::delete_remote_addon(const std::string& id, std::string& response_message)
274 {
275  response_message.clear();
276 
277  config cfg = get_addon_pbl_info(id);
278 
279  utils::string_map i18n_symbols;
280  i18n_symbols["addon_title"] = font::escape_text(cfg["title"]);
281  if(i18n_symbols["addon_title"].empty()) {
282  i18n_symbols["addon_title"] = font::escape_text(make_addon_title(id));
283  }
284 
285  config request_buf, response_buf;
286  config& request_body = request_buf.add_child("delete");
287 
288  request_body["name"] = id;
289  request_body["passphrase"] = cfg["passphrase"];
290 
291  LOG_ADDONS << "requesting server to delete " << id << '\n';
292 
293  send_request(request_buf, response_buf);
294  wait_for_transfer_done(VGETTEXT("Removing add-on <i>$addon_title</i> from the server...", i18n_symbols
295  ));
296 
297  if(const config& message_cfg = response_buf.child("message")) {
298  response_message = message_cfg["message"].str();
299  LOG_ADDONS << "server response: " << response_message << '\n';
300  }
301 
302  return !update_last_error(response_buf);
303 }
304 
305 bool addons_client::download_addon(config& archive_cfg, const std::string& id, const std::string& title, const version_info& version, bool increase_downloads)
306 {
307  archive_cfg.clear();
308 
309  config request_buf;
310  config& request_body = request_buf.add_child("request_campaign");
311 
312  request_body["name"] = id;
313  request_body["increase_downloads"] = increase_downloads;
314  request_body["version"] = version.str();
315  request_body["from_version"] = get_addon_version_info(id);
316 
317  utils::string_map i18n_symbols;
318  i18n_symbols["addon_title"] = font::escape_text(title);
319 
320  LOG_ADDONS << "downloading " << id << '\n';
321 
322  send_request(request_buf, archive_cfg);
323  wait_for_transfer_done(VGETTEXT("Downloading add-on <i>$addon_title</i>...", i18n_symbols));
324 
325  return !update_last_error(archive_cfg);
326 }
327 
329 {
330  const cursor::setter cursor_setter(cursor::WAIT);
331 
332  utils::string_map i18n_symbols;
333  i18n_symbols["addon_title"] = font::escape_text(info.title);
334 
335  if(archive_cfg.has_child("removelist") || archive_cfg.has_child("addlist")) {
336  LOG_ADDONS << "Received an updatepack for the addon '" << info.id << "'\n";
337 
338  // A consistency check
339  for(const config::any_child entry : archive_cfg.all_children_range()) {
340  if(entry.key == "removelist" || entry.key == "addlist") {
341  if(!check_names_legal(entry.cfg)) {
342  gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has an invalid file or directory "
343  "name and cannot be installed.", i18n_symbols));
344  return false;
345  }
346  if(!check_case_insensitive_duplicates(entry.cfg)) {
347  gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has file or directory names "
348  "with case conflicts. This may cause problems.", i18n_symbols));
349  }
350  }
351  }
352 
353  for(const config::any_child entry : archive_cfg.all_children_range()) {
354  if(entry.key == "removelist") {
355  purge_addon(entry.cfg);
356  } else if(entry.key == "addlist") {
357  unarchive_addon(entry.cfg);
358  }
359  }
360 
361  LOG_ADDONS << "Update completed.\n";
362 
363  //#TODO: hash verification ???
364  } else {
365  LOG_ADDONS << "Received a full pack for the addon '" << info.id << "'\n";
366 
367  if(!check_names_legal(archive_cfg)) {
368  gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has an invalid file or directory "
369  "name and cannot be installed.", i18n_symbols));
370  return false;
371  }
372  if(!check_case_insensitive_duplicates(archive_cfg)) {
373  gui2::show_error_message(VGETTEXT("The add-on <i>$addon_title</i> has file or directory names "
374  "with case conflicts. This may cause problems.", i18n_symbols));
375  }
376 
377  LOG_ADDONS << "unpacking " << info.id << '\n';
378 
379  // Remove any previously installed versions
380  if(!remove_local_addon(info.id)) {
381  WRN_ADDONS << "failed to uninstall previous version of " << info.id << "; the add-on may not work properly!\n";
382  }
383 
384  unarchive_addon(archive_cfg);
385  LOG_ADDONS << "unpacking finished\n";
386  }
387 
388  config info_cfg;
389  info.write_minimal(info_cfg);
390  write_addon_install_info(info.id, info_cfg);
391 
392  return true;
393 }
394 
396 {
397  config archive;
398 
399  if(!(
400  download_addon(archive, addon.id, addon.display_title_full(), addon.current_version, !is_addon_installed(addon.id)) &&
401  install_addon(archive, addon)
402  )) {
403  const std::string& server_error = get_last_server_error();
404  if(!server_error.empty()) {
406  _("The server responded with an error:") + "\n" + server_error);
407  }
408  return false;
409  } else {
410  return true;
411  }
412 }
413 
415 {
416  install_result result;
418  result.wml_changed = false;
419 
420  auto cursor_setter = std::make_unique<cursor::setter>(cursor::WAIT);
421 
422  // TODO: We don't currently check for the need to upgrade. I'll probably
423  // work on that when implementing dependency tiers later.
424 
425  const std::set<std::string>& deps = addon.resolve_dependencies(addons);
426 
427  std::vector<std::string> missing_deps;
428  std::vector<std::string> broken_deps;
429 
430  for(const std::string& dep : deps) {
431  try {
433 
434  // ADDON_NONE means not installed.
435  if(info.state == ADDON_NONE) {
436  missing_deps.push_back(dep);
437  } else if(info.state == ADDON_INSTALLED_UPGRADABLE) {
438  // Tight now, we don't need to distinguish the lists of missing
439  // and outdated addons, so just add them to missing.
440  missing_deps.push_back(dep);
441  }
442  } catch(const std::out_of_range&) {
443  // Dependency wasn't found on server, check locally directly.
444  if(!is_addon_installed(dep)) {
445  broken_deps.push_back(dep);
446  }
447  }
448  }
449 
450  cursor_setter.reset();
451 
452  if(!broken_deps.empty()) {
453  std::string broken_deps_report;
454 
455  broken_deps_report = _n(
456  "The selected add-on has the following dependency, which is not currently installed or available from the server. Do you wish to continue?",
457  "The selected add-on has the following dependencies, which are not currently installed or available from the server. Do you wish to continue?",
458  broken_deps.size());
459  broken_deps_report += "\n";
460 
461  for(const std::string& broken_dep_id : broken_deps) {
462  broken_deps_report += "\n " + font::unicode_bullet + " " + make_addon_title(broken_dep_id);
463  }
464 
465  if(gui2::show_message(_("Broken Dependencies"), broken_deps_report, gui2::dialogs::message::yes_no_buttons) != gui2::retval::OK) {
467  return result; // canceled by user
468  }
469  }
470 
471  if(missing_deps.empty()) {
472  // No dependencies to install, carry on.
473  return result;
474  }
475 
476  {
478  for(const std::string& dep : missing_deps) {
479  options[dep] = addons.at(dep);
480  }
481 
482  if(!gui2::dialogs::install_dependencies::execute(options)) {
483  return result; // the user has chosen to continue without installing anything.
484  }
485  }
486 
487  //
488  // Install dependencies now.
489  //
490 
491  std::vector<std::string> failed_titles;
492 
493  for(const std::string& dep : missing_deps) {
494  const addon_info& missing_addon = addons.at(dep);
495 
496  if(!try_fetch_addon(missing_addon)) {
497  failed_titles.push_back(missing_addon.title);
498  } else {
499  result.wml_changed = true;
500  }
501  }
502 
503  if(!failed_titles.empty()) {
504  const std::string& failed_deps_report = _n(
505  "The following dependency could not be installed. Do you still wish to continue?",
506  "The following dependencies could not be installed. Do you still wish to continue?",
507  failed_titles.size()) + std::string("\n\n") + utils::bullet_list(failed_titles);
508 
509  result.outcome = gui2::show_message(_("Dependencies Installation Failed"), failed_deps_report, gui2::dialogs::message::yes_no_buttons) == gui2::retval::OK ? install_outcome::success : install_outcome::abort; // If the user cancels, return abort. Otherwise, return success, since the user chose to ignore the failure.
510  return result;
511  }
512 
513  return result;
514 }
515 
517 {
518  const std::string& addon_id = addon.id;
519 
520  const bool pbl = have_addon_pbl_info(addon_id);
521  const bool vcs = have_addon_in_vcs_tree(addon_id);
522 
523  if(!pbl && !vcs) {
524  return true;
525  }
526 
527  utils::string_map symbols;
528  symbols["addon"] = font::escape_text(addon.title);
529  std::string text;
530  std::vector<std::string> extra_items;
531 
532  text = VGETTEXT("The add-on '$addon|' is already installed and contains additional information that will be permanently lost if you continue:", symbols);
533  text += "\n\n";
534 
535  if(pbl) {
536  extra_items.push_back(_("Publishing information file (.pbl)"));
537  }
538 
539  if(vcs) {
540  extra_items.push_back(_("Version control system (VCS) information"));
541  }
542 
543  text += utils::bullet_list(extra_items) + "\n\n";
544  text += _("Do you really wish to continue?");
545 
547 }
548 
550 {
551  if(!(do_check_before_overwriting_addon(addon))) {
552  // Just do nothing and leave.
553  install_result result;
555  result.wml_changed = false;
556 
557  return result;
558  }
559 
560  // Resolve any dependencies
561  install_result res = do_resolve_addon_dependencies(addons, addon);
562  if(res.outcome != install_outcome::success) { // this function only returns SUCCESS and ABORT as outcomes
563  return res; // user aborted
564  }
565 
566  if(!try_fetch_addon(addon)) {
568  return res; //wml_changed should have whatever value was obtained in resolving dependencies
569  } else {
570  res.wml_changed = true;
571  return res; //we successfully installed something, so now the wml was definitely changed
572  }
573 }
574 
576 {
577  if(const config& error = response_cfg.child("error")) {
578  if(error.has_attribute("status_code")) {
579  const auto& status_msg = translated_addon_check_status(error["status_code"].to_unsigned());
580  last_error_ = font::escape_text(status_msg);
581  } else {
582  last_error_ = font::escape_text(error["message"].str());
583  }
584  last_error_data_ = font::escape_text(error["extra_data"].str());
585  ERR_ADDONS << "server error: " << error << '\n';
586  return true;
587  } else {
588  last_error_.clear();
589  last_error_data_.clear();
590  return false;
591  }
592 }
593 
595 {
596  last_error_.clear();
597  last_error_data_.clear();
598 }
599 
601 {
602  server_version_.clear();
603  server_capabilities_.clear();
604  server_url_.clear();
605  license_notice_.clear();
606 }
607 
609 {
610  assert(conn_ != nullptr);
611  if(conn_ == nullptr) {
612  ERR_ADDONS << "not connected to server" << std::endl;
613  throw not_connected_to_server();
614  }
615 }
616 
617 void addons_client::send_request(const config& request, config& response)
618 {
619  check_connected();
620 
621  response.clear();
622  conn_->transfer(request, response);
623 }
624 
625 void addons_client::send_simple_request(const std::string& request_string, config& response)
626 {
627  config request;
628  request.add_child(request_string);
629  send_request(request, response);
630 }
631 struct read_addon_connection_data : public network_transmission::connection_data
632 {
634  : conn_(conn), client_(client) {}
635  std::size_t total() override { return conn_.bytes_to_read(); }
636  virtual std::size_t current() override { return conn_.bytes_read(); }
637  virtual bool finished() override { return conn_.done(); }
638  virtual void cancel() override { client_.connect(); }
639  virtual void poll() override { conn_.poll(); }
642 };
643 struct connect_connection_data : public network_transmission::connection_data
644 {
646  : conn_(conn), client_(client) {}
647  std::size_t total() override { return conn_.bytes_to_read(); }
648  std::size_t current() override { return conn_.bytes_read(); }
649  bool finished() override { return conn_.done(); }
650  void cancel() override { client_.disconnect(); }
651  void poll() override { conn_.poll(); }
654 };
655 struct write_addon_connection_data : public network_transmission::connection_data
656 {
658  : conn_(conn), client_(client) {}
659  std::size_t total() override { return conn_.bytes_to_write(); }
660  virtual std::size_t current() override { return conn_.bytes_written(); }
661  virtual bool finished() override { return conn_.done(); }
662  virtual void cancel() override { client_.connect(); }
663  virtual void poll() override { conn_.poll(); }
666 };
667 void addons_client::wait_for_transfer_done(const std::string& status_message, transfer_mode mode)
668 {
669  check_connected();
670  std::unique_ptr<network_transmission::connection_data> cd;
671  switch(mode) {
673  cd.reset(new read_addon_connection_data{*conn_, *this});
674  break;
676  cd.reset(new connect_connection_data{*conn_, *this});
677  break;
679  cd.reset(new write_addon_connection_data{*conn_, *this});
680  break;
681  default:
682  throw std::invalid_argument("Addon client: invalid transfer mode");
683  }
684 
685  gui2::dialogs::network_transmission stat(*cd, _("Add-ons Manager"), status_message);
686 
687  if(!stat.show()) {
688  // Notify the caller chain that the user aborted the operation.
689  if(mode == transfer_mode::connect) {
690  throw user_disconnect();
691  } else {
692  throw user_exit();
693  }
694  }
695 }
install_result do_resolve_addon_dependencies(const addons_list &addons, const addon_info &addon)
Warns the user about unresolved dependencies and installs them if they choose to do so...
Definition: client.cpp:414
const std::string & get_last_server_error() const
Returns the last error message sent by the server, or an empty string.
Definition: client.hpp:73
bool check_names_legal(const config &dir, std::vector< std::string > *badlist)
Scans an add-on archive for illegal names.
Definition: validation.cpp:227
ADDON_STATUS state
Definition: state.hpp:56
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
static std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:96
config & child(config_key_type 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:420
bool check_case_insensitive_duplicates(const config &dir, std::vector< std::string > *badlist)
Scans an add-on archive for case-conflicts.
Definition: validation.cpp:236
std::string port_
Definition: client.hpp:214
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:921
void clear_server_info()
Definition: client.cpp:600
std::map< std::string, t_string > string_map
virtual bool finished() override
Definition: client.cpp:661
bool contains_hashlist(const config &from, const config &to)
Definition: validation.cpp:348
void purge_addon(const config &removelist)
Removes the listed files from the addon.
Definition: manager.cpp:345
void append(const config &cfg)
Append data from another config object to this one.
Definition: config.cpp:287
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
bool download_addon(config &archive_cfg, const std::string &id, const std::string &title, const version_info &version, bool increase_downloads=true)
Downloads the specified add-on from the server.
Definition: client.cpp:305
void write_addon_install_info(const std::string &addon_name, const config &cfg)
Definition: manager.cpp:112
std::string host_
Definition: client.hpp:213
virtual void poll() override
Definition: client.cpp:663
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:39
logger & info()
Definition: log.cpp:91
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:412
bool try_fetch_addon(const addon_info &addon)
Definition: client.cpp:395
bool is_addon_installed(const std::string &addon_name)
Check whether the specified add-on is currently installed.
Definition: manager.cpp:204
bool update_last_error(config &response_cfg)
Definition: client.cpp:575
addons_client(const addons_client &)=delete
virtual std::size_t current() override
Definition: client.cpp:660
std::pair< std::string, std::string > parse_network_address(const std::string &address, const std::string &default_port)
Parse a host:port style network address, supporting [] notation for ipv6 addresses.
#define ERR_ADDONS
Definition: client.cpp:39
virtual bool finished() override
Definition: client.cpp:637
std::string last_error_
Definition: client.hpp:216
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
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:328
void clear()
Definition: config.cpp:863
bool have_addon_in_vcs_tree(const std::string &addon_name)
Returns whether the specified add-on appears to be managed by a VCS or not.
Definition: manager.cpp:53
static std::string _(const char *str)
Definition: gettext.hpp:92
void check_connected() const
Makes sure the add-ons server connection is working.
Definition: client.cpp:608
bool show(const unsigned auto_close_time=0)
Shows the window.
void write_minimal(config &cfg) const
Write only minimal WML used for state tracking (_info.cfg) files.
Definition: info.cpp:144
version_info get_addon_version_info(const std::string &addon)
Returns a particular installed add-on&#39;s version information.
Definition: manager.cpp:396
Add-on is not installed.
Definition: state.hpp:23
void send_request(const config &request, config &response)
Sends a request to the add-ons server.
Definition: client.cpp:617
void poll() override
Definition: client.cpp:651
connect_connection_data(network_asio::connection &conn, addons_client &client)
Definition: client.cpp:645
static lg::log_domain log_addons_client("addons-client")
#define LOG_ADDONS
Definition: client.cpp:41
const config & options()
Definition: game.cpp:563
std::string server_url_
Definition: client.hpp:221
virtual void poll() override
Definition: client.cpp:639
std::string bullet_list(const T &v, std::size_t indent=4, const std::string &bullet=font::unicode_bullet)
Generates a new string containing a bullet list.
addons_client & client_
Definition: client.cpp:665
read_addon_connection_data(network_asio::connection &conn, addons_client &client)
Definition: client.cpp:633
std::size_t current() override
Definition: client.cpp:648
virtual void cancel() override
Definition: client.cpp:662
void clear_last_error()
Definition: client.cpp:594
std::set< std::string > server_capabilities_
Definition: client.hpp:220
std::string id
Definition: info.hpp:75
Version in the server is newer than local installation.
Definition: state.hpp:27
addons_client & client_
Definition: client.cpp:641
void set_addon_pbl_info(const std::string &addon_name, const config &cfg)
Definition: manager.cpp:81
void wait_for_transfer_done(const std::string &status_message, transfer_mode mode=transfer_mode::download)
Waits for a network transfer, displaying a status window.
Definition: client.cpp:667
A class that represents a TCP/IP connection.
Shows a yes and no button.
Definition: message.hpp:79
network_asio::connection & conn_
Definition: client.cpp:640
bool remove_local_addon(const std::string &addon)
Removes the specified add-on, deleting its full directory structure.
Definition: manager.cpp:129
void connect()
Tries to establish a connection to the add-ons server.
Definition: client.cpp:65
void swap(config &lhs, config &rhs)
Implement non-member swap function for std::swap (calls config::swap).
Definition: config.cpp:1399
std::size_t total() override
Definition: client.cpp:647
Add-ons (campaignd) client class.
Definition: client.hpp:39
The add-on was correctly installed.
network_asio::connection & conn_
Definition: client.cpp:652
config get_addon_pbl_info(const std::string &addon_name)
Gets the publish information for an add-on.
Definition: manager.cpp:67
User aborted the operation because of an issue with dependencies or chose not to overwrite the add-on...
bool finished() override
Definition: client.cpp:649
std::string addr_
Definition: client.hpp:212
bool addon_name_legal(const std::string &name)
Checks whether an add-on id/name is legal or not.
Definition: validation.cpp:95
std::string escape_text(const std::string &text)
Escapes the pango markup characters in a text.
Definition: escape.hpp:32
bool do_check_before_overwriting_addon(const addon_info &addon)
Checks whether the given add-on has local .pbl or VCS information and asks before overwriting it...
Definition: client.cpp:516
Thrown by operations encountering invalid UTF-8 data.
network_asio::connection & conn_
Definition: client.cpp:664
bool have_addon_pbl_info(const std::string &addon_name)
Returns whether a .pbl file is present for the specified add-on or not.
Definition: manager.cpp:62
void archive_addon(const std::string &addon_name, config &cfg)
Archives an add-on into a config object for campaignd transactions.
Definition: manager.cpp:282
std::size_t total() override
Definition: client.cpp:659
std::string translated_addon_check_status(unsigned int code)
Definition: validation.cpp:580
std::string display_title_full() const
Definition: info.cpp:223
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
Definition: random.hpp:51
const std::string unicode_bullet
Definition: constants.cpp:43
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t total() override
Definition: client.cpp:635
write_addon_connection_data(network_asio::connection &conn, addons_client &client)
Definition: client.cpp:657
install_outcome outcome
Overall outcome of the operation.
Definition: client.hpp:125
rng * generator
This generator is automatically synced during synced context.
Definition: random.cpp:60
Represents version numbers.
config & add_child(config_key_type key)
Definition: config.cpp:476
#define WRN_ADDONS
Definition: client.cpp:40
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
Definition: info.cpp:319
std::string last_error_data_
Definition: client.hpp:217
std::unique_ptr< network_asio::connection > conn_
Definition: client.hpp:215
void make_updatepack(config &pack, const config &from, const config &to)
&from, &to are the top directories of their structures; addlist/removelist tag is treated as [dir] ...
Definition: validation.cpp:430
addon_tracking_info get_addon_tracking_info(const addon_info &addon)
Get information about an add-on comparing its local state with the add-ons server entry...
Definition: state.cpp:24
std::vector< std::string > split(const config_attribute_value &val)
const unsigned short default_campaignd_port
Default port number for the addon server.
Definition: validation.cpp:27
bool wml_changed
Specifies if WML on disk was altered and needs to be reloaded.
Definition: client.hpp:134
void unarchive_addon(const config &cfg)
Definition: manager.cpp:314
std::string title
Definition: info.hpp:76
std::set< std::string > resolve_dependencies(const addons_list &addons) const
Resolve an add-on&#39;s dependency tree in a recursive fashion.
Definition: info.cpp:280
std::string server_version_
Definition: client.hpp:219
Standard logging facilities (interface).
std::string str() const
Serializes the version number into string form.
bool upload_addon(const std::string &id, std::string &response_message, config &cfg, bool local_only)
Uploads an add-on to the server.
Definition: client.cpp:149
Dialog that tracks network transmissions.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:205
The add-on could not be downloaded from the server.
Stores additional status information about add-ons.
Definition: state.hpp:45
addons_client & client_
Definition: client.cpp:653
Dialog was closed with the OK button.
Definition: retval.hpp:34
virtual std::size_t current() override
Definition: client.cpp:636
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
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:273
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
Definition: client.cpp:126
Contains the outcome of an add-on install operation.
Definition: client.hpp:120
version_info current_version
Definition: info.hpp:81
static map_location::DIRECTION n
std::map< std::string, addon_info > addons_list
Definition: info.hpp:27
virtual void cancel() override
Definition: client.cpp:638
install_result install_addon_with_checks(const addons_list &addons, const addon_info &addon)
Performs an add-on download and install cycle.
Definition: client.cpp:549
bool request_addons_list(config &cfg)
Request the add-ons list from the server.
Definition: client.cpp:109
void send_simple_request(const std::string &request_string, config &response)
Sends a simple request message to the add-ons server.
Definition: client.cpp:625
Networked add-ons (campaignd) client interface.
std::string license_notice_
Definition: client.hpp:222
void cancel() override
Definition: client.cpp:650