diff --git a/src/filesystem.hpp b/src/filesystem.hpp index 9a6c110cce8..dba1b8a7dcb 100644 --- a/src/filesystem.hpp +++ b/src/filesystem.hpp @@ -89,6 +89,8 @@ public: directory_patterns_.push_back(pattern); } + void remove_blacklisted_files_and_dirs(std::vector& files, std::vector& directories) const; + private: std::vector file_patterns_; std::vector directory_patterns_; @@ -97,7 +99,7 @@ private: static const blacklist_pattern_list default_blacklist{ { /* Blacklist dot-files/dirs, which are hidden files in UNIX platforms */ - ".*", + ".+", "#*#", "*~", "*-bak", @@ -122,7 +124,7 @@ static const blacklist_pattern_list default_blacklist{ "*.project", }, { - ".*", + ".+", /* macOS metadata-like cruft (http://floatingsun.net/2007/02/07/whats-with-__macosx-in-zip-files/) */ "__MACOSX", } diff --git a/src/filesystem_boost.cpp b/src/filesystem_boost.cpp index c08de788230..3a03d1ddb19 100644 --- a/src/filesystem_boost.cpp +++ b/src/filesystem_boost.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #ifdef _WIN32 @@ -1135,27 +1136,34 @@ void clear_binary_paths_cache() binary_paths_cache.clear(); } -static bool is_legal_file(const std::string &filename) +static bool is_legal_file(const std::string &filename_str) { - DBG_FS << "Looking for '" << filename << "'.\n"; + DBG_FS << "Looking for '" << filename_str << "'.\n"; - if (filename.empty()) { + if(filename_str.empty()) { LOG_FS << " invalid filename\n"; return false; } - if (filename.find("..") != std::string::npos) { - ERR_FS << "Illegal path '" << filename << "' (\"..\" not allowed).\n"; + if(filename_str.find("..") != std::string::npos) { + ERR_FS << "Illegal path '" << filename_str << "' (\"..\" not allowed).\n"; return false; } - if (filename.find('\\') != std::string::npos) { - ERR_FS << "Illegal path '" << filename << R"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl; + if(filename_str.find('\\') != std::string::npos) { + ERR_FS << "Illegal path '" << filename_str << R"end(' ("\" not allowed, for compatibility with GNU/Linux and macOS).)end" << std::endl; return false; } - if (looks_like_pbl(filename)) { - ERR_FS << "Illegal path '" << filename << "' (.pbl files are not allowed)." << std::endl; + path filepath(filename_str); + + if(default_blacklist.match_file(filepath.filename().string())) { + ERR_FS << "Illegal path '" << filename_str << "' (blacklisted filename)." << std::endl; + return false; + } + + if(std::any_of(filepath.begin(), filepath.end(), [](const path& dirname) { return default_blacklist.match_dir(dirname.string()); })) { + ERR_FS << "Illegal path '" << filename_str << "' (blacklisted directory name)." << std::endl; return false; } diff --git a/src/filesystem_common.cpp b/src/filesystem_common.cpp index f7ee6bf37b3..ff767d5bbba 100644 --- a/src/filesystem_common.cpp +++ b/src/filesystem_common.cpp @@ -29,6 +29,16 @@ static lg::log_domain log_filesystem("filesystem"); namespace filesystem { +void blacklist_pattern_list::remove_blacklisted_files_and_dirs(std::vector& files, std::vector& directories) const +{ + files.erase( + std::remove_if(files.begin(), files.end(), [this](const std::string& name) { return match_file(name); }), + files.end()); + directories.erase( + std::remove_if(directories.begin(), directories.end(), [this](const std::string& name) { return match_dir(name); }), + directories.end()); +} + std::string get_prefs_file() { return get_user_config_dir() + "/preferences"; diff --git a/src/scripting/lua_fileops.cpp b/src/scripting/lua_fileops.cpp index 641e2ce8558..0016df0d202 100644 --- a/src/scripting/lua_fileops.cpp +++ b/src/scripting/lua_fileops.cpp @@ -21,6 +21,7 @@ #include "scripting/lua_common.hpp" // for chat_message, luaW_pcall #include "scripting/push_check.hpp" +#include #include #include @@ -149,6 +150,7 @@ int intf_read_file(lua_State *L) if(filesystem::is_directory(p)) { std::vector files, dirs; filesystem::get_files_in_dir(p, &files, &dirs); + filesystem::default_blacklist.remove_blacklisted_files_and_dirs(files, dirs); size_t ndirs = dirs.size(); std::copy(files.begin(), files.end(), std::back_inserter(dirs)); lua_push(L, dirs); diff --git a/src/serialization/string_utils.cpp b/src/serialization/string_utils.cpp index 7a6ad091156..5ca13b0284e 100644 --- a/src/serialization/string_utils.cpp +++ b/src/serialization/string_utils.cpp @@ -701,17 +701,23 @@ bool word_match(const std::string& message, const std::string& word) { } bool wildcard_string_match(const std::string& str, const std::string& match) { - const bool wild_matching = (!match.empty() && match[0] == '*'); - const std::string::size_type solid_begin = match.find_first_not_of('*'); + const bool wild_matching = (!match.empty() && (match[0] == '*' || match[0] == '+')); + const std::string::size_type solid_begin = match.find_first_not_of("*+"); const bool have_solids = (solid_begin != std::string::npos); - // Check the simple case first - if(str.empty() || !have_solids) { - return wild_matching || str == match; + // Check the simple cases first + if(!have_solids) { + const std::string::size_type plus_count = std::count(match.begin(), match.end(), '+'); + return match.empty() ? str.empty() : str.length() >= plus_count; + } else if(str.empty()) { + return false; } - const std::string::size_type solid_end = match.find_first_of('*', solid_begin); + + const std::string::size_type solid_end = match.find_first_of("*+", solid_begin); const std::string::size_type solid_len = (solid_end == std::string::npos) ? match.length() - solid_begin : solid_end - solid_begin; - std::string::size_type current = 0; + // Since + always consumes at least one character, increment current if the match + // begins with one + std::string::size_type current = match[0] == '+' ? 1 : 0; bool matches; do { matches = true; diff --git a/src/serialization/string_utils.hpp b/src/serialization/string_utils.hpp index 189d852f18f..18800b24200 100644 --- a/src/serialization/string_utils.hpp +++ b/src/serialization/string_utils.hpp @@ -321,8 +321,8 @@ bool word_completion(std::string& text, std::vector& wordlist); bool word_match(const std::string& message, const std::string& word); /** - * Match using '*' as any number of characters (including none), and '?' as any - * one character. + * Match using '*' as any number of characters (including none), + * '+' as one or more characters, and '?' as any one character. */ bool wildcard_string_match(const std::string& str, const std::string& match); diff --git a/src/tests/test_serialization.cpp b/src/tests/test_serialization.cpp index df375f0172d..739c8310169 100644 --- a/src/tests/test_serialization.cpp +++ b/src/tests/test_serialization.cpp @@ -106,12 +106,16 @@ BOOST_AUTO_TEST_CASE( test_wildcard_string_match ) const std::string str = "foo bar baz"; BOOST_CHECK(utils::wildcard_string_match(str, "*bar*")); + BOOST_CHECK(utils::wildcard_string_match(str, "+bar+")); BOOST_CHECK(!utils::wildcard_string_match(str, "*BAR*")); + BOOST_CHECK(!utils::wildcard_string_match(str, "+BAR+")); BOOST_CHECK(!utils::wildcard_string_match(str, "bar")); BOOST_CHECK(utils::wildcard_string_match(str, "*ba? b*")); + BOOST_CHECK(utils::wildcard_string_match(str, "+ba? b+")); BOOST_CHECK(utils::wildcard_string_match(str, "*?a?*")); + BOOST_CHECK(utils::wildcard_string_match(str, "+?a?+")); BOOST_CHECK(!utils::wildcard_string_match(str, "foo? ")); BOOST_CHECK(!utils::wildcard_string_match(str, "?foo")); @@ -126,11 +130,19 @@ BOOST_AUTO_TEST_CASE( test_wildcard_string_match ) BOOST_CHECK(utils::wildcard_string_match(str, superfluous_mask)); BOOST_CHECK(utils::wildcard_string_match(str, superfluous_mask + '*')); + superfluous_mask = std::string(str.length(), '+'); + BOOST_CHECK(utils::wildcard_string_match(str, superfluous_mask)); + BOOST_CHECK(!utils::wildcard_string_match(str, superfluous_mask + '+')); + BOOST_CHECK(utils::wildcard_string_match("", "")); BOOST_CHECK(!utils::wildcard_string_match(str, "")); BOOST_CHECK(utils::wildcard_string_match("", "*")); - BOOST_CHECK(utils::wildcard_string_match("", "***?**")); + BOOST_CHECK(utils::wildcard_string_match("", "***")); + BOOST_CHECK(!utils::wildcard_string_match("", "+")); + BOOST_CHECK(!utils::wildcard_string_match("", "*bar")); + BOOST_CHECK(!utils::wildcard_string_match("", "***?**")); + BOOST_CHECK(!utils::wildcard_string_match("", "+++?++")); BOOST_CHECK(!utils::wildcard_string_match("", "?")); BOOST_CHECK(!utils::wildcard_string_match("", "???")); }