diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp index 5a699ecbbfd..65ef0cbdb2b 100644 --- a/src/corelib/io/qurl.cpp +++ b/src/corelib/io/qurl.cpp @@ -748,32 +748,6 @@ static const ushort * const pathInIsolation = userNameInIsolation + 5; static const ushort * const queryInIsolation = userNameInIsolation + 6; static const ushort * const fragmentInIsolation = userNameInIsolation + 7; -static const ushort localPathFromUser[] = { - // we force-decode some of the gen-delims, because - // pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - // the gen-delim lines are leave() in qt_urlRecode, so we don't need to - // repeat them if we want to keep them decoded - // decode(':'), // allowed - // decode('@'), // allowed - encode(']'), - encode('['), - // decode('/'), // special and allowed - // decode('?'), // handled by path() and others - // decode('#'), // ditto - - // the rest is like pathInIsolation above - decode('"'), - decode('<'), - decode('>'), - decode('^'), - decode('\\'), - decode('|'), - decode('{'), - decode('}'), - - 0 -}; - static const ushort userNameInUserInfo[] = { encode(':'), // 0 decode('@'), // 1 @@ -833,9 +807,11 @@ static const ushort * const pathInUrl = userNameInUrl + 5; static const ushort * const queryInUrl = userNameInUrl + 6; static const ushort * const fragmentInUrl = userNameInUrl + 6; -static inline void parseDecodedComponent(QString &data) +static inline void parseDecodedComponent(QString &data, QUrlPrivate::Section section) { data.replace(u'%', "%25"_L1); + if (section != QUrlPrivate::Host) + data.replace(u'[', "%5B"_L1).replace(u']', "%5D"_L1); } static inline QString @@ -2135,7 +2111,7 @@ void QUrl::setUserName(const QString &userName, ParsingMode mode) QString data = userName; if (mode == DecodedMode) { - parseDecodedComponent(data); + parseDecodedComponent(data, QUrlPrivate::UserName); mode = TolerantMode; } @@ -2198,7 +2174,7 @@ void QUrl::setPassword(const QString &password, ParsingMode mode) QString data = password; if (mode == DecodedMode) { - parseDecodedComponent(data); + parseDecodedComponent(data, QUrlPrivate::Password); mode = TolerantMode; } @@ -2260,7 +2236,7 @@ void QUrl::setHost(const QString &host, ParsingMode mode) QString data = host; if (mode == DecodedMode) { - parseDecodedComponent(data); + parseDecodedComponent(data, QUrlPrivate::Host); mode = TolerantMode; } @@ -2385,7 +2361,7 @@ void QUrl::setPath(const QString &path, ParsingMode mode) QString data = path; if (mode == DecodedMode) { - parseDecodedComponent(data); + parseDecodedComponent(data, QUrlPrivate::Path); mode = TolerantMode; } @@ -2521,7 +2497,7 @@ void QUrl::setQuery(const QString &query, ParsingMode mode) QString data = query; if (mode == DecodedMode) { - parseDecodedComponent(data); + parseDecodedComponent(data, QUrlPrivate::Query); mode = TolerantMode; } @@ -2619,7 +2595,7 @@ void QUrl::setFragment(const QString &fragment, ParsingMode mode) QString data = fragment; if (mode == DecodedMode) { - parseDecodedComponent(data); + parseDecodedComponent(data, QUrlPrivate::Fragment); mode = TolerantMode; } @@ -3386,11 +3362,7 @@ QUrl QUrl::fromLocalFile(const QString &localFile) } url.setScheme(scheme); - - // not directly using setPath here, as we do a few more transforms - parseDecodedComponent(deslashified); - if (!qt_urlRecode(url.d->path, deslashified, {}, localPathFromUser)) - url.d->path = std::move(deslashified); + url.setPath(deslashified, DecodedMode); return url; } diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index fbc2ec9df1c..db3efe467db 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -1422,6 +1422,11 @@ void tst_QUrl::toLocalFile() url.setPath(url.path(QUrl::PrettyDecoded), QUrl::TolerantMode); QCOMPARE(url.toLocalFile(), theFile); QCOMPARE(url.isLocalFile(), !theFile.isEmpty()); + + // local file paths can be fully decoded without loss + url.setPath(url.path()); + QCOMPARE(url.toLocalFile(), theFile); + QCOMPARE(url.isLocalFile(), !theFile.isEmpty()); } void tst_QUrl::fromLocalFile_data() @@ -1508,6 +1513,11 @@ void tst_QUrl::fromLocalFile() url.setPath(url.path(QUrl::PrettyDecoded), QUrl::TolerantMode); QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl); QCOMPARE(url.path(), thePath); + + // local file paths can be fully decoded without loss + url.setPath(url.path()); + QCOMPARE(url.toString(QUrl::DecodeReserved), theUrl); + QCOMPARE(url.path(), thePath); } void tst_QUrl::fromLocalFileNormalize_data() @@ -4150,24 +4160,25 @@ void tst_QUrl::setComponents_data() << int(Scheme) << "http%61" << Decoded << false << PrettyDecoded << "" << ""; QTest::newRow("username-encode") << QUrl("http://example.com") - << int(UserName) << "h%61llo:world" << Decoded << true - << PrettyDecoded << "h%2561llo:world" << "http://h%2561llo%3Aworld@example.com"; + << int(UserName) << "h%61llo[:]world" << Decoded << true + << PrettyDecoded << "h%2561llo[:]world" << "http://h%2561llo%5B%3A%5Dworld@example.com"; QTest::newRow("password-encode") << QUrl("http://example.com") - << int(Password) << "h%61llo:world@" << Decoded << true - << PrettyDecoded << "h%2561llo:world@" << "http://:h%2561llo:world%40@example.com"; + << int(Password) << "h%61llo[:]world@" << Decoded << true + << PrettyDecoded << "h%2561llo[:]world@" << "http://:h%2561llo%5B:%5Dworld%40@example.com"; // '%' characters are not permitted in the hostname, these test that it fails to set anything QTest::newRow("invalid-host-encode") << QUrl("http://example.com") << int(Host) << "ex%61mple.com" << Decoded << false << PrettyDecoded << QString() << QString(); + // square brackets are force-encoded from decoded forms in the path, query, and fragment QTest::newRow("path-encode") << QUrl("http://example.com/foo") - << int(Path) << "/bar%23" << Decoded << true - << PrettyDecoded << "/bar%2523" << "http://example.com/bar%2523"; + << int(Path) << "/ba[r]%23" << Decoded << true + << PrettyDecoded << "/ba%5Br%5D%2523" << "http://example.com/ba%5Br%5D%2523"; QTest::newRow("query-encode") << QUrl("http://example.com/foo?q") - << int(Query) << "bar%23" << Decoded << true - << PrettyDecoded << "bar%2523" << "http://example.com/foo?bar%2523"; + << int(Query) << "ba[r]%23" << Decoded << true + << PrettyDecoded << "ba%5Br%5D%2523" << "http://example.com/foo?ba%5Br%5D%2523"; QTest::newRow("fragment-encode") << QUrl("http://example.com/foo#z") - << int(Fragment) << "bar%23" << Decoded << true - << PrettyDecoded << "bar%2523" << "http://example.com/foo#bar%2523"; + << int(Fragment) << "ba[r]%23" << Decoded << true + << PrettyDecoded << "ba%5Br%5D%2523" << "http://example.com/foo#ba%5Br%5D%2523"; // force decoding QTest::newRow("username-decode") << QUrl("http://example.com") << int(UserName) << "hello%3Aworld%25" << Tolerant << true @@ -4176,8 +4187,8 @@ void tst_QUrl::setComponents_data() << int(Password) << "}}>b9o%25kR(" << Tolerant << true << FullyDecoded << "}}>b9o%kR(" << "http://:%7D%7D%3Eb9o%25kR(@example.com"; QTest::newRow("path-decode") << QUrl("http://example.com/") - << int(Path) << "/bar%25foo" << Tolerant << true - << FullyDecoded << "/bar%foo" << "http://example.com/bar%25foo"; + << int(Path) << "/bar%25[foo]" << Tolerant << true + << FullyDecoded << "/bar%[foo]" << "http://example.com/bar%25[foo]"; QTest::newRow("query-decode") << QUrl("http://example.com/foo?qq") << int(Query) << "bar%25foo" << Tolerant << true << FullyDecoded << "bar%foo" << "http://example.com/foo?bar%25foo";