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