Fix decoding of QByteArray in the deprecated "encoded" setters in QUrl
The asymmetry is intentional: the getters can use toLatin1() because the called functions, with a QUrl::FullyEncoded parameter, return ASCII only. This gives a small performance improvement over the need to run the UTF-8 encoder. However, the data passed to setters could contain non-ASCII binary data, in addition to the percent-encoded data. We can't use fromUtf8 because it's binary and we can't use toPercentEncoded because it already encoded. Change-Id: I5ecdb49be5af51ac86fd9764eb3a6aa96385f512 Reviewed-by: David Faure <faure@kde.org>
This commit is contained in:
parent
60818231d8
commit
ce9b010ec6
@ -2978,6 +2978,17 @@ QByteArray QUrl::toPercentEncoding(const QString &input, const QByteArray &exclu
|
|||||||
return input.toUtf8().toPercentEncoding(exclude, include);
|
return input.toUtf8().toPercentEncoding(exclude, include);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
\since 5.0
|
||||||
|
Used in the setEncodedXXX compatibility functions. Converts \a ba to
|
||||||
|
QString form.
|
||||||
|
*/
|
||||||
|
QString QUrl::fromEncodedComponent_helper(const QByteArray &ba)
|
||||||
|
{
|
||||||
|
return qt_urlRecodeByteArray(ba);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn QByteArray QUrl::toPunycode(const QString &uc)
|
\fn QByteArray QUrl::toPunycode(const QString &uc)
|
||||||
\obsolete
|
\obsolete
|
||||||
|
@ -274,37 +274,37 @@ public:
|
|||||||
QT_DEPRECATED inline void removeAllEncodedQueryItems(const QByteArray &key);
|
QT_DEPRECATED inline void removeAllEncodedQueryItems(const QByteArray &key);
|
||||||
|
|
||||||
QT_DEPRECATED void setEncodedUrl(const QByteArray &u, ParsingMode mode = TolerantMode)
|
QT_DEPRECATED void setEncodedUrl(const QByteArray &u, ParsingMode mode = TolerantMode)
|
||||||
{ setUrl(QString::fromUtf8(u.constData(), u.size()), mode); }
|
{ setUrl(fromEncodedComponent_helper(u), mode); }
|
||||||
|
|
||||||
QT_DEPRECATED QByteArray encodedUserName() const
|
QT_DEPRECATED QByteArray encodedUserName() const
|
||||||
{ return userName(FullyEncoded).toLatin1(); }
|
{ return userName(FullyEncoded).toLatin1(); }
|
||||||
QT_DEPRECATED void setEncodedUserName(const QByteArray &value)
|
QT_DEPRECATED void setEncodedUserName(const QByteArray &value)
|
||||||
{ setUserName(QString::fromLatin1(value)); }
|
{ setUserName(fromEncodedComponent_helper(value)); }
|
||||||
|
|
||||||
QT_DEPRECATED QByteArray encodedPassword() const
|
QT_DEPRECATED QByteArray encodedPassword() const
|
||||||
{ return password(FullyEncoded).toLatin1(); }
|
{ return password(FullyEncoded).toLatin1(); }
|
||||||
QT_DEPRECATED void setEncodedPassword(const QByteArray &value)
|
QT_DEPRECATED void setEncodedPassword(const QByteArray &value)
|
||||||
{ setPassword(QString::fromLatin1(value)); }
|
{ setPassword(fromEncodedComponent_helper(value)); }
|
||||||
|
|
||||||
QT_DEPRECATED QByteArray encodedHost() const
|
QT_DEPRECATED QByteArray encodedHost() const
|
||||||
{ return host(FullyEncoded).toLatin1(); }
|
{ return host(FullyEncoded).toLatin1(); }
|
||||||
QT_DEPRECATED void setEncodedHost(const QByteArray &value)
|
QT_DEPRECATED void setEncodedHost(const QByteArray &value)
|
||||||
{ setHost(QString::fromLatin1(value)); }
|
{ setHost(fromEncodedComponent_helper(value)); }
|
||||||
|
|
||||||
QT_DEPRECATED QByteArray encodedPath() const
|
QT_DEPRECATED QByteArray encodedPath() const
|
||||||
{ return path(FullyEncoded).toLatin1(); }
|
{ return path(FullyEncoded).toLatin1(); }
|
||||||
QT_DEPRECATED void setEncodedPath(const QByteArray &value)
|
QT_DEPRECATED void setEncodedPath(const QByteArray &value)
|
||||||
{ setPath(QString::fromLatin1(value)); }
|
{ setPath(fromEncodedComponent_helper(value)); }
|
||||||
|
|
||||||
QT_DEPRECATED QByteArray encodedQuery() const
|
QT_DEPRECATED QByteArray encodedQuery() const
|
||||||
{ return toLatin1_helper(query(FullyEncoded)); }
|
{ return toLatin1_helper(query(FullyEncoded)); }
|
||||||
QT_DEPRECATED void setEncodedQuery(const QByteArray &value)
|
QT_DEPRECATED void setEncodedQuery(const QByteArray &value)
|
||||||
{ setQuery(value.isNull() ? QString() : QString::fromLatin1(value)); }
|
{ setQuery(fromEncodedComponent_helper(value)); }
|
||||||
|
|
||||||
QT_DEPRECATED QByteArray encodedFragment() const
|
QT_DEPRECATED QByteArray encodedFragment() const
|
||||||
{ return toLatin1_helper(fragment(FullyEncoded)); }
|
{ return toLatin1_helper(fragment(FullyEncoded)); }
|
||||||
QT_DEPRECATED void setEncodedFragment(const QByteArray &value)
|
QT_DEPRECATED void setEncodedFragment(const QByteArray &value)
|
||||||
{ setFragment(value.isNull() ? QString() : QString::fromLatin1(value)); }
|
{ setFragment(fromEncodedComponent_helper(value)); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// helper function for the encodedQuery and encodedFragment functions
|
// helper function for the encodedQuery and encodedFragment functions
|
||||||
@ -315,6 +315,8 @@ private:
|
|||||||
return string.toLatin1();
|
return string.toLatin1();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
private:
|
||||||
|
static QString fromEncodedComponent_helper(const QByteArray &ba);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static QString fromAce(const QByteArray &);
|
static QString fromAce(const QByteArray &);
|
||||||
|
@ -182,6 +182,7 @@ extern Q_AUTOTEST_EXPORT void qt_nameprep(QString *source, int from);
|
|||||||
extern Q_AUTOTEST_EXPORT bool qt_check_std3rules(const QChar *uc, int len);
|
extern Q_AUTOTEST_EXPORT bool qt_check_std3rules(const QChar *uc, int len);
|
||||||
extern Q_AUTOTEST_EXPORT void qt_punycodeEncoder(const QChar *s, int ucLength, QString *output);
|
extern Q_AUTOTEST_EXPORT void qt_punycodeEncoder(const QChar *s, int ucLength, QString *output);
|
||||||
extern Q_AUTOTEST_EXPORT QString qt_punycodeDecoder(const QString &pc);
|
extern Q_AUTOTEST_EXPORT QString qt_punycodeDecoder(const QString &pc);
|
||||||
|
extern Q_AUTOTEST_EXPORT QString qt_urlRecodeByteArray(const QByteArray &ba);
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -132,22 +132,22 @@ inline void QUrl::removeAllQueryItems(const QString &key)
|
|||||||
{ QUrlQuery q(*this); q.removeAllQueryItems(key); }
|
{ QUrlQuery q(*this); q.removeAllQueryItems(key); }
|
||||||
|
|
||||||
inline void QUrl::addEncodedQueryItem(const QByteArray &key, const QByteArray &value)
|
inline void QUrl::addEncodedQueryItem(const QByteArray &key, const QByteArray &value)
|
||||||
{ QUrlQuery q(*this); q.addQueryItem(QString::fromUtf8(key), QString::fromUtf8(value)); setQuery(q); }
|
{ QUrlQuery q(*this); q.addQueryItem(fromEncodedComponent_helper(key), fromEncodedComponent_helper(value)); setQuery(q); }
|
||||||
inline bool QUrl::hasEncodedQueryItem(const QByteArray &key) const
|
inline bool QUrl::hasEncodedQueryItem(const QByteArray &key) const
|
||||||
{ return QUrlQuery(*this).hasQueryItem(QString::fromUtf8(key)); }
|
{ return QUrlQuery(*this).hasQueryItem(fromEncodedComponent_helper(key)); }
|
||||||
inline QByteArray QUrl::encodedQueryItemValue(const QByteArray &key) const
|
inline QByteArray QUrl::encodedQueryItemValue(const QByteArray &key) const
|
||||||
{ return QUrlQuery(*this).queryItemValue(QString::fromUtf8(key), QUrl::FullyEncoded).toLatin1(); }
|
{ return QUrlQuery(*this).queryItemValue(fromEncodedComponent_helper(key), QUrl::FullyEncoded).toLatin1(); }
|
||||||
inline void QUrl::removeEncodedQueryItem(const QByteArray &key)
|
inline void QUrl::removeEncodedQueryItem(const QByteArray &key)
|
||||||
{ QUrlQuery q(*this); q.removeQueryItem(QString::fromUtf8(key)); setQuery(q); }
|
{ QUrlQuery q(*this); q.removeQueryItem(fromEncodedComponent_helper(key)); setQuery(q); }
|
||||||
inline void QUrl::removeAllEncodedQueryItems(const QByteArray &key)
|
inline void QUrl::removeAllEncodedQueryItems(const QByteArray &key)
|
||||||
{ QUrlQuery q(*this); q.removeAllQueryItems(QString::fromUtf8(key)); }
|
{ QUrlQuery q(*this); q.removeAllQueryItems(fromEncodedComponent_helper(key)); }
|
||||||
|
|
||||||
inline void QUrl::setEncodedQueryItems(const QList<QPair<QByteArray, QByteArray> > &qry)
|
inline void QUrl::setEncodedQueryItems(const QList<QPair<QByteArray, QByteArray> > &qry)
|
||||||
{
|
{
|
||||||
QUrlQuery q;
|
QUrlQuery q;
|
||||||
QList<QPair<QByteArray, QByteArray> >::ConstIterator it = qry.constBegin();
|
QList<QPair<QByteArray, QByteArray> >::ConstIterator it = qry.constBegin();
|
||||||
for ( ; it != qry.constEnd(); ++it)
|
for ( ; it != qry.constEnd(); ++it)
|
||||||
q.addQueryItem(QString::fromUtf8(it->first), QString::fromUtf8(it->second));
|
q.addQueryItem(fromEncodedComponent_helper(it->first), fromEncodedComponent_helper(it->second));
|
||||||
setQuery(q);
|
setQuery(q);
|
||||||
}
|
}
|
||||||
inline QList<QPair<QByteArray, QByteArray> > QUrl::encodedQueryItems() const
|
inline QList<QPair<QByteArray, QByteArray> > QUrl::encodedQueryItems() const
|
||||||
@ -162,7 +162,7 @@ inline QList<QPair<QByteArray, QByteArray> > QUrl::encodedQueryItems() const
|
|||||||
}
|
}
|
||||||
inline QList<QByteArray> QUrl::allEncodedQueryItemValues(const QByteArray &key) const
|
inline QList<QByteArray> QUrl::allEncodedQueryItemValues(const QByteArray &key) const
|
||||||
{
|
{
|
||||||
QStringList items = QUrlQuery(*this).allQueryItemValues(QString::fromUtf8(key), QUrl::FullyEncoded);
|
QStringList items = QUrlQuery(*this).allQueryItemValues(fromEncodedComponent_helper(key), QUrl::FullyEncoded);
|
||||||
QList<QByteArray> result;
|
QList<QByteArray> result;
|
||||||
result.reserve(items.size());
|
result.reserve(items.size());
|
||||||
Q_FOREACH (const QString &item, items)
|
Q_FOREACH (const QString &item, items)
|
||||||
|
@ -674,4 +674,54 @@ qt_urlRecode(QString &appendTo, const QChar *begin, const QChar *end,
|
|||||||
encoding, actionTable, false);
|
encoding, actionTable, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
\since 5.0
|
||||||
|
|
||||||
|
\a ba contains an 8-bit form of the component and it might be
|
||||||
|
percent-encoded already. We can't use QString::fromUtf8 because it might
|
||||||
|
contain non-UTF8 sequences. We can't use QByteArray::toPercentEncoding
|
||||||
|
because it might already contain percent-encoded sequences. We can't use
|
||||||
|
qt_urlRecode because it needs UTF-16 input.
|
||||||
|
*/
|
||||||
|
Q_AUTOTEST_EXPORT
|
||||||
|
QString qt_urlRecodeByteArray(const QByteArray &ba)
|
||||||
|
{
|
||||||
|
if (ba.isNull())
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
// scan ba for anything above or equal to 0x80
|
||||||
|
// control points below 0x20 are fine in QString
|
||||||
|
const char *in = ba.constData();
|
||||||
|
const char *const end = ba.constEnd();
|
||||||
|
for ( ; in < end; ++in) {
|
||||||
|
if (*in & 0x80)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in == end) {
|
||||||
|
// no non-ASCII found, we're safe to convert to QString
|
||||||
|
return QString::fromLatin1(ba, ba.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// we found something that we need to encode
|
||||||
|
QByteArray intermediate = ba;
|
||||||
|
intermediate.resize(ba.size() * 3 - (in - ba.constData()));
|
||||||
|
uchar *out = reinterpret_cast<uchar *>(intermediate.data() + (in - ba.constData()));
|
||||||
|
for ( ; in < end; ++in) {
|
||||||
|
if (*in & 0x80) {
|
||||||
|
// encode
|
||||||
|
*out++ = '%';
|
||||||
|
*out++ = encodeNibble(uchar(*in) >> 4);
|
||||||
|
*out++ = encodeNibble(uchar(*in) & 0xf);
|
||||||
|
} else {
|
||||||
|
// keep
|
||||||
|
*out++ = uchar(*in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now it's safe to call fromLatin1
|
||||||
|
return QString::fromLatin1(intermediate, out - reinterpret_cast<uchar *>(intermediate.data()));
|
||||||
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -2247,6 +2247,8 @@ void tst_QUrl::setEncodedFragment_data()
|
|||||||
QTest::newRow("initial url has fragment") << BA("http://www.kde.org#old") << BA("new") << BA("http://www.kde.org#new");
|
QTest::newRow("initial url has fragment") << BA("http://www.kde.org#old") << BA("new") << BA("http://www.kde.org#new");
|
||||||
QTest::newRow("encoded fragment") << BA("http://www.kde.org") << BA("a%20c") << BA("http://www.kde.org#a%20c");
|
QTest::newRow("encoded fragment") << BA("http://www.kde.org") << BA("a%20c") << BA("http://www.kde.org#a%20c");
|
||||||
QTest::newRow("with #") << BA("http://www.kde.org") << BA("a#b") << BA("http://www.kde.org#a#b");
|
QTest::newRow("with #") << BA("http://www.kde.org") << BA("a#b") << BA("http://www.kde.org#a#b");
|
||||||
|
QTest::newRow("unicode") << BA("http://www.kde.org") << BA("\xc3\xa9") << BA("http://www.kde.org#%C3%A9");
|
||||||
|
QTest::newRow("binary") << BA("http://www.kde.org") << BA("\x00\xc0\x80", 3) << BA("http://www.kde.org#%00%C0%80");
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QUrl::setEncodedFragment()
|
void tst_QUrl::setEncodedFragment()
|
||||||
@ -2926,6 +2928,17 @@ void tst_QUrl::componentEncodings()
|
|||||||
QCOMPARE(url.toString(formatting),
|
QCOMPARE(url.toString(formatting),
|
||||||
(((QString(toString ))))); // the weird () and space is to align the output
|
(((QString(toString ))))); // the weird () and space is to align the output
|
||||||
|
|
||||||
|
if (formatting == QUrl::FullyEncoded) {
|
||||||
|
QCOMPARE(url.encodedUserName(), userName.toUtf8());
|
||||||
|
QCOMPARE(url.encodedPassword(), password.toUtf8());
|
||||||
|
// no encodedUserInfo
|
||||||
|
QCOMPARE(url.encodedHost(), host.toUtf8());
|
||||||
|
// no encodedAuthority
|
||||||
|
QCOMPARE(url.encodedPath(), path.toUtf8());
|
||||||
|
QCOMPARE(url.encodedQuery(), query.toUtf8());
|
||||||
|
QCOMPARE(url.encodedFragment(), fragment.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
// repeat with the URL we got from toString
|
// repeat with the URL we got from toString
|
||||||
QUrl url2(toString);
|
QUrl url2(toString);
|
||||||
QCOMPARE(url2.userName(formatting), userName);
|
QCOMPARE(url2.userName(formatting), userName);
|
||||||
|
@ -95,6 +95,8 @@ private Q_SLOTS:
|
|||||||
void encodingRecode();
|
void encodingRecode();
|
||||||
void encodingRecodeInvalidUtf8_data();
|
void encodingRecodeInvalidUtf8_data();
|
||||||
void encodingRecodeInvalidUtf8();
|
void encodingRecodeInvalidUtf8();
|
||||||
|
void recodeByteArray_data();
|
||||||
|
void recodeByteArray();
|
||||||
};
|
};
|
||||||
#include "tst_qurlinternal.moc"
|
#include "tst_qurlinternal.moc"
|
||||||
|
|
||||||
@ -1006,4 +1008,34 @@ void tst_QUrlInternal::encodingRecodeInvalidUtf8()
|
|||||||
QCOMPARE(output, QTest::currentDataTag() + input);
|
QCOMPARE(output, QTest::currentDataTag() + input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QUrlInternal::recodeByteArray_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QByteArray>("input");
|
||||||
|
QTest::addColumn<QString>("expected");
|
||||||
|
|
||||||
|
QTest::newRow("null") << QByteArray() << QString();
|
||||||
|
QTest::newRow("empty") << QByteArray("") << QString("");
|
||||||
|
QTest::newRow("normal") << QByteArray("Hello") << "Hello";
|
||||||
|
QTest::newRow("valid-utf8") << QByteArray("\xc3\xa9") << "%C3%A9";
|
||||||
|
QTest::newRow("percent-encoded") << QByteArray("%C3%A9%00%C0%80") << "%C3%A9%00%C0%80";
|
||||||
|
QTest::newRow("invalid-utf8-1") << QByteArray("\xc3\xc3") << "%C3%C3";
|
||||||
|
QTest::newRow("invalid-utf8-2") << QByteArray("\xc0\x80") << "%C0%80";
|
||||||
|
|
||||||
|
// note: percent-encoding the control characters ("\0" -> "%00") would also
|
||||||
|
// be correct, but it's unnecessary for this function
|
||||||
|
QTest::newRow("binary") << QByteArray("\0\x1f", 2) << QString::fromLatin1("\0\x1f", 2);;
|
||||||
|
QTest::newRow("binary+percent-encoded") << QByteArray("\0%25", 4) << QString::fromLatin1("\0%25", 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QUrlInternal::recodeByteArray()
|
||||||
|
{
|
||||||
|
QFETCH(QByteArray, input);
|
||||||
|
QFETCH(QString, expected);
|
||||||
|
QString output = qt_urlRecodeByteArray(input);
|
||||||
|
|
||||||
|
QCOMPARE(output.isNull(), input.isNull());
|
||||||
|
QCOMPARE(output.isEmpty(), input.isEmpty());
|
||||||
|
QCOMPARE(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_APPLESS_MAIN(tst_QUrlInternal)
|
QTEST_APPLESS_MAIN(tst_QUrlInternal)
|
||||||
|
Loading…
Reference in New Issue
Block a user