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