Skip to content

Commit

Permalink
Add filename listing for case conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
AI0867 authored and GregoryLundberg committed Nov 30, 2017
1 parent daddedb commit e0f5772
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 20 deletions.
49 changes: 34 additions & 15 deletions src/addon/validation.cpp
Expand Up @@ -144,6 +144,38 @@ bool check_names_legal_internal(const config& dir, std::string current_prefix, s
return badlist ? badlist->empty() : true;
}

bool check_case_insensitive_duplicates_internal(const config& dir, std::string prefix, std::vector<std::string>* badlist){
std::set<std::string> filenames;
bool inserted;
for (const config &path : dir.child_range("file")) {
const config::attribute_value &filename = path["name"];
std::tie(std::ignore, inserted) = filenames.insert(boost::algorithm::to_lower_copy(filename.str(), std::locale::classic()));
if (!inserted){
if(badlist){
badlist->push_back(prefix + filename.str());
} else {
return false;
}
}
}
for (const config &path : dir.child_range("dir")) {
const config::attribute_value &filename = path["name"];
std::tie(std::ignore, inserted) = filenames.insert(boost::algorithm::to_lower_copy(filename.str(), std::locale::classic()));
if (!inserted) {
if(badlist){
badlist->push_back(prefix + filename.str());
} else {
return false;
}
}
if (!check_case_insensitive_duplicates_internal(path, prefix + filename + "/", badlist) && !badlist){
return false;
}
}

return badlist ? badlist->empty() : true;
}

} // end unnamed namespace 3

bool check_names_legal(const config& dir, std::vector<std::string>* badlist)
Expand All @@ -155,21 +187,8 @@ bool check_names_legal(const config& dir, std::vector<std::string>* badlist)
return check_names_legal_internal(dir, "", badlist);
}

bool check_case_insensitive_duplicates(const config& dir){
std::set<std::string> filenames;
bool inserted;
for (const config &path : dir.child_range("file")) {
const config::attribute_value &filename = path["name"];
std::tie(std::ignore, inserted) = filenames.insert(boost::algorithm::to_lower_copy(filename.str(), std::locale::classic()));
if (!inserted) return false;
}
for (const config &path : dir.child_range("dir")) {
const config::attribute_value &filename = path["name"];
std::tie(std::ignore, inserted) = filenames.insert(boost::algorithm::to_lower_copy(filename.str(), std::locale::classic()));
if (!inserted) return false;
if (!check_case_insensitive_duplicates(path)) return false;
}
return true;
bool check_case_insensitive_duplicates(const config& dir, std::vector<std::string>* badlist){
return check_case_insensitive_duplicates_internal(dir, "", badlist);
}

ADDON_TYPE get_addon_type(const std::string& str)
Expand Down
17 changes: 15 additions & 2 deletions src/addon/validation.hpp
Expand Up @@ -77,8 +77,21 @@ bool addon_filename_legal(const std::string& name);
* @returns True if no illegal names were found.
*/
bool check_names_legal(const config& dir, std::vector<std::string>* badlist = nullptr);
/** Probes an add-on archive for case-conflicts on case-insensitive filesystems. */
bool check_case_insensitive_duplicates(const config& dir);
/**
* Scans an add-on archive for case-conflicts.
*
* Case conflicts may cause trouble on case-insensitive filesystems.
*
* @param dir The WML container for the root [dir] node where the search
* should begin.
* @param badlist If provided and not null, any case conflicts encountered will
* be added to this list. This also makes the archive scan more
* thorough instead of returning on the first conflict
* encountered.
*
* @returns True if no conflicts were found.
*/
bool check_case_insensitive_duplicates(const config& dir, std::vector<std::string>* badlist = nullptr);

std::string encode_binary(const std::string& str);
std::string unencode_binary(const std::string& str);
Expand Down
10 changes: 7 additions & 3 deletions src/campaign_server/campaign_server.cpp
Expand Up @@ -693,9 +693,13 @@ void server::handle_upload(const server::request& req)
"File or directory names may not contain whitespace, control characters or any of the following characters: '\" * / : < > ? \\ | ~'. "
"It also may not contain '..' end with '.' or be longer than 255 characters.",
filelist, req.sock);
} else if(!check_case_insensitive_duplicates(data)) {
LOG_CS << "Upload aborted - case conflict in add-on data.\n";
send_error("Add-on rejected: Two files have a case conflict", req.sock);
} else if(!check_case_insensitive_duplicates(data, &badnames)) {
const std::string& filelist = utils::join(badnames, "\n");
LOG_CS << "Upload aborted - case conflict in add-on data (" << badnames.size() << " entries).\n";
send_error(
"Add-on rejected: The add-on contains files or directories with case conflicts. "
"File or directory names may not be differently-cased versions of the same string.",
filelist, req.sock);
} else if(campaign && !authenticate(*campaign, upload["passphrase"])) {
LOG_CS << "Upload aborted - incorrect passphrase.\n";
send_error("Add-on rejected: The add-on already exists, and your passphrase was incorrect.", req.sock);
Expand Down

0 comments on commit e0f5772

Please sign in to comment.