summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2011-10-20 16:23:41 +0200
committerQt by Nokia <qt-info@nokia.com>2012-03-30 01:19:59 +0200
commitcff38329aa8635ac8a0696b0aa78782fe5605d16 (patch)
tree03d1845dd091f58ae72af1af749a6281b9d2014c
parent74d2dba46041448c70dbd3049ae2a8277770baf6 (diff)
Re-introduce support for QUrl::errorString()
Note that QUrl can only remember one error. If the URL contains more than one error condition, only the latest (in whichever parsing order URL decides to use) will be reported. I don't want too keep too much data in QUrlPrivate for validation, so let's use 4 bytes only. Change-Id: I2afbf80734d3633f41f779984ab76b3a5ba293a2 Reviewed-by: Lars Knoll <lars.knoll@nokia.com>
-rw-r--r--src/corelib/io/qurl.cpp168
-rw-r--r--src/corelib/io/qurl_p.h42
-rw-r--r--tests/auto/corelib/io/qurl/tst_qurl.cpp9
3 files changed, 123 insertions, 96 deletions
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp
index 62d6092c9d..806edb8a13 100644
--- a/src/corelib/io/qurl.cpp
+++ b/src/corelib/io/qurl.cpp
@@ -234,6 +234,7 @@ static inline QString fileScheme()
QUrlPrivate::QUrlPrivate()
: ref(1), port(-1),
+ errorCode(NoError), errorSupplement(0),
sectionIsPresent(0), sectionHasError(0)
{
}
@@ -247,6 +248,8 @@ QUrlPrivate::QUrlPrivate(const QUrlPrivate &copy)
path(copy.path),
query(copy.query),
fragment(copy.fragment),
+ errorCode(copy.errorCode),
+ errorSupplement(copy.errorSupplement),
sectionIsPresent(copy.sectionIsPresent),
sectionHasError(copy.sectionHasError)
{
@@ -263,6 +266,8 @@ void QUrlPrivate::clear()
query.clear();
fragment.clear();
+ errorCode = NoError;
+ errorSupplement = 0;
sectionIsPresent = 0;
sectionHasError = 0;
}
@@ -520,10 +525,12 @@ bool QUrlPrivate::setScheme(const QString &value, int len, bool decoded)
scheme.clear();
sectionIsPresent |= Scheme;
sectionHasError |= Scheme; // assume it has errors, we'll clear before returning true
+ errorCode = SchemeEmptyError;
if (len == 0)
return false;
// validate it:
+ errorCode = InvalidSchemeError;
int needsLowercasing = -1;
const ushort *p = reinterpret_cast<const ushort *>(value.constData());
for (int i = 0; i < len; ++i) {
@@ -541,6 +548,7 @@ bool QUrlPrivate::setScheme(const QString &value, int len, bool decoded)
if (p[i] == '%') {
// found a percent-encoded sign
// if we haven't decoded yet, decode and try again
+ errorSupplement = '%';
if (decoded)
return false;
@@ -551,11 +559,13 @@ bool QUrlPrivate::setScheme(const QString &value, int len, bool decoded)
}
// found something else
+ errorSupplement = p[i];
return false;
}
scheme = value.left(len);
sectionHasError &= ~Scheme;
+ errorCode = NoError;
if (needsLowercasing != -1) {
// schemes are ASCII only, so we don't need the full Unicode toLower
@@ -588,11 +598,6 @@ bool QUrlPrivate::setAuthority(const QString &auth, int from, int end)
from = userInfoIndex + 1;
}
- if (userInfoIndex == end - 1) {
- // authority without a hostname is invalid
- return false;
- }
-
int colonIndex = auth.lastIndexOf(QLatin1Char(':'), end - 1);
if (colonIndex < from)
colonIndex = -1;
@@ -609,6 +614,7 @@ bool QUrlPrivate::setAuthority(const QString &auth, int from, int end)
if (colonIndex == end - 1) {
// found a colon but no digits after it
sectionHasError |= Port;
+ errorCode = PortEmptyError;
} else if (uint(colonIndex) < uint(end)) {
unsigned long x = 0;
for (int i = colonIndex + 1; i < end; ++i) {
@@ -618,6 +624,7 @@ bool QUrlPrivate::setAuthority(const QString &auth, int from, int end)
x += c - '0';
} else {
sectionHasError |= Port;
+ errorCode = InvalidPortError;
x = ulong(-1); // x != ushort(x)
break;
}
@@ -757,7 +764,7 @@ inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions o
}
// the whole IPvFuture is passed and parsed here, including brackets
-static bool parseIpFuture(QString &host, const QChar *begin, const QChar *end)
+static int parseIpFuture(QString &host, const QChar *begin, const QChar *end)
{
// IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" )
static const char acceptable[] =
@@ -767,7 +774,7 @@ static bool parseIpFuture(QString &host, const QChar *begin, const QChar *end)
// the brackets and the "v" have been checked
if (begin[3].unicode() != '.')
- return false;
+ return begin[3].unicode();
if ((begin[2].unicode() >= 'A' && begin[2].unicode() >= 'F') ||
(begin[2].unicode() >= 'a' && begin[2].unicode() <= 'f') ||
(begin[2].unicode() >= '0' && begin[2].unicode() <= '9')) {
@@ -793,12 +800,12 @@ static bool parseIpFuture(QString &host, const QChar *begin, const QChar *end)
else if (begin->unicode() < 0x80 && strchr(acceptable, begin->unicode()) != 0)
host += *begin;
else
- return false;
+ return begin->unicode();
}
host += QLatin1Char(']');
- return true;
+ return -1;
}
- return false;
+ return begin[2].unicode();
}
// ONLY the IPv6 address is parsed here, WITHOUT the brackets
@@ -834,30 +841,36 @@ bool QUrlPrivate::setHost(const QString &value, int from, int iend, bool maybePe
const int len = end - begin;
host.clear();
sectionIsPresent |= Host;
- if (len == 0) {
- sectionHasError &= ~Host;
+ sectionHasError &= ~Host;
+ if (len == 0)
return true;
- }
-
- // we'll clear just before returning true
- sectionHasError |= Host;
if (begin[0].unicode() == '[') {
// IPv6Address or IPvFuture
// smallest IPv6 address is "[::]" (len = 4)
// smallest IPvFuture address is "[v7.X]" (len = 6)
- if (end[-1].unicode() != ']')
+ if (end[-1].unicode() != ']') {
+ sectionHasError |= Host;
+ errorCode = HostMissingEndBracket;
return false;
+ }
- bool ok;
- if (len > 5 && begin[1].unicode() == 'v')
- ok = parseIpFuture(host, begin, end);
- else
- ok = parseIp6(host, begin + 1, end - 1);
+ if (len > 5 && begin[1].unicode() == 'v') {
+ int c = parseIpFuture(host, begin, end);
+ if (c != -1) {
+ sectionHasError |= Host;
+ errorCode = InvalidIPvFutureError;
+ errorSupplement = short(c);
+ }
+ return c == -1;
+ }
- if (ok)
- sectionHasError &= ~Host;
- return ok;
+ if (parseIp6(host, begin + 1, end - 1))
+ return true;
+
+ sectionHasError |= Host;
+ errorCode = InvalidIPv6AddressError;
+ return false;
}
// check if it's an IPv4 address
@@ -887,16 +900,22 @@ bool QUrlPrivate::setHost(const QString &value, int from, int iend, bool maybePe
if (maybePercentEncoded && qt_urlRecode(s, begin, end, QUrl::MostDecoded, 0)) {
// something was decoded
// anything encoded left?
- if (s.contains(QChar(0x25))) // '%'
+ if (s.contains(QChar(0x25))) { // '%'
+ sectionHasError |= Host;
+ errorCode = InvalidRegNameError;
return false;
+ }
// recurse
return setHost(s, 0, s.length(), false);
}
s = qt_ACE_do(QString::fromRawData(begin, len), NormalizeAce);
- if (s.isEmpty())
+ if (s.isEmpty()) {
+ sectionHasError |= Host;
+ errorCode = InvalidRegNameError;
return false;
+ }
// check IPv4 again
if (QIPAddressUtils::parseIp4(ip4, s.constBegin(), s.constEnd())) {
@@ -904,7 +923,6 @@ bool QUrlPrivate::setHost(const QString &value, int from, int iend, bool maybePe
} else {
host = s;
}
- sectionHasError &= ~Host;
return true;
}
@@ -1217,45 +1235,6 @@ const QByteArray &QUrlPrivate::normalized() const
return encodedNormalized;
}
-
-QString QUrlPrivate::createErrorString()
-{
- if (isValid && isHostValid)
- return QString();
-
- QString errorString(QLatin1String(QT_TRANSLATE_NOOP(QUrl, "Invalid URL \"")));
- errorString += QLatin1String(encodedOriginal.constData());
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, "\""));
-
- if (errorInfo._source) {
- int position = encodedOriginal.indexOf(errorInfo._source) - 1;
- if (position > 0) {
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": error at position "));
- errorString += QString::number(position);
- } else {
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": "));
- errorString += QLatin1String(errorInfo._source);
- }
- }
-
- if (errorInfo._expected) {
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": expected \'"));
- errorString += QLatin1Char(errorInfo._expected);
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, "\'"));
- } else {
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ": "));
- if (isHostValid)
- errorString += QLatin1String(errorInfo._message);
- else
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, "invalid hostname"));
- }
- if (errorInfo._found) {
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, ", but found \'"));
- errorString += QLatin1Char(errorInfo._found);
- errorString += QLatin1String(QT_TRANSLATE_NOOP(QUrl, "\'"));
- }
- return errorString;
-}
#endif
/*!
@@ -1591,9 +1570,17 @@ void QUrl::setHost(const QString &host)
if (d->setHost(host, 0, host.length())) {
if (host.isNull())
d->sectionIsPresent &= ~QUrlPrivate::Host;
- } else {
+ } else if (!host.startsWith(QLatin1Char('['))) {
// setHost failed, it might be IPv6 or IPvFuture in need of bracketing
- d->setHost(QLatin1Char('[') + host + QLatin1Char(']'), 0, host.length() + 2);
+ ushort oldCode = d->errorCode;
+ ushort oldSupplement = d->errorSupplement;
+ if (!d->setHost(QLatin1Char('[') + host + QLatin1Char(']'), 0, host.length() + 2)) {
+ // failed again: choose if this was an IPv6 error or not
+ if (!host.contains(QLatin1Char(':'))) {
+ d->errorCode = oldCode;
+ d->errorSupplement = oldSupplement;
+ }
+ }
}
}
@@ -1627,6 +1614,7 @@ void QUrl::setPort(int port)
qWarning("QUrl::setPort: Out of range");
port = -1;
d->sectionHasError |= QUrlPrivate::Port;
+ d->errorCode = QUrlPrivate::InvalidPortError;
} else {
d->sectionHasError &= ~QUrlPrivate::Port;
}
@@ -2413,7 +2401,47 @@ QDebug operator<<(QDebug d, const QUrl &url)
*/
QString QUrl::errorString() const
{
- return QString();
+ if (!d)
+ return QString();
+
+ if (d->sectionHasError == 0)
+ return QString();
+
+ // check if the error code matches a section with error
+ if ((d->sectionHasError & (d->errorCode >> 8)) == 0)
+ return QString();
+
+ QChar c = d->errorSupplement;
+ switch (QUrlPrivate::ErrorCode(d->errorCode)) {
+ case QUrlPrivate::NoError:
+ return QString();
+
+ case QUrlPrivate::InvalidSchemeError: {
+ QString msg = QStringLiteral("Invalid scheme (character '%1' not permitted)");
+ return msg.arg(c);
+ }
+ case QUrlPrivate::SchemeEmptyError:
+ return QStringLiteral("Empty scheme");
+
+ case QUrlPrivate::InvalidRegNameError:
+ return QStringLiteral("Hostname contains invalid characters");
+ case QUrlPrivate::InvalidIPv4AddressError:
+ return QString(); // doesn't happen yet
+ case QUrlPrivate::InvalidIPv6AddressError:
+ return QStringLiteral("Invalid IPv6 address");
+ case QUrlPrivate::InvalidIPvFutureError:
+ return QStringLiteral("Invalid IPvFuture address");
+ case QUrlPrivate::HostMissingEndBracket:
+ return QStringLiteral("Expected '[' to match ']' in hostname");
+
+ case QUrlPrivate::InvalidPortError:
+ case QUrlPrivate::PortEmptyError:
+ return QStringLiteral("Invalid port or port number out of range");
+
+ case QUrlPrivate::PathContainsColonBeforeSlash:
+ return QStringLiteral("Path component contains ':' before any '/'");
+ }
+ return QStringLiteral("<unknown error>");
}
/*!
diff --git a/src/corelib/io/qurl_p.h b/src/corelib/io/qurl_p.h
index 6a0af1c90a..d9207bd809 100644
--- a/src/corelib/io/qurl_p.h
+++ b/src/corelib/io/qurl_p.h
@@ -57,24 +57,6 @@
QT_BEGIN_NAMESPACE
-struct QUrlErrorInfo {
- inline QUrlErrorInfo() : _source(0), _message(0), _expected(0), _found(0)
- { }
-
- const char *_source;
- const char *_message;
- char _expected;
- char _found;
-
- inline void setParams(const char *source, const char *message, char expected, char found)
- {
- _source = source;
- _message = message;
- _expected = expected;
- _found = found;
- }
-};
-
class QUrlPrivate
{
public:
@@ -92,6 +74,24 @@ public:
Fragment = 0x80
};
+ enum ErrorCode {
+ InvalidSchemeError = 0x000,
+ SchemeEmptyError,
+
+ InvalidRegNameError = 0x800,
+ InvalidIPv4AddressError,
+ InvalidIPv6AddressError,
+ InvalidIPvFutureError,
+ HostMissingEndBracket,
+
+ InvalidPortError = 0x1000,
+ PortEmptyError,
+
+ PathContainsColonBeforeSlash = 0x2000,
+
+ NoError = 0xffff
+ };
+
QUrlPrivate();
QUrlPrivate(const QUrlPrivate &copy);
@@ -143,6 +143,9 @@ public:
QString query;
QString fragment;
+ ushort errorCode;
+ ushort errorSupplement;
+
// not used for:
// - Port (port == -1 means absence)
// - Path (there's no path delimiter, so we optimize its use out of existence)
@@ -152,9 +155,6 @@ public:
// UserName, Password, Path, Query, and Fragment never contain errors in TolerantMode.
// Those flags are set only by the strict parser.
uchar sectionHasError;
-
- mutable QUrlErrorInfo errorInfo;
- QString createErrorString();
};
diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp
index 680cc3b137..259e7570fa 100644
--- a/tests/auto/corelib/io/qurl/tst_qurl.cpp
+++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp
@@ -1615,8 +1615,7 @@ void tst_QUrl::isValid()
QVERIFY(url.isValid());
url.setAuthority("strange;hostname");
QVERIFY(!url.isValid());
- QEXPECT_FAIL("", "QUrl::errorString not reimplemented", Continue);
- QVERIFY(url.errorString().contains("invalid hostname"));
+ QVERIFY(url.errorString().contains("Hostname contains invalid characters"));
}
{
@@ -1629,8 +1628,8 @@ void tst_QUrl::isValid()
QVERIFY(url.isValid());
url.setHost("stuff;1");
QVERIFY(!url.isValid());
- QEXPECT_FAIL("", "QUrl::errorString not reimplemented", Continue);
- QVERIFY(url.errorString().contains("invalid hostname"));
+ QVERIFY2(url.errorString().contains("Hostname contains invalid characters"),
+ qPrintable(url.errorString()));
}
}
@@ -2164,6 +2163,7 @@ void tst_QUrl::setPort()
QTest::ignoreMessage(QtWarningMsg, "QUrl::setPort: Out of range");
url.setPort(65536);
QCOMPARE(url.port(), -1);
+ QVERIFY(url.errorString().contains("out of range"));
}
}
@@ -2219,7 +2219,6 @@ void tst_QUrl::errorString()
QVERIFY(!u.isValid());
QString errorString = "Invalid URL \"http://strange<username>@bad_hostname/\": "
"error at position 14: expected end of URL, but found '<'";
- QEXPECT_FAIL("", "errorString not implemented yet", Abort);
QCOMPARE(u.errorString(), errorString);
}