diff --git a/src/addon/validation.cpp b/src/addon/validation.cpp index a32d3250aad9..6761556deb79 100644 --- a/src/addon/validation.cpp +++ b/src/addon/validation.cpp @@ -16,6 +16,7 @@ #include "addon/validation.hpp" #include "config.hpp" #include "serialization/unicode_cast.hpp" +#include "hash.hpp" #include #include @@ -305,3 +306,66 @@ std::string unencode_binary(const std::string& str) res.resize(n); return res; } + +static const std::string file_hash_raw(const config& file) +{ + return utils::md5(file["contents"].str()).base64_digest(); +} + +const std::string file_hash(const config& file) +{ + std::string hash = file["hash"].str(); + if(hash.empty()) { + hash = file_hash_raw(file); + } + return hash; +} + +bool comp_file_hash(const config& file_a, const config& file_b) +{ + return file_hash(file_a) == file_hash(file_b); +} + +void write_hashlist(config& hashlist, const config& data) +{ + hashlist["name"] = data["name"]; + + for(const config& f : data.child_range("file")) { + config& file = hashlist.add_child("file"); + file["name"] = f["name"]; + file["hash"] = file_hash_raw(f); + } + + for(const config& d : data.child_range("dir")) { + config& dir = hashlist.add_child("dir"); + write_hashlist(dir, d); + } +} + +bool contains_hashlist(const config& from, const config& to) +{ + 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) { + return false; + } + } + + for(const config& d : to.child_range("dir")) { + const config& origin_dir = from.find_child("dir", "name", d["name"]); + if(origin_dir) { + return contains_hashlist(origin_dir, d); + } else { + // The case of empty new subdirectories + const config dummy_dir = config("name", d["name"]); + return contains_hashlist(dummy_dir, d); + } + } + + return true; +} diff --git a/src/addon/validation.hpp b/src/addon/validation.hpp index 5a476bf5e7fc..577f93778dca 100644 --- a/src/addon/validation.hpp +++ b/src/addon/validation.hpp @@ -96,3 +96,8 @@ bool check_case_insensitive_duplicates(const config& dir, std::vector @@ -153,20 +153,6 @@ void add_license(config& cfg) copying["contents"] = contents; } -static const std::string file_hash(const config& file) -{ - std::string hash = file["hash"].str(); - if(hash.empty()) { - hash = utils::md5(file["contents"].str()).base64_digest(); - } - return hash; -} - -static bool comp_file_hash(const config& file_a, const config& file_b) -{ - return file_hash(file_a) == file_hash(file_b); -} - //! Surround with [dir][/dir] static void write_difference(config& pack, const config& from, const config& to, bool with_content) { diff --git a/src/server/campaignd/server.cpp b/src/server/campaignd/server.cpp index a15c3654a821..77106ecf0965 100644 --- a/src/server/campaignd/server.cpp +++ b/src/server/campaignd/server.cpp @@ -275,6 +275,14 @@ void server::load_config() writer.write(data); campaign_file.commit(); } + { + filesystem::atomic_commit campaign_hash_file(addon_file + version_cfg["filename"].str() + ".hash"); + config_writer writer(*campaign_hash_file.ostream(), true, compress_level_); + config data_hash = config("name", ""); + write_hashlist(data_hash, data); + writer.write(data_hash); + campaign_hash_file.commit(); + } addons_.emplace(addon_id, campaign); dirty_addons_.emplace(addon_id); @@ -1073,7 +1081,6 @@ void server::handle_upload(const server::request& req) add_license(data); const std::string& new_version = (*campaign)["version"].str(); - //#TODO: probably make hash from new_version + required_wesnoth_version ? const std::string& file_hash = utils::md5(new_version).hex_digest(); //sorted version map @@ -1099,6 +1106,15 @@ void server::handle_upload(const server::request& req) writer.write(data); campaign_file.commit(); } + // Let's write its hashes + { + filesystem::atomic_commit campaign_hash_file(filename + version_cfg["filename"].str() + ".hash"); + config_writer writer(*campaign_hash_file.ostream(), true, compress_level_); + config data_hash = config("name", ""); + write_hashlist(data_hash, data); + writer.write(data_hash); + campaign_hash_file.commit(); + } (*campaign)["size"] = filesystem::file_size(filename + version_cfg["filename"].str());