18 #include "addon/manager.hpp" 
   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) 
   58     , server_capabilities_()
 
   64     } 
catch(
const std::runtime_error&) {
 
   74     i18n_symbols[
"server_address"] = 
addr_;
 
   78     const auto& 
msg = 
VGETTEXT(
"Connecting to $server_address|...", i18n_symbols);
 
  111     LOG_ADDONS << 
"Server " << id_desc << 
" version " << version_desc
 
  117     std::map<std::string, int> counts;
 
  121     request.
add_child(
"addon_count_by_type");
 
  127         counts[attr.first] = attr.second.to_int();
 
  143     child[
"addon"] = addon;
 
  198     child[
"username"] = username;
 
  199     child[
"passphrase"] = passphrase;
 
  218     child[
"addon"] = addon;
 
  219     child[
"username"] = username;
 
  220     child[
"passphrase"] = passphrase;
 
  238     child[
"addon"] = addon;
 
  239     child[
"username"] = username;
 
  240     child[
"passphrase"] = passphrase;
 
  259     req_child[
"send_icons"] = icons;
 
  291         terms = msg_cfg[
"message"].str();
 
  301     response_message.clear();
 
  305     if(i18n_symbols[
"addon_title"].empty()) {
 
  312             VGETTEXT(
"The add-on <i>$addon_title</i> has an invalid id '$addon_id' " 
  313                 "and cannot be published.", i18n_symbols);
 
  324             VGETTEXT(
"The add-on <i>$addon_title</i> has a file or directory " 
  325                 "containing invalid characters and cannot be published.", i18n_symbols);
 
  329     std::vector<std::string> badnames;
 
  332             VGETTEXT(
"The add-on <i>$addon_title</i> has an invalid file or directory " 
  333                 "name and cannot be published. " 
  335                 "File or directory names may not contain '..' or end with '.' or be longer than 255 characters. " 
  336                 "It also may not contain whitespace, control characters, or any of the following characters:\n\n" * / : < > ? \\ | ~" 
  343             VGETTEXT(
"The add-on <i>$addon_title</i> contains files or directories with case conflicts. " 
  344                 "File or directory names may not be differently-cased versions of the same string.", i18n_symbols);
 
  350         last_error_ = 
VGETTEXT(
"The connection to the remote server is not secure. The add-on <i>$addon_title</i> cannot be uploaded.", i18n_symbols);
 
  355         last_error_ = 
VGETTEXT(
"The file size for the icon for the add-on <i>$addon_title</i> is too large.", i18n_symbols);
 
  361         config hashlist, hash_request;
 
  362         config& request_body = hash_request.
add_child(
"request_campaign_hash");
 
  365         request_body[
"name"] = 
cfg[
"name"];
 
  373                 LOG_ADDONS << 
"making an update pack for the add-on " << 
id;
 
  379                 config request_buf, response_buf;
 
  388                     response_message = message_cfg[
"message"].str();
 
  389                     LOG_ADDONS << 
"server response: " << response_message;
 
  399     config request_buf, response_buf;
 
  409         response_message = message_cfg[
"message"].str();
 
  410         LOG_ADDONS << 
"server response: " << response_message;
 
  419     response_message.clear();
 
  422     if(admin_set.empty()) {
 
  431     if(i18n_symbols[
"addon_title"].empty()) {
 
  435     config request_buf, response_buf;
 
  439     if(
cfg[
"passphrase"].empty()) {
 
  441         if(!gui2::dialogs::addon_auth::execute(
cfg)) {
 
  444             error[
"message"] = 
"Password not provided.";
 
  451     request_body[
"admin"] = admin_set.size() > 0;
 
  452     request_body[
"name"] = 
id;
 
  453     request_body[
"passphrase"] = 
cfg[
"passphrase"];
 
  455     request_body[
"uploader"] = 
cfg[
"uploader"];
 
  463         response_message = message_cfg[
"message"].str();
 
  464         LOG_ADDONS << 
"server response: " << response_message;
 
  477     request_body[
"name"] = 
id;
 
  478     request_body[
"increase_downloads"] = increase_downloads;
 
  479     request_body[
"version"] = version.
str();
 
  501     auto progress_cb = [&progress_dlg](
unsigned value) {
 
  502         progress_dlg->update_progress(value);
 
  506         LOG_ADDONS << 
"Received an updatepack for the addon '" << 
info.id << 
"'";
 
  510             if(key == 
"removelist" || key == 
"addlist") {
 
  513                                     "name and cannot be installed.", i18n_symbols));
 
  518                                     "with case conflicts. This may cause problems.", i18n_symbols));
 
  524             if(key == 
"removelist") {
 
  526             } 
else if(key == 
"addlist") {
 
  535         LOG_ADDONS << 
"Received a full pack for the addon '" << 
info.id << 
"'";
 
  539                             "name and cannot be installed.", i18n_symbols));
 
  544                             "with case conflicts. This may cause problems.", i18n_symbols));
 
  551             WRN_ADDONS << 
"failed to uninstall previous version of " << 
info.id << 
"; the add-on may not work properly!";
 
  559     info.write_minimal(info_cfg);
 
  575         if(!server_error.empty()) {
 
  577                 _(
"The server responded with an error:") + 
"\n" + server_error, 
true);
 
  591     auto cursor_setter = std::make_unique<cursor::setter>(
cursor::WAIT);
 
  598     std::vector<std::string> missing_deps;
 
  599     std::vector<std::string> broken_deps;
 
  606     for(
const std::string& dep : deps) {
 
  612                 missing_deps.push_back(dep);
 
  616                 missing_deps.push_back(dep);
 
  618         } 
catch(
const std::out_of_range&) {
 
  621                 broken_deps.push_back(dep);
 
  626     cursor_setter.reset();
 
  628     if(!broken_deps.empty()) {
 
  629         std::string broken_deps_report;
 
  631         broken_deps_report = 
_n(
 
  632             "The selected add-on has the following dependency, which is not currently installed or available from the server. Do you wish to continue?",
 
  633             "The selected add-on has the following dependencies, which are not currently installed or available from the server. Do you wish to continue?",
 
  635         broken_deps_report += 
"\n";
 
  637         for(
const std::string& broken_dep_id : broken_deps) {
 
  647     if(missing_deps.empty()) {
 
  654         for(
const std::string& dep : missing_deps) {
 
  655             options[dep] = addons.at(dep);
 
  658         if(!gui2::dialogs::install_dependencies::execute(options)) {
 
  667     std::vector<std::string> failed_titles;
 
  669     for(
const std::string& dep : missing_deps) {
 
  670         const addon_info& missing_addon = addons.at(dep);
 
  673             failed_titles.push_back(missing_addon.
title);
 
  679     if(!failed_titles.empty()) {
 
  680         const std::string& failed_deps_report = 
_n(
 
  681             "The following dependency could not be installed. Do you still wish to continue?",
 
  682             "The following dependencies could not be installed. Do you still wish to continue?",
 
  694     const std::string& addon_id = addon.
id;
 
  706     std::vector<std::string> extra_items;
 
  708     text = 
VGETTEXT(
"The add-on '$addon|' is already installed and contains additional information that will be permanently lost if you continue:", symbols);
 
  712         extra_items.push_back(
_(
"Publishing information file (.pbl)"));
 
  716         extra_items.push_back(
_(
"Version control system (VCS) information"));
 
  720     text += 
_(
"Do you really wish to continue?");
 
  754         if(error->has_attribute(
"status_code")) {
 
  787     assert(
conn_ != 
nullptr);
 
  788     if(
conn_ == 
nullptr) {
 
  799     conn_->transfer(request, response);
 
  847     std::unique_ptr<network_transmission::connection_data> cd;
 
  859         throw std::invalid_argument(
"Addon client: invalid transfer mode");
 
bool remove_local_addon(const std::string &addon)
Removes the specified add-on, deleting its full directory structure.
config get_addon_pbl_info(const std::string &addon_name, bool do_validate)
Gets the publish information for an add-on.
void unarchive_addon(const config &cfg, std::function< void(unsigned)> progress_callback)
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.
void purge_addon(const config &removelist)
Removes the listed files from the addon.
void archive_addon(const std::string &addon_name, config &cfg)
Archives an add-on into a config object for campaignd transactions.
void write_addon_install_info(const std::string &addon_name, const config &cfg)
bool have_addon_pbl_info(const std::string &addon_name)
Returns whether a .pbl file is present for the specified add-on or not.
version_info get_addon_version_info(const std::string &addon)
Returns a particular installed add-on's version information.
bool is_addon_installed(const std::string &addon_name)
Check whether the specified add-on is currently installed.
Add-ons (campaignd) client class.
std::map< std::string, int > get_addon_count_by_type()
void disconnect()
Disconnects from the add-on server.
install_result install_addon_with_checks(const addons_list &addons, const addon_info &addon)
Performs an add-on download and install cycle.
bool unhide_addon(const std::string &addon, const std::string &username, const std::string &passphrase)
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.
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.
config get_forum_auth_usage()
std::unique_ptr< network_asio::connection > conn_
@ 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...
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.
void send_request(const config &request, config &response)
Sends a request to the add-ons server.
bool try_fetch_addon(const addon_info &addon)
config get_addon_downloads_by_version(const std::string &addon)
bool hide_addon(const std::string &addon, const std::string &username, const std::string &passphrase)
void check_connected() const
Makes sure the add-ons server connection is working.
std::set< std::string > server_capabilities_
bool install_addon(config &archive_cfg, const addon_info &info)
Installs the specified add-on using an archive received from the server.
config get_hidden_addons(const std::string &username, const std::string &passphrase)
bool delete_remote_addon(const std::string &id, std::string &response_message, const std::set< std::string > &admin_set={})
Requests the specified add-on to be removed from the server.
std::string license_notice_
addons_client(const addons_client &)=delete
std::string last_error_data_
std::string server_version_
void connect()
Tries to establish a connection to the add-ons server.
void send_simple_request(const std::string &request_string, config &response)
Sends a simple request message to the add-ons server.
bool is_error_response(const config &response_cfg)
If the response has the [error] child, then check for the status_code attribute.
config get_addon_admins()
bool request_distribution_terms(std::string &terms)
Request the add-ons server distribution terms message.
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.
const std::string & get_last_server_error() const
Returns the last error message sent by the server, or an empty string.
bool request_addons_list(config &cfg, bool icons)
Request the add-ons list from the server.
bool upload_addon(const std::string &id, std::string &response_message, config &cfg, bool local_only)
Uploads an add-on to the server.
A config object defines a single node in a WML file, with access to child nodes.
config & add_child(std::string_view key)
void append(const config &cfg)
Append data from another config object to this one.
optional_config_impl< config > optional_child(std::string_view key, int n=0)
Equivalent to mandatory_child, but returns an empty optional if the nth child was not found.
const_attr_itors attribute_range() const
auto all_children_view() const
In-order iteration over all children.
bool has_child(std::string_view key) const
Determine whether a config has a child or not.
config & mandatory_child(std::string_view key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
static auto display(T &&... args)
@ yes_no_buttons
Shows a yes and no button.
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.
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.
static lg::log_domain log_addons_client("addons-client")
Networked add-ons (campaignd) client interface.
void swap(config &lhs, config &rhs) noexcept
Implement non-member swap function for std::swap (calls config::swap).
static std::string _n(const char *str1, const char *str2, int n)
static std::string _(const char *str)
std::string id
Text to match against addon_info.tags()
std::string make_addon_title(const std::string &id)
Replaces underscores to dress up file or dirnames as add-on titles.
std::map< std::string, addon_info > addons_list
Standard logging facilities (interface).
std::string escape_text(std::string_view text)
Escapes the pango markup characters in a text.
const std::string unicode_bullet
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
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.
@ OK
Dialog was closed with the OK button.
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="")
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.
@ ADDON_NONE
Add-on is not installed.
@ ADDON_INSTALLED_UPGRADABLE
Version in the server is newer than local installation.
version_info current_version
std::string display_title_full() const
std::set< std::string > resolve_dependencies(const addons_list &addons) const
Resolve an add-on's dependency tree in a recursive fashion.
Stores additional status information about add-ons.
Contains the outcome of an add-on install operation.
install_outcome outcome
Overall outcome of the operation.
bool wml_changed
Specifies if WML on disk was altered and needs to be reloaded.
connect_connection_data(network_asio::connection &conn, addons_client &client)
std::size_t total() override
network_asio::connection & conn_
std::size_t current() override
network_asio::connection & conn_
virtual void poll() override
virtual void cancel() override
virtual std::size_t current() override
virtual bool finished() override
read_addon_connection_data(network_asio::connection &conn, addons_client &client)
std::size_t total() override
virtual void poll() override
virtual bool finished() override
std::size_t total() override
write_addon_connection_data(network_asio::connection &conn, addons_client &client)
virtual void cancel() override
virtual std::size_t current() override
network_asio::connection & conn_
bool addon_name_legal(const std::string &name)
Checks whether an add-on id/name is legal or not.
bool addon_icon_too_large(const std::string &icon)
Checks whether an add-on icon is too large.
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]
bool check_names_legal(const config &dir, std::vector< std::string > *badlist)
Scans an add-on archive for illegal names.
bool contains_hashlist(const config &from, const config &to)
const unsigned short default_campaignd_port
Default port number for the addon server.
std::string translated_addon_check_status(unsigned int code)
bool check_case_insensitive_duplicates(const config &dir, std::vector< std::string > *badlist)
Scans an add-on archive for case-conflicts.