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