diff options
author | Joni Poikelin <joni.poikelin@qt.io> | 2018-12-11 11:42:34 +0200 |
---|---|---|
committer | Joni Poikelin <joni.poikelin@qt.io> | 2018-12-13 05:23:12 +0000 |
commit | eaf4438b3511c8380b9b691b656a87a60e342e29 (patch) | |
tree | 7b853fef8105d7250a3cc349dc3615cbd798cec2 /src/corelib/io/qdir.cpp | |
parent | bc997b856aa7b0b2137b568cd3fc6f190cd89f84 (diff) |
Make url normalization closer to common browser behavior
Firefox, Chrome and various http libraries normalize /./ and /../ from
urls, but retain multiple adjacent slashes as is. Qt removes
duplicated slashes which makes it impossible to access some web
resources that rely on those.
Fixes: QTBUG-71973
Change-Id: Ie18ae6ad3264acb252fcd87a754726a8c546e5ec
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Diffstat (limited to 'src/corelib/io/qdir.cpp')
-rw-r--r-- | src/corelib/io/qdir.cpp | 64 |
1 files changed, 54 insertions, 10 deletions
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp index 7df461ddce..405718aba8 100644 --- a/src/corelib/io/qdir.cpp +++ b/src/corelib/io/qdir.cpp @@ -2161,9 +2161,10 @@ bool QDir::match(const QString &filter, const QString &fileName) This method is shared with QUrl, so it doesn't deal with QDir::separator(), nor does it remove the trailing slash, if any. */ -Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool allowUncPaths, - bool *ok = nullptr) +QString qt_normalizePathSegments(const QString &name, QDirPrivate::PathNormalizations flags, bool *ok) { + const bool allowUncPaths = QDirPrivate::AllowUncPaths & flags; + const bool isRemote = QDirPrivate::RemotePath & flags; const int len = name.length(); if (ok) @@ -2185,14 +2186,30 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all i -= prefixLength; // replicate trailing slash (i > 0 checks for emptiness of input string p) - if (i > 0 && p[i] == '/') { + // except for remote paths because there can be /../ or /./ ending + if (i > 0 && p[i] == '/' && !isRemote) { out[--used] = '/'; --i; } + auto isDot = [](const ushort *p, int i) { + return i > 1 && p[i - 1] == '.' && p[i - 2] == '/'; + }; + auto isDotDot = [](const ushort *p, int i) { + return i > 2 && p[i - 1] == '.' && p[i - 2] == '.' && p[i - 3] == '/'; + }; + while (i >= 0) { - // remove trailing slashes + // copy trailing slashes for remote urls if (p[i] == '/') { + if (isRemote && !up) { + if (isDot(p, i)) { + i -= 2; + continue; + } + out[--used] = p[i]; + } + --i; continue; } @@ -2204,10 +2221,17 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all } // detect up dir - if (i >= 1 && p[i] == '.' && p[i-1] == '.' - && (i == 1 || (i >= 2 && p[i-2] == '/'))) { + if (i >= 1 && p[i] == '.' && p[i-1] == '.' && (i < 2 || p[i - 2] == '/')) { ++up; - i -= 2; + i -= i >= 2 ? 3 : 2; + + if (isRemote) { + // moving up should consider empty path segments too (/path//../ -> /path/) + while (i > 0 && up && p[i] == '/') { + --up; + --i; + } + } continue; } @@ -2217,7 +2241,27 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all // skip or copy while (i >= 0) { - if (p[i] == '/') { // do not copy slashes + if (p[i] == '/') { + // copy all slashes as is for remote urls if they are not part of /./ or /../ + if (isRemote && !up) { + while (i > 0 && p[i] == '/' && !isDotDot(p, i)) { + + if (isDot(p, i)) { + i -= 2; + continue; + } + + out[--used] = p[i]; + --i; + } + + // in case of /./, jump over + if (isDot(p, i)) + i -= 2; + + break; + } + --i; break; } @@ -2238,7 +2282,7 @@ Q_AUTOTEST_EXPORT QString qt_normalizePathSegments(const QString &name, bool all *ok = prefixLength == 0 || up == 0; // add remaining '..' - while (up) { + while (up && !isRemote) { if (used != len && out[used] != '/') // is not empty and there isn't already a '/' out[--used] = '/'; out[--used] = '.'; @@ -2284,7 +2328,7 @@ static QString qt_cleanPath(const QString &path, bool *ok) if (dir_separator != QLatin1Char('/')) name.replace(dir_separator, QLatin1Char('/')); - QString ret = qt_normalizePathSegments(name, OSSupportsUncPaths, ok); + QString ret = qt_normalizePathSegments(name, OSSupportsUncPaths ? QDirPrivate::AllowUncPaths : QDirPrivate::DefaultNormalization, ok); // Strip away last slash except for root directories if (ret.length() > 1 && ret.endsWith(QLatin1Char('/'))) { |