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