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