diff --git a/src/addon/client.cpp b/src/addon/client.cpp index ac38f50eabf4..5daf8b595004 100644 --- a/src/addon/client.cpp +++ b/src/addon/client.cpp @@ -134,7 +134,14 @@ bool addons_client::upload_addon(const std::string& id, std::string& response_me cfg["name"] = id; config addon_data; - archive_addon(id, addon_data); + try { + archive_addon(id, addon_data); + } catch(utf8::invalid_utf8_exception&){ + this->last_error_ = + vgettext("The add-on $addon_title has a file or directory " + "containing invalid characters and cannot be published.", i18n_symbols); + return false; + } config request_buf, response_buf; request_buf.add_child("upload", cfg).add_child("data", addon_data); diff --git a/src/addon/validation.cpp b/src/addon/validation.cpp index 1564f4681da8..4a293119cf2e 100644 --- a/src/addon/validation.cpp +++ b/src/addon/validation.cpp @@ -15,6 +15,7 @@ #include "addon/validation.hpp" #include "config.hpp" +#include "serialization/unicode_cast.hpp" #include @@ -44,13 +45,40 @@ namespace { } } }; + + struct addon_filename_ucs4char_illegal + { + inline bool operator()(ucs4::char_t c) const + { + switch(c){ + case ' ': + case '"': + case '*': + case '/': + case ':': + case '<': + case '>': + case '?': + case '\\': + case '|': + case '~': + case 0x7F: // DEL + return true; + default: + return ( + c < 0x20 || // C0 control characters + (c >= 0x80 && c < 0xA0) || // C1 control characters + (c >= 0xD800 && c < 0xE000) // surrogate pairs + ); + } + } + }; } bool addon_name_legal(const std::string& name) { - if(name.empty() || name == "." || - std::find_if(name.begin(), name.end(), addon_name_char_illegal()) != name.end() || - name.find("..") != std::string::npos) { + if(name.empty() || + std::find_if(name.begin(), name.end(), addon_name_char_illegal()) != name.end()) { return false; } else { return true; @@ -59,12 +87,17 @@ bool addon_name_legal(const std::string& name) bool addon_filename_legal(const std::string& name) { - if(name.empty() || name == "." || - name.find_first_of("/:\\~ \r\n\v\t") != std::string::npos || - name.find("..") != std::string::npos) { + if(name.empty() || name.back() == '.' || + name.find("..") != std::string::npos || + name.size() > 255) { return false; } else { - return true; + const ucs4::string name_ucs4 = unicode_cast(name); + const std::string name_utf8 = unicode_cast(name_ucs4); + if(name != name_utf8){ // name is invalid UTF-8 + return false; + } + return std::find_if(name_ucs4.begin(), name_ucs4.end(), addon_filename_ucs4char_illegal()) == name_ucs4.end(); } }