# HG changeset patch # User Sebastien Jodogne # Date 1772360514 -3600 # Node ID f30191e9ffbf978ee51e961af99a5ac54368b459 # Parent 29388610f3e2670426dfba91388f26f903ec2315 dealing with slashes in Toolbox::NormalizePath() diff -r 29388610f3e2 -r f30191e9ffbf OrthancFramework/Sources/Compression/HierarchicalZipWriter.cpp --- a/OrthancFramework/Sources/Compression/HierarchicalZipWriter.cpp Fri Feb 27 18:37:52 2026 +0100 +++ b/OrthancFramework/Sources/Compression/HierarchicalZipWriter.cpp Sun Mar 01 11:21:54 2026 +0100 @@ -51,12 +51,9 @@ std::string HierarchicalZipWriter::Index::EnsureUniqueFilename(const std::string& filename, bool allowUtf8) { - const std::string standardized = Toolbox::NormalizePath(filename, allowUtf8); + const std::string standardized = Toolbox::NormalizePath(filename, allowUtf8, false /* replace (back)slashes by spaces */); - if (standardized.find('/') != std::string::npos) - { - throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange, "Your filename must not contain slashes or backslashes"); - } + assert(standardized.find('\\') == std::string::npos); Directory& d = *stack_.back(); Directory::Content::iterator it = d.content_.find(standardized); diff -r 29388610f3e2 -r f30191e9ffbf OrthancFramework/Sources/Compression/ZipWriter.cpp --- a/OrthancFramework/Sources/Compression/ZipWriter.cpp Fri Feb 27 18:37:52 2026 +0100 +++ b/OrthancFramework/Sources/Compression/ZipWriter.cpp Sun Mar 01 11:21:54 2026 +0100 @@ -609,7 +609,7 @@ { Open(); - const std::string normalized = Toolbox::NormalizePath(filename, allowUtf8_); + const std::string normalized = Toolbox::NormalizePath(filename, allowUtf8_, true /* allow slashes, necessary for subdirectories */); zip_fileinfo zfi; PrepareFileInfo(zfi); diff -r 29388610f3e2 -r f30191e9ffbf OrthancFramework/Sources/Toolbox.cpp --- a/OrthancFramework/Sources/Toolbox.cpp Fri Feb 27 18:37:52 2026 +0100 +++ b/OrthancFramework/Sources/Toolbox.cpp Sun Mar 01 11:21:54 2026 +0100 @@ -2959,7 +2959,8 @@ std::string Toolbox::NormalizePath(const std::string& utf8, - bool allowUtf8) + bool allowUtf8, + bool allowSlashes) { std::string converted; @@ -2973,14 +2974,13 @@ converted = Toolbox::ConvertToAscii(utf8); } - bool previousSpace = true; - std::string result; result.reserve(converted.size()); for (size_t i = 0; i < converted.size(); i++) { unsigned char c = converted[i]; + bool allowRepetitions = true; if (isspace(c) || c <= 31 || // Control characters @@ -2994,21 +2994,33 @@ c == '>' || c == '|') { - if (!previousSpace) + // This is a space character: Avoid their repetition + allowRepetitions = false; + c = ' '; + } + else if (c == '/' || + c == '\\') + { + // This is a slash or backslash: Prevent their repetition + allowRepetitions = false; + if (allowSlashes) { - previousSpace = true; - result.push_back(' '); + c = '/'; // Normalize backslashes as forward slashes + } + else + { + c = ' '; // Slashes are not allowed: Replace by spaces } } + + if (allowRepetitions) + { + result.push_back(c); + } else { - previousSpace = false; - - if (c == '\\') - { - result.push_back('/'); // Replace backslashes by forward slashes - } - else + if (result.empty() || + result.back() != c) { result.push_back(c); } diff -r 29388610f3e2 -r f30191e9ffbf OrthancFramework/Sources/Toolbox.h --- a/OrthancFramework/Sources/Toolbox.h Fri Feb 27 18:37:52 2026 +0100 +++ b/OrthancFramework/Sources/Toolbox.h Sun Mar 01 11:21:54 2026 +0100 @@ -408,7 +408,8 @@ static bool IsValidUtf8(const std::string& s); static std::string NormalizePath(const std::string& utf8, - bool allowUtf8); + bool allowUtf8, + bool allowSlashes); class ORTHANC_PUBLIC ElapsedTimer : public boost::noncopyable { diff -r 29388610f3e2 -r f30191e9ffbf OrthancFramework/UnitTestsSources/ZipTests.cpp --- a/OrthancFramework/UnitTestsSources/ZipTests.cpp Fri Feb 27 18:37:52 2026 +0100 +++ b/OrthancFramework/UnitTestsSources/ZipTests.cpp Sun Mar 01 11:21:54 2026 +0100 @@ -111,23 +111,25 @@ ASSERT_EQ("hello-3", i.OpenFile("hello", true)); ASSERT_EQ("trE hell", i.OpenFile(" ÊtrE hellô ", false)); ASSERT_EQ("ÊtrE hellô", i.OpenFile(" ÊtrE hellô ", true)); - ASSERT_THROW(i.OpenFile("hel/lo", true), OrthancException); - ASSERT_THROW(i.OpenFile("hel\\lo", true), OrthancException); + ASSERT_EQ("hel^lo", i.OpenFile("hel^lo", true)); + ASSERT_EQ("hel lo", i.OpenFile("hel////lo", true)); + ASSERT_EQ("hel lo-2", i.OpenFile("hel\\\\/\\/\\lo", true)); i.OpenDirectory("coucou", true); ASSERT_EQ("coucou-2/world", i.OpenFile("world", true)); ASSERT_EQ("coucou-2/world-2", i.OpenFile("world", true)); - ASSERT_THROW(i.OpenDirectory("hel/lo", true), OrthancException); - ASSERT_THROW(i.OpenDirectory("hel\\lo", true), OrthancException); + i.OpenDirectory("world", true); + i.OpenDirectory("hel///lo", true); + i.OpenDirectory("hel\\\\/\\/\\lo", true); - i.OpenDirectory("world", true); - - ASSERT_EQ("coucou-2/world-3/hello", i.OpenFile("hello", true)); - ASSERT_EQ("coucou-2/world-3/hello-2", i.OpenFile("hello", true)); + ASSERT_EQ("coucou-2/world-3/hel lo/hel lo/hello", i.OpenFile("hello", true)); + ASSERT_EQ("coucou-2/world-3/hel lo/hel lo/hello-2", i.OpenFile("hello", true)); i.CloseDirectory(); + i.CloseDirectory(); + i.CloseDirectory(); ASSERT_EQ("coucou-2/world-4", i.OpenFile("world", true)); @@ -142,9 +144,13 @@ TEST(HierarchicalZipWriter, Filenames) { - ASSERT_EQ("trE hell", Toolbox::NormalizePath(" ÊtrE hellô ", false)); - ASSERT_EQ("ÊtrE hellô", Toolbox::NormalizePath(" ÊtrE hellô ", true)); - ASSERT_EQ("Hel^^ ^ ^^lo world", Toolbox::NormalizePath(" Hel^^ ^\r\n\t^^lo \t ", true)); + ASSERT_EQ("trE h/ell", Toolbox::NormalizePath(" ÊtrE h\\\\/\\/\\ellô ", false, true)); + ASSERT_EQ("ÊtrE h/ellô", Toolbox::NormalizePath(" ÊtrE h\\\\/\\/\\ellô ", true, true)); + ASSERT_EQ("H/el^^ ^ ^^lo world", Toolbox::NormalizePath(" H\\\\/\\/\\el^^ ^\r\n\t^^lo \t ", true, true)); + + ASSERT_EQ("trE h ell", Toolbox::NormalizePath(" ÊtrE h\\\\/\\/\\ellô ", false, false)); + ASSERT_EQ("ÊtrE h ellô", Toolbox::NormalizePath(" ÊtrE h\\\\/\\/\\ellô ", true, false)); + ASSERT_EQ("H el^^ ^ ^^lo world", Toolbox::NormalizePath(" H\\\\/\\/\\el^^ ^\r\n\t^^lo \t ", true, false)); }