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