Skip to content

Commit

Permalink
Incremental addon uploads (client side)
Browse files Browse the repository at this point in the history
  • Loading branch information
kabachuha authored and irydacea committed Oct 15, 2020
1 parent c146034 commit a0cca4b
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 61 deletions.
39 changes: 38 additions & 1 deletion src/addon/client.cpp
Expand Up @@ -105,7 +105,7 @@ bool addons_client::request_distribution_terms(std::string& terms)
return !this->update_last_error(response_buf);
}

bool addons_client::upload_addon(const std::string& id, std::string& response_message, config& cfg)
bool addons_client::upload_addon(const std::string& id, std::string& response_message, config& cfg, bool local_only)
{
LOG_ADDONS << "preparing to upload " << id << '\n';

Expand Down Expand Up @@ -171,6 +171,43 @@ bool addons_client::upload_addon(const std::string& id, std::string& response_me
return false;
}

if(!local_only) {
// Try to make an upload pack if it's avaible on the server
config hashlist, hash_request;
config& request_body = hash_request.add_child("request_campaign_hash");
// We're requesting the latest version of an addon, so we may not specify it
// #TODO: Make a selection of the base version for the update ?
request_body["name"] = cfg["name"];
this->send_request(hash_request, hashlist);
this->wait_for_transfer_done(VGETTEXT("Requesting the older version composition for the add-on <i>$addon_title</i>...", i18n_symbols));

// A silent error check
if(!hashlist.child("error")) {
if(!contains_hashlist(addon_data, hashlist) || !contains_hashlist(hashlist, addon_data)) {
LOG_ADDONS << "making an update pack for the add-on " << id << '\n';
config updatepack;
// The client shouldn't send the pack if the server is old due to the previous check,
// so the server should handle the new format in the `upload` request
make_updatepack(updatepack, hashlist, addon_data);

config request_buf, response_buf;
request_buf.add_child("upload", cfg).append(std::move(updatepack));
this->send_request(request_buf, response_buf);
this->wait_for_transfer_done(VGETTEXT("Sending an update pack for the add-on <i>$addon_title</i>...", i18n_symbols
), transfer_mode::upload);

if(const config& message_cfg = response_buf.child("message")) {
response_message = message_cfg["message"].str();
LOG_ADDONS << "server response: " << response_message << '\n';
}

if(!this->update_last_error(response_buf))
return true;
}
}
}
// 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

config request_buf, response_buf;
request_buf.add_child("upload", cfg).add_child("data", std::move(addon_data));

Expand Down
3 changes: 2 additions & 1 deletion src/addon/client.hpp
Expand Up @@ -110,8 +110,9 @@ class addons_client
* @param id Id. of the add-on to upload.
* @param response_message The server response message on success, such as "add-on accepted".
* @param cfg The pbl config of the add-on with the specified id.
* @param local_only Whether the addon is not present on the server.
*/
bool upload_addon(const std::string& id, std::string& response_message, config& cfg);
bool upload_addon(const std::string& id, std::string& response_message, config& cfg, bool local_only);

/**
* Requests the specified add-on to be removed from the server.
Expand Down
48 changes: 48 additions & 0 deletions src/addon/validation.cpp
Expand Up @@ -369,3 +369,51 @@ bool contains_hashlist(const config& from, const config& to)

return true;
}

//! Surround with [dir][/dir]
static void write_difference(config& pack, const config& from, const config& to, bool with_content)
{
pack["name"] = to["name"];

for(const config& f : to.child_range("file")) {
bool found = false;
for(const config& d : from.child_range("file")) {
found |= comp_file_hash(f, d);
if(found)
break;
}
if(!found) {
config& file = pack.add_child("file");
file["name"] = f["name"];
if(with_content) {
file["contents"] = f["contents"];
file["hash"] = file_hash(f);
}
}
}

for(const config& d : to.child_range("dir")) {
const config& origin_dir = from.find_child("dir", "name", d["name"]);
config& dir = pack.add_child("dir");
if(origin_dir) {
write_difference(dir, origin_dir, d, with_content);
} else {
const config dummy_dir = config("name", d["name"]);
write_difference(dir, dummy_dir, d, with_content);
}
}
}

/**
* &from, &to are top-dirs of their structures; addlist/removelist are equal to [dir]
* #TODO: make a clientside function to allow incremental uploadpacks using hash request from server
* Does it worth it to archive and write the pack on the fly using config_writer?
* #TODO: clientside verification?
*/
void make_updatepack(config& pack, const config& from, const config& to)
{
config& removelist = pack.add_child("removelist");
write_difference(removelist, to, from, false);
config& addlist = pack.add_child("addlist");
write_difference(addlist, from, to, true);
}
1 change: 1 addition & 0 deletions src/addon/validation.hpp
Expand Up @@ -101,3 +101,4 @@ const std::string file_hash(const config& file);
bool comp_file_hash(const config& file_a, const config& file_b);
void write_hashlist(config& hashlist, const config& data);
bool contains_hashlist(const config& from, const config& to);
void make_updatepack(config& pack, const config& from, const config& to);
2 changes: 1 addition & 1 deletion src/gui/dialogs/addon/manager.cpp
Expand Up @@ -756,7 +756,7 @@ void addon_manager::publish_addon(const addon_info& addon, window& window)
gui2::show_error_message(
_("The server responded with an error:") + "\n" + client_.get_last_server_error());
} else if(gui2::show_message(_("Terms"), server_msg, gui2::dialogs::message::ok_cancel_buttons, true) == gui2::retval::OK) {
if(!client_.upload_addon(addon_id, server_msg, cfg)) {
if(!client_.upload_addon(addon_id, server_msg, cfg, tracking_info_[addon_id].state == ADDON_INSTALLED_LOCAL_ONLY)) {
const std::string& msg = _("The server responded with an error:") +
"\n\n" + client_.get_last_server_error();
const std::string& extra_data = client_.get_last_server_error_data();
Expand Down
48 changes: 0 additions & 48 deletions src/server/campaignd/addon_utils.cpp
Expand Up @@ -153,54 +153,6 @@ void add_license(config& cfg)
copying["contents"] = contents;
}

//! Surround with [dir][/dir]
static void write_difference(config& pack, const config& from, const config& to, bool with_content)
{
pack["name"] = to["name"];

for(const config& f : to.child_range("file")) {
bool found = false;
for(const config& d : from.child_range("file")) {
found |= comp_file_hash(f, d);
if(found)
break;
}
if(!found) {
config& file = pack.add_child("file");
file["name"] = f["name"];
if(with_content) {
file["contents"] = f["contents"];
file["hash"] = file_hash(f);
}
}
}

for(const config& d : to.child_range("dir")) {
const config& origin_dir = from.find_child("dir", "name", d["name"]);
config& dir = pack.add_child("dir");
if(origin_dir) {
write_difference(dir, origin_dir, d, with_content);
} else {
const config dummy_dir = config("name", d["name"]);
write_difference(dir, dummy_dir, d, with_content);
}
}
}

/**
* &from, &to are top-dirs of their structures; addlist/removelist are equal to [dir]
* #TODO: make a clientside function to allow incremental uploadpacks using hash request from server
* Does it worth it to archive and write the pack on the fly using config_writer?
* #TODO: clientside verification?
*/
void make_updatepack(config& pack, const config& from, const config& to)
{
config& removelist = pack.add_child("removelist");
write_difference(removelist, to, from, false);
config& addlist = pack.add_child("addlist");
write_difference(addlist, from, to, true);
}

std::map<version_info, config&> get_version_map(config& addon)
{
auto version_map = std::map<version_info, config&>();
Expand Down
2 changes: 0 additions & 2 deletions src/server/campaignd/addon_utils.hpp
Expand Up @@ -81,8 +81,6 @@ void find_translations(const config& base_dir, config& addon);
*/
void add_license(config& cfg);

void make_updatepack(config& pack, const config& from, const config& to);

std::map<version_info, config&> get_version_map(config& addon);

}
11 changes: 3 additions & 8 deletions src/server/campaignd/server.cpp
Expand Up @@ -881,12 +881,6 @@ void server::handle_request_campaign_hash(const server::request& req)
return;
}

std::string to = req.cfg["version"].str();
if(to.empty()) {
send_error("No version of the add-on '" + req.cfg["name"].str() + "' was specified for a hash list request.", req.sock);
return;
}

std::string filename = campaign["filename"].str();

auto version_map = get_version_map(campaign);
Expand All @@ -895,11 +889,12 @@ void server::handle_request_campaign_hash(const server::request& req)
send_error("No versions of the add-on '" + req.cfg["name"].str() + "' are available on the server.", req.sock);
return;
} else {
auto version = version_map.find(version_info(to));
auto version = version_map.find(version_info(campaign["version"].str()));
if(version != version_map.end()) {
filename += version->second["filename"].str();
} else {
send_error("The selected version (" + to + ") of the add-on '" + req.cfg["name"].str() + "' has not been found by the server.", req.sock);
// Selecting the latest version if unspecified
filename += version_map.rbegin()->second["filename"].str();
return;
}

Expand Down

0 comments on commit a0cca4b

Please sign in to comment.