}
const ushort *actions = 0;
- if (options & QUrl::DecodeDelimiters)
- actions = decodedActions;
- else
+ if (options & QUrl::EncodeDelimiters)
actions = encodedActions;
+ else
+ actions = decodedActions;
if (!qt_urlRecode(appendTo, value.constData(), value.constEnd(), options, actions))
appendTo += value;
}
-void QUrlPrivate::appendAuthority(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
+inline void QUrlPrivate::appendAuthority(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
{
if ((options & QUrl::RemoveUserInfo) != QUrl::RemoveUserInfo) {
appendUserInfo(appendTo, options, appendingTo);
appendTo += QLatin1Char(':') + QString::number(port);
}
-void QUrlPrivate::appendUserInfo(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
+inline void QUrlPrivate::appendUserInfo(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
{
if (Q_LIKELY(userName.isEmpty() && password.isEmpty()))
return;
const ushort *userNameActions;
const ushort *passwordActions;
- if (options & QUrl::DecodeDelimiters) {
+ if (options & QUrl::EncodeDelimiters) {
+ userNameActions = encodedUserNameActions;
+ passwordActions = encodedPasswordActions;
+ } else {
switch (appendingTo) {
case UserInfo:
userNameActions = decodedUserNameInUserInfoActions;
passwordActions = decodedPasswordInUrlActions;
break;
}
- } else {
- userNameActions = encodedUserNameActions;
- passwordActions = encodedPasswordActions;
}
+ if ((options & QUrl::EncodeReserved) == 0)
+ options |= QUrl::DecodeReserved;
+
if (!qt_urlRecode(appendTo, userName.constData(), userName.constEnd(), options, userNameActions))
appendTo += userName;
if (options & QUrl::RemovePassword || !hasPassword()) {
inline void QUrlPrivate::appendPath(QString &appendTo, QUrl::FormattingOptions options, Section appendingTo) const
{
- if (appendingTo != Path && options & QUrl::DecodeDelimiters) {
+ if (appendingTo != Path && !(options & QUrl::EncodeDelimiters)) {
if (!qt_urlRecode(appendTo, path.constData(), path.constEnd(), options, decodedPathInUrlActions))
appendTo += path;
}
const ushort *actions = 0;
- if (options & QUrl::DecodeDelimiters) {
+ if (options & QUrl::EncodeDelimiters) {
+ actions = encodedQueryActions;
+ } else {
// reset to default qt_urlRecode behaviour (leave delimiters alone)
- options &= ~QUrl::DecodeDelimiters;
+ options |= QUrl::EncodeDelimiters;
actions = appendingTo == Query ? decodedQueryInIsolationActions : decodedQueryInUrlActions;
- } else if ((options & QUrl::DecodeDelimiters) == 0) {
- actions = encodedQueryActions;
}
if (!qt_urlRecode(appendTo, query.constData(), query.constData() + query.length(),
QString output;
const QChar *begin = value.constData() + from;
const QChar *end = value.constData() + iend;
- if (qt_urlRecode(output, begin, end, QUrl::DecodeUnicode | QUrl::DecodeSpaces,
+
+ // leave delimiters alone but decode the rest
+ if (qt_urlRecode(output, begin, end, QUrl::EncodeDelimiters,
decodedQueryInIsolationActions))
query = output;
else
inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions options) const
{
// this is the only flag that matters
- options &= QUrl::DecodeUnicode;
+ options &= QUrl::EncodeUnicode;
if (host.isEmpty())
return;
if (host.at(0).unicode() == '[') {
} else {
// this is either an IPv4Address or a reg-name
// if it is a reg-name, it is already stored in Unicode form
- if (options == QUrl::DecodeUnicode)
- appendTo += host;
- else
+ if (options == QUrl::EncodeUnicode)
appendTo += qt_ACE_do(host, ToAceOnly);
+ else
+ appendTo += host;
}
}
QString QUrl::topLevelDomain(ComponentFormattingOptions options) const
{
QString tld = qTopLevelDomain(host());
- if ((options & DecodeUnicode) == 0) {
+ if (options & EncodeUnicode) {
return qt_ACE_do(tld, ToAceOnly);
}
return tld;
}
QString url;
- if (!options.testFlag(DecodeReserved))
- options &= ~DecodeReserved;
+
+ // for the full URL, we consider that the reserved characters are prettier if encoded
+ if (options & DecodeReserved)
+ options &= ~EncodeReserved;
+ else
+ options |= EncodeReserved;
if (!(options & QUrl::RemoveScheme) && d->hasScheme())
url += d->scheme + QLatin1Char(':');
*/
QByteArray QUrl::toEncoded(FormattingOptions options) const
{
- QString stringForm = toString(options);
- if (options & DecodeUnicode)
- return stringForm.toUtf8();
+ options &= ~DecodeReserved;
+ QString stringForm = toString(options | FullyEncoded);
return stringForm.toLatin1();
}
};
enum ComponentFormattingOption {
- FullyEncoded = 0x000000,
- DecodeSpaces = 0x100000,
- DecodeUnicode = 0x200000,
- DecodeDelimiters = 0x400000 | 0x800000,
- PrettyDecodeReserved = 0x1000000,
- DecodeReserved = PrettyDecodeReserved | 0x2000000,
-
- PrettyDecoded = DecodeSpaces | DecodeDelimiters | PrettyDecodeReserved | DecodeUnicode,
- MostDecoded = PrettyDecoded
+ PrettyDecoded = 0x000000,
+ EncodeSpaces = 0x100000,
+ EncodeUnicode = 0x200000,
+ EncodeDelimiters = 0x400000 | 0x800000,
+ EncodeReserved = 0x1000000,
+ DecodeReserved = 0x2000000,
+
+ FullyEncoded = EncodeSpaces | EncodeUnicode | EncodeDelimiters | EncodeReserved,
+ MostDecoded = PrettyDecoded | DecodeReserved
};
Q_DECLARE_FLAGS(ComponentFormattingOptions, ComponentFormattingOption)
#ifdef qdoc
{ return QIncompatibleFlag(int(f1) | f2); }
// add operators for OR'ing the two types of flags
-inline QUrl::FormattingOptions &operator|=(QUrl::FormattingOptions &i, QUrl::ComponentFormattingOption f)
-{ i |= QUrl::UrlFormattingOption(int(f)); return i; }
inline QUrl::FormattingOptions &operator|=(QUrl::FormattingOptions &i, QUrl::ComponentFormattingOptions f)
{ i |= QUrl::UrlFormattingOption(int(f)); return i; }
Q_DECL_CONSTEXPR inline QUrl::FormattingOptions operator|(QUrl::UrlFormattingOption i, QUrl::ComponentFormattingOption f)
if (idempotentRecodeToUser(encoding))
return input;
- if (encoding & QUrl::DecodeDelimiters) {
+ if (!(encoding & QUrl::EncodeDelimiters)) {
QString output;
if (qt_urlRecode(output, input.constData(), input.constData() + input.length(),
encoding, prettyDecodedActions))
decode('#'), // 3
0
};
- if (encoding & QUrl::DecodeDelimiters) {
- // full decoding: we only encode the characters above
- tableActions[3] = 0;
- } else {
+ if (encoding & QUrl::EncodeDelimiters) {
tableActions[3] = encode('#');
}
if (decoded >= 0x80) {
// decode the UTF-8 sequence
- if (encoding & QUrl::DecodeUnicode &&
+ if (!(encoding & QUrl::EncodeUnicode) &&
encodedUtf8ToUtf16(result, output, begin, input, end, decoded))
continue;
}
} else {
decoded = c;
- if (decoded >= 0x80 && (encoding & QUrl::DecodeUnicode) == 0) {
+ if (decoded >= 0x80 && encoding & QUrl::EncodeUnicode) {
// encode the UTF-8 sequence
unicodeToEncodedUtf8(result, output, begin, input, end, decoded);
continue;
table[i] &= mask[i];
}
+/*!
+ \internal
+
+ Recodes the string from \a begin to \a end. If any transformations are
+ done, append them to \a appendTo and return the number of characters added.
+ If no transformations were required, return 0.
+
+ The \a encoding option modifies the default behaviour:
+ \list
+ \li QUrl::EncodeDelimiters: if set, delimiters will be left untransformed (note: not encoded!);
+ if unset, delimiters will be decoded
+ \li QUrl::DecodeReserved: if set, reserved characters will be decoded;
+ if unset, reserved characters will be encoded
+ \li QUrl::EncodeSpaces: if set, spaces will be encoded to "%20"; if unset, they will be " "
+ \li QUrl::EncodeUnicode: if set, characters above U+0080 will be encoded to their UTF-8
+ percent-encoded form; if unset, they will be decoded to UTF-16
+ \endlist
+
+ Other flags are ignored (including QUrl::EncodeReserved).
+
+ The \a tableModifications argument can be used to supply extra
+ modifications to the tables, to be applied after the flags above are
+ handled. It consists of a sequence of 16-bit values, where the low 8 bits
+ indicate the character in question and the high 8 bits are either \c
+ EncodeCharacter, \c LeaveCharacter or \c DecodeCharacter.
+ */
+
Q_AUTOTEST_EXPORT int
qt_urlRecode(QString &appendTo, const QChar *begin, const QChar *end,
QUrl::ComponentFormattingOptions encoding, const ushort *tableModifications)
{
uchar actionTable[sizeof defaultActionTable];
- if (encoding & QUrl::DecodeDelimiters && encoding & QUrl::DecodeReserved) {
+ if (!(encoding & QUrl::EncodeDelimiters) && encoding & QUrl::DecodeReserved) {
// reset the table
memset(actionTable, DecodeCharacter, sizeof actionTable);
- if (!(encoding & QUrl::DecodeSpaces))
+ if (encoding & QUrl::EncodeSpaces)
actionTable[0] = EncodeCharacter;
// these are always encoded
actionTable[0x7F - ' '] = EncodeCharacter;
} else {
memcpy(actionTable, defaultActionTable, sizeof actionTable);
- if (encoding & QUrl::DecodeDelimiters)
+ if (!(encoding & QUrl::EncodeDelimiters))
maskTable(actionTable, delimsMask);
if (encoding & QUrl::DecodeReserved)
maskTable(actionTable, reservedMask);
- if (encoding & QUrl::DecodeSpaces)
+ if (!(encoding & QUrl::EncodeSpaces))
actionTable[0] = DecodeCharacter; // decode
}
template<> inline char *toString(const QUrl &uri)
{
- return qstrdup(uri.toEncoded(QUrl::DecodeDelimiters).constData());
+ return qstrdup(uri.toEncoded().constData());
}
template<> inline char *toString(const QVariant &v)
// hostname cannot contain spaces
QTest::newRow("encoded-space") << QUrl("x://user name:pass word@host/path name?query value#fragment value")
- << int(QUrl::FullyEncoded)
+ << int(QUrl::EncodeSpaces)
<< "user%20name" << "pass%20word" << "user%20name:pass%20word"
<< "host" << "user%20name:pass%20word@host"
<< "/path%20name" << "query%20value" << "fragment%20value"
<< "x://user%20name:pass%20word@host/path%20name?query%20value#fragment%20value";
QTest::newRow("decoded-space") << QUrl("x://user%20name:pass%20word@host/path%20name?query%20value#fragment%20value")
- << int(QUrl::DecodeSpaces)
+ << int(QUrl::MostDecoded)
<< "user name" << "pass word" << "user name:pass word"
<< "host" << "user name:pass word@host"
<< "/path name" << "query value" << "fragment value"
// unicode tests
// hostnames can participate in this test, but we need a top-level domain that accepts Unicode
QTest::newRow("encoded-unicode") << QUrl(QString::fromUtf8("x://\xc2\x80:\xc3\x90@smørbrød.example.no/\xe0\xa0\x80?\xf0\x90\x80\x80#é"))
- << int(QUrl::FullyEncoded)
+ << int(QUrl::EncodeUnicode)
<< "%C2%80" << "%C3%90" << "%C2%80:%C3%90"
<< "xn--smrbrd-cyad.example.no" << "%C2%80:%C3%90@xn--smrbrd-cyad.example.no"
<< "/%E0%A0%80" << "%F0%90%80%80" << "%C3%A9"
<< "x://%C2%80:%C3%90@xn--smrbrd-cyad.example.no/%E0%A0%80?%F0%90%80%80#%C3%A9";
QTest::newRow("decoded-unicode") << QUrl("x://%C2%80:%C3%90@XN--SMRBRD-cyad.example.NO/%E0%A0%80?%F0%90%80%80#%C3%A9")
- << int(QUrl::DecodeUnicode)
+ << int(QUrl::MostDecoded)
<< QString::fromUtf8("\xc2\x80") << QString::fromUtf8("\xc3\x90")
<< QString::fromUtf8("\xc2\x80:\xc3\x90")
<< QString::fromUtf8("smørbrød.example.no")
// gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@"
// these are the separators between fields
- // they must appear encoded in proper URLs everywhere
- // 1) test the delimiters that, if they were decoded, would change the URL parsing
+ // they must appear encoded in certain positions, no exceptions
+ // in other positions, they can appear decoded, so they always do
+ // 1) test the delimiters that must appear encoded
+ // (if they were decoded, they'd would change the URL parsing)
QTest::newRow("encoded-gendelims-changing") << QUrl("x://%5b%3a%2f%3f%23%40%5d:%5b%2f%3f%23%40%5d@host/%2f%3f%23?%23")
<< int(QUrl::MostDecoded)
<< "[:/?#@]" << "[/?#@]" << "[%3A/?#@]:[/?#@]"
// and test that %2f is *not* decoded to a slash in the path
// don't test the query because in this mode it doesn't transform anything
QTest::newRow("decoded-gendelims-unchanging") << QUrl("x://:%3a@host/%2f%3a%40#%23%3a%2f%3f%40")
- << int(QUrl::DecodeDelimiters)
+ << int(QUrl::FullyEncoded)
<< "" << ":" << "::"
<< "host" << "::@host"
<< "/%2F:@" << "" << "#:/?@"
<< QString() << "!$()*+,;=:/?[]@" << QString()
<< "?!$()*+,;=:/?[]@";
QTest::newRow("undecoded-delims-query") << QUrl("?%21%24%26%27%28%29%2a%2b%2c%2f%3a%3b%3d%3f%40%5b%5d")
- << int(QUrl::DecodeDelimiters)
+ << int(QUrl::MostDecoded)
<< QString() << QString() << QString()
<< QString() << QString()
<< QString() << "%21%24%26%27%28%29%2A%2B%2C%2F%3A%3B%3D%3F%40%5B%5D" << QString()
QString output = QTest::currentDataTag();
expected.prepend(output);
- if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::DecodeUnicode))
+ if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), 0))
output += input;
QCOMPARE(output, expected);
}
QString encoded = QByteArray(data).toPercentEncoding();
QString decoded = QString::fromUtf8(data);
- QTest::newRow(QByteArray("decode-") + name) << encoded << QUrl::ComponentFormattingOptions(QUrl::DecodeUnicode) << decoded;
+ QTest::newRow(QByteArray("decode-") + name) << encoded << QUrl::ComponentFormattingOptions(QUrl::MostDecoded) << decoded;
QTest::newRow(QByteArray("encode-") + name) << decoded << QUrl::ComponentFormattingOptions(QUrl::FullyEncoded) << encoded;
}
QTest::newRow("encode-nul") << QString::fromLatin1("abc\0def", 7) << F(QUrl::MostDecoded) << "abc%00def";
// space
- QTest::newRow("space-leave-decoded") << "Hello World " << F(QUrl::DecodeSpaces) << "Hello World ";
+ QTest::newRow("space-leave-decoded") << "Hello World " << F(QUrl::MostDecoded) << "Hello World ";
QTest::newRow("space-leave-encoded") << "Hello%20World%20" << F(QUrl::FullyEncoded) << "Hello%20World%20";
QTest::newRow("space-encode") << "Hello World " << F(QUrl::FullyEncoded) << "Hello%20World%20";
- QTest::newRow("space-decode") << "Hello%20World%20" << F(QUrl::DecodeSpaces) << "Hello World ";
+ QTest::newRow("space-decode") << "Hello%20World%20" << F(QUrl::MostDecoded) << "Hello World ";
// decode unreserved
QTest::newRow("unreserved-decode") << "%66%6f%6f%42a%72" << F(QUrl::FullyEncoded) << "fooBar";
// mix encoding with decoding
- QTest::newRow("encode-control-decode-space") << "\1\2%200" << F(QUrl::DecodeSpaces) << "%01%02 0";
- QTest::newRow("decode-space-encode-control") << "%20\1\2" << F(QUrl::DecodeSpaces) << " %01%02";
+ QTest::newRow("encode-control-decode-space") << "\1\2%200" << F(QUrl::MostDecoded) << "%01%02 0";
+ QTest::newRow("decode-space-encode-control") << "%20\1\2" << F(QUrl::MostDecoded) << " %01%02";
// decode and encode valid UTF-8 data
// invalid is tested in encodingRecodeInvalidUtf8
QTest::newRow("ff") << "%ff" << F(QUrl::FullyEncoded) << "%FF";
// decode UTF-8 mixed with non-UTF-8 and unreserved
- QTest::newRow("utf8-mix-1") << "%80%C2%80" << F(QUrl::DecodeUnicode) << QString::fromUtf8("%80\xC2\x80");
- QTest::newRow("utf8-mix-2") << "%C2%C2%80" << F(QUrl::DecodeUnicode) << QString::fromUtf8("%C2\xC2\x80");
- QTest::newRow("utf8-mix-3") << "%E0%C2%80" << F(QUrl::DecodeUnicode) << QString::fromUtf8("%E0\xC2\x80");
- QTest::newRow("utf8-mix-3") << "A%C2%80" << F(QUrl::DecodeUnicode) << QString::fromUtf8("A\xC2\x80");
- QTest::newRow("utf8-mix-3") << "%C2%80A" << F(QUrl::DecodeUnicode) << QString::fromUtf8("\xC2\x80""A");
+ QTest::newRow("utf8-mix-1") << "%80%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("%80\xC2\x80");
+ QTest::newRow("utf8-mix-2") << "%C2%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("%C2\xC2\x80");
+ QTest::newRow("utf8-mix-3") << "%E0%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("%E0\xC2\x80");
+ QTest::newRow("utf8-mix-3") << "A%C2%80" << F(QUrl::MostDecoded) << QString::fromUtf8("A\xC2\x80");
+ QTest::newRow("utf8-mix-3") << "%C2%80A" << F(QUrl::MostDecoded) << QString::fromUtf8("\xC2\x80""A");
}
void tst_QUrlInternal::encodingRecode()
extern void loadNonCharactersRows();
loadNonCharactersRows();
- QTest::newRow("utf8-mix-4") << QByteArray("\xE0!A2\x80");
- QTest::newRow("utf8-mix-5") << QByteArray("\xE0\xA2!80");
- QTest::newRow("utf8-mix-5") << QByteArray("\xE0\xA2\x33");
+ QTest::newRow("utf8-mix-4") << QByteArray("\xE0.A2\x80");
+ QTest::newRow("utf8-mix-5") << QByteArray("\xE0\xA2.80");
+ QTest::newRow("utf8-mix-6") << QByteArray("\xE0\xA2\x33");
}
void tst_QUrlInternal::encodingRecodeInvalidUtf8()
// prepend some data to be sure that it remains there
QString output = QTest::currentDataTag();
- if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::DecodeUnicode))
+ if (!qt_urlRecode(output, input.constData(), input.constData() + input.length(), QUrl::MostDecoded))
output += input;
QCOMPARE(output, QTest::currentDataTag() + input);
QTest::newRow("encode-space") << " = " << " " << " " << F(QUrl::FullyEncoded)
<< "%20=%20" << "%20" << "%20";
- QTest::newRow("non-delimiters") << "%3C%5C%3E=%7B%7C%7D%5E%60" << "%3C%5C%3E" << "%7B%7C%7D%5E%60" << F(QUrl::PrettyDecoded)
+ // tri-state
+ QTest::newRow("decode-non-delimiters") << "%3C%5C%3E=%7B%7C%7D%5E%60" << "%3C%5C%3E" << "%7B%7C%7D%5E%60" << F(QUrl::DecodeReserved)
<< "<\\>={|}^`" << "<\\>" << "{|}^`";
- QTest::newRow("encode-non-delimiters") << "<\\>={|}^`" << "<\\>" << "{|}^`" << F(QUrl::FullyEncoded)
+ QTest::newRow("encode-non-delimiters") << "<\\>={|}^`" << "<\\>" << "{|}^`" << F(QUrl::EncodeReserved)
<< "%3C%5C%3E=%7B%7C%7D%5E%60" << "%3C%5C%3E" << "%7B%7C%7D%5E%60";
+ QTest::newRow("pretty-non-delimiters") << "<\\>={|}^`" << "<\\>" << "{|}^`" << F(QUrl::PrettyDecoded)
+ << "%3C%5C%3E=%7B%7C%7D%5E%60" << "<\\>" << "{|}^`";
QTest::newRow("equals") << "%3D=%3D" << "%3D" << "%3D" << F(QUrl::PrettyDecoded)
<< "%3D=%3D" << "=" << "=";
<< "%26=%26" << "&" << "&";
QTest::newRow("hash") << "#=#" << "%23" << "%23" << F(QUrl::PrettyDecoded)
<< "#=#" << "#" << "#";
- QTest::newRow("decode-hash") << "%23=%23" << "%23" << "%23" << F(QUrl::DecodeDelimiters)
+ QTest::newRow("decode-hash") << "%23=%23" << "%23" << "%23" << F(QUrl::PrettyDecoded)
<< "#=#" << "#" << "#";
QTest::newRow("percent") << "%25=%25" << "%25" << "%25" << F(QUrl::PrettyDecoded)
// plus signs must not be touched
QTest::newRow("encode-plus") << "+=+" << "+" << "+" << F(QUrl::FullyEncoded)
<< "+=+" << "+" << "+";
- QTest::newRow("decode-2b") << "%2b=%2b" << "%2b" << "%2b" << F(QUrl::DecodeDelimiters)
+ QTest::newRow("decode-2b") << "%2b=%2b" << "%2b" << "%2b" << F(QUrl::MostDecoded)
<< "%2B=%2B" << "%2B" << "%2B";
expected << qItem("foo", "bar") << qItem("hello", "world");
COMPARE_ITEMS(query.queryItems(), expected);
COMPARE_ITEMS(query.queryItems(QUrl::FullyEncoded), expected);
- COMPARE_ITEMS(query.queryItems(QUrl::DecodeDelimiters), expected);
+ COMPARE_ITEMS(query.queryItems(QUrl::MostDecoded), expected);
}
{