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();
}
}