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