From f8ad76417d2c52ef1392302e493d7a13b811915b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Tue, 12 Jul 2011 09:09:53 +0200 Subject: [PATCH 01/12] Added macro to check if the malloc hooks should be volatile. The malloc hooks changed in glibc 2.14, and are now volatile. Note also that the malloc hooks are now marked as deprecated. Ref: http://sourceware.org/bugzilla/show_bug.cgi?id=11781 http://repo.or.cz/w/glibc.git/commitdiff/a33c44639a8784589c8386ac0b56baf6d0beca32 http://repo.or.cz/w/glibc.git/commitdiff/cf6bbbd701380accba8f709cf88b7bb160131e32 http://repo.or.cz/w/glibc.git/commitdiff/7d17596c198f11fa85cbcf9587443f262e63b616 Change-Id: Iab80bea7a7f5d72ac96cacd75468eefbfd9b8326 Reviewed-on: http://codereview.qt.nokia.com/1497 Reviewed-by: Qt Sanity Bot Reviewed-by: Olivier Goffart --- .../exceptionsafety_objects/oomsimulator.h | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/tests/auto/exceptionsafety_objects/oomsimulator.h b/tests/auto/exceptionsafety_objects/oomsimulator.h index a85a65eac5..3de181b57a 100644 --- a/tests/auto/exceptionsafety_objects/oomsimulator.h +++ b/tests/auto/exceptionsafety_objects/oomsimulator.h @@ -64,6 +64,13 @@ static void my_terminate_handler() #ifdef __GLIBC__ /* Use glibc's memory allocation hooks */ +// From glibc 2.14, the malloc hook variables are declared volatile. +// Note: The malloc hook implementation is marked as deprecated. + +#if !defined(__MALLOC_HOOK_VOLATILE) +# define __MALLOC_HOOK_VOLATILE +#endif + /* our hooks */ static void *my_malloc_hook(size_t, const void *); static void *my_realloc_hook(void *, size_t, const void *); @@ -71,16 +78,17 @@ static void *my_memalign_hook(size_t, size_t, const void *); static void my_free_hook(void *, const void *); /* original hooks. */ -static void *(*old_malloc_hook)(size_t, const void *); -static void *(*old_realloc_hook)(void *, size_t, const void *); -static void *(*old_memalign_hook)(size_t, size_t, const void *); -static void (*old_free_hook)(void *, const void *); +static void *(*__MALLOC_HOOK_VOLATILE old_malloc_hook)(size_t, const void *); +static void *(*__MALLOC_HOOK_VOLATILE old_realloc_hook)(void *, size_t, const void *); +static void *(*__MALLOC_HOOK_VOLATILE old_memalign_hook)(size_t, size_t, const void *); +static void (*__MALLOC_HOOK_VOLATILE old_free_hook)(void *, const void *); /* initializer function */ static void my_init_hook(); /* Override initialising hook from the C library. */ -void (*__malloc_initialize_hook) (void) = my_init_hook; + +void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook) (void) = my_init_hook; static void disableHooks() { From 9fea02400c450b4b1a031506655d8c4b165cecd6 Mon Sep 17 00:00:00 2001 From: Jedrzej Nowacki Date: Fri, 10 Jun 2011 10:47:07 +0200 Subject: [PATCH 02/12] Cleanup reference counting in QDateTimePrivate class. It is better to use QSharedData which is a common interface for ref counting. Change-Id: I990476d6763901bf383f241bce16fe26665b021c Reviewed-on: http://codereview.qt.nokia.com/1439 Reviewed-by: Qt Sanity Bot Reviewed-by: Olivier Goffart --- src/corelib/tools/qdatetime_p.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/corelib/tools/qdatetime_p.h b/src/corelib/tools/qdatetime_p.h index 8d931dccc0..c20f2123f9 100644 --- a/src/corelib/tools/qdatetime_p.h +++ b/src/corelib/tools/qdatetime_p.h @@ -76,17 +76,16 @@ QT_BEGIN_NAMESPACE -class QDateTimePrivate +class QDateTimePrivate : public QSharedData { public: enum Spec { LocalUnknown = -1, LocalStandard = 0, LocalDST = 1, UTC = 2, OffsetFromUTC = 3}; QDateTimePrivate() : spec(LocalUnknown), utcOffset(0) {} QDateTimePrivate(const QDateTimePrivate &other) - : date(other.date), time(other.time), spec(other.spec), utcOffset(other.utcOffset) + : QSharedData(other), date(other.date), time(other.time), spec(other.spec), utcOffset(other.utcOffset) {} - QAtomicInt ref; QDate date; QTime time; Spec spec; From ad35a41739c8e1fb6db62ed37b764448b2e59ece Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 11 Jul 2011 23:16:58 +0200 Subject: [PATCH 03/12] Refactor QByteArray to allow for const data Similar refactoring as done for QString. Make shared_null read-only, and add support for compile time generated QByteArrayData. Add support for properly reserving capacity. Change-Id: Ie4c41d4caac7b3b4bb1aef40c1c860a30b82edb8 Reviewed-on: http://codereview.qt.nokia.com/1484 Reviewed-by: Qt Sanity Bot Reviewed-by: Olivier Goffart --- src/corelib/tools/qbytearray.cpp | 311 ++++++++++++----------- src/corelib/tools/qbytearray.h | 132 +++++++--- tests/auto/qbytearray/tst_qbytearray.cpp | 30 ++- 3 files changed, 285 insertions(+), 188 deletions(-) diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 767b71338f..0c5276f442 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -57,7 +57,7 @@ #include #include -#define IS_RAW_DATA(d) ((d)->data != (d)->array) +#define IS_RAW_DATA(d) ((d)->offset != 0) QT_BEGIN_NAMESPACE @@ -546,7 +546,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) qWarning("qUncompress: Input data is corrupted"); return QByteArray(); } - QByteArray::Data *p = static_cast(qRealloc(d.data(), sizeof(QByteArray::Data) + alloc)); + QByteArray::Data *p = static_cast(qRealloc(d.data(), sizeof(QByteArray::Data) + alloc + 1)); if (!p) { // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS qWarning("qUncompress: could not allocate enough memory to uncompress data"); @@ -554,8 +554,9 @@ QByteArray qUncompress(const uchar* data, int nbytes) } d.take(); // realloc was successful d.reset(p); + d->offset = 0; - int res = ::uncompress((uchar*)d->array, &len, + int res = ::uncompress((uchar*)d->data(), &len, (uchar*)data+4, nbytes-4); switch (res) { @@ -566,7 +567,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) qWarning("qUncompress: Input data is corrupted"); return QByteArray(); } - QByteArray::Data *p = static_cast(qRealloc(d.data(), sizeof(QByteArray::Data) + len)); + QByteArray::Data *p = static_cast(qRealloc(d.data(), sizeof(QByteArray::Data) + len + 1)); if (!p) { // we are not allowed to crash here when compiling with QT_NO_EXCEPTIONS qWarning("qUncompress: could not allocate enough memory to uncompress data"); @@ -576,9 +577,11 @@ QByteArray qUncompress(const uchar* data, int nbytes) d.reset(p); } d->ref = 1; - d->alloc = d->size = len; - d->data = d->array; - d->array[len] = 0; + d->size = len; + d->alloc = len; + d->capacityReserved = false; + d->offset = 0; + d->data()[len] = 0; return QByteArray(d.take(), 0, 0); @@ -611,10 +614,10 @@ static inline char qToLower(char c) return c; } -QByteArray::Data QByteArray::shared_null = {Q_BASIC_ATOMIC_INITIALIZER(1), - 0, 0, shared_null.array, {0} }; -QByteArray::Data QByteArray::shared_empty = { Q_BASIC_ATOMIC_INITIALIZER(1), - 0, 0, shared_empty.array, {0} }; +QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), + 0, 0, 0, { 0 } }, { 0 } }; +QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), + 0, 0, 0, { 0 } }, { 0 } }; /*! \class QByteArray @@ -896,15 +899,15 @@ QByteArray &QByteArray::operator=(const char *str) { Data *x; if (!str) { - x = &shared_null; + x = const_cast(&shared_null.ba); } else if (!*str) { - x = &shared_empty; + x = const_cast(&shared_empty.ba); } else { int len = qstrlen(str); if (d->ref != 1 || len > d->alloc || (len < d->size && len < d->alloc >> 1)) realloc(len); x = d; - memcpy(x->data, str, len + 1); // include null terminator + memcpy(x->data(), str, len + 1); // include null terminator x->size = len; } x->ref.ref(); @@ -1294,19 +1297,20 @@ void QByteArray::chop(int n) QByteArray::QByteArray(const char *str) { if (!str) { - d = &shared_null; + d = const_cast(&shared_null.ba); } else if (!*str) { - d = &shared_empty; + d = const_cast(&shared_empty.ba); } else { int len = qstrlen(str); - d = static_cast(qMalloc(sizeof(Data)+len)); + d = static_cast(qMalloc(sizeof(Data) + len + 1)); Q_CHECK_PTR(d); - d->ref = 0;; - d->alloc = d->size = len; - d->data = d->array; - memcpy(d->array, str, len+1); // include null terminator + d->ref = 1; + d->size = len; + d->alloc = len; + d->capacityReserved = false; + d->offset = 0; + memcpy(d->data(), str, len+1); // include null terminator } - d->ref.ref(); } /*! @@ -1323,19 +1327,20 @@ QByteArray::QByteArray(const char *str) QByteArray::QByteArray(const char *data, int size) { if (!data) { - d = &shared_null; + d = const_cast(&shared_null.ba); } else if (size <= 0) { - d = &shared_empty; + d = const_cast(&shared_empty.ba); } else { - d = static_cast(qMalloc(sizeof(Data) + size)); + d = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 0; - d->alloc = d->size = size; - d->data = d->array; - memcpy(d->array, data, size); - d->array[size] = '\0'; + d->ref = 1; + d->size = size; + d->alloc = size; + d->capacityReserved = false; + d->offset = 0; + memcpy(d->data(), data, size); + d->data()[size] = '\0'; } - d->ref.ref(); } /*! @@ -1348,17 +1353,18 @@ QByteArray::QByteArray(const char *data, int size) QByteArray::QByteArray(int size, char ch) { if (size <= 0) { - d = &shared_null; + d = const_cast(&shared_null.ba); } else { - d = static_cast(qMalloc(sizeof(Data)+size)); + d = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 0; - d->alloc = d->size = size; - d->data = d->array; - d->array[size] = '\0'; - memset(d->array, ch, size); + d->ref = 1; + d->size = size; + d->alloc = size; + d->capacityReserved = false; + d->offset = 0; + memset(d->data(), ch, size); + d->data()[size] = '\0'; } - d->ref.ref(); } /*! @@ -1369,12 +1375,14 @@ QByteArray::QByteArray(int size, char ch) QByteArray::QByteArray(int size, Qt::Initialization) { - d = static_cast(qMalloc(sizeof(Data)+size)); + d = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); d->ref = 1; - d->alloc = d->size = size; - d->data = d->array; - d->array[size] = '\0'; + d->size = size; + d->alloc = size; + d->capacityReserved = false; + d->offset = 0; + d->data()[size] = '\0'; } /*! @@ -1392,13 +1400,20 @@ QByteArray::QByteArray(int size, Qt::Initialization) void QByteArray::resize(int size) { - if (size <= 0) { - Data *x = &shared_empty; - x->ref.ref(); + if (size < 0) + size = 0; + + if (d->offset && d->ref == 1 && size < d->size) { + d->size = size; + return; + } + + if (size == 0 && !d->capacityReserved) { + Data *x = const_cast(&shared_empty.ba); if (!d->ref.deref()) qFree(d); d = x; - } else if (d == &shared_null) { + } else if (d == &shared_null.ba || d == &shared_empty.ba) { // // Optimize the idiom: // QByteArray a; @@ -1407,22 +1422,21 @@ void QByteArray::resize(int size) // which is used in place of the Qt 3 idiom: // QByteArray a(sz); // - Data *x = static_cast(qMalloc(sizeof(Data)+size)); + Data *x = static_cast(qMalloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(x); x->ref = 1; - x->alloc = x->size = size; - x->data = x->array; - x->array[size] = '\0'; - (void) d->ref.deref(); // cannot be 0, x points to shared_null + x->size = size; + x->alloc = size; + x->capacityReserved = false; + x->offset = 0; + x->data()[size] = '\0'; d = x; } else { - if (d->ref != 1 || size > d->alloc || (size < d->size && size < d->alloc >> 1)) + if (d->ref != 1 || size > d->alloc || (!d->capacityReserved && size < d->size && size < d->alloc >> 1)) realloc(qAllocMore(size, sizeof(Data))); if (d->alloc >= size) { d->size = size; - if (d->data == d->array) { - d->array[size] = '\0'; - } + d->data()[size] = '\0'; } } } @@ -1442,29 +1456,30 @@ QByteArray &QByteArray::fill(char ch, int size) { resize(size < 0 ? d->size : size); if (d->size) - memset(d->data, ch, d->size); + memset(d->data(), ch, d->size); return *this; } void QByteArray::realloc(int alloc) { - if (d->ref != 1 || d->data != d->array) { - Data *x = static_cast(qMalloc(sizeof(Data) + alloc)); + if (d->ref != 1 || d->offset) { + Data *x = static_cast(qMalloc(sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); - x->size = qMin(alloc, d->size); - ::memcpy(x->array, d->data, x->size); - x->array[x->size] = '\0'; x->ref = 1; + x->size = qMin(alloc, d->size); x->alloc = alloc; - x->data = x->array; + x->capacityReserved = d->capacityReserved; + x->offset = 0; + ::memcpy(x->data(), d->data(), x->size); + x->data()[x->size] = '\0'; if (!d->ref.deref()) qFree(d); d = x; } else { - Data *x = static_cast(qRealloc(d, sizeof(Data) + alloc)); + Data *x = static_cast(qRealloc(d, sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); x->alloc = alloc; - x->data = x->array; + x->offset = 0; d = x; } } @@ -1486,7 +1501,7 @@ void QByteArray::expand(int i) QByteArray QByteArray::nulTerminated() const { // is this fromRawData? - if (d->data == d->array) + if (!d->offset) return *this; // no, then we're sure we're zero terminated QByteArray copy(*this); @@ -1517,9 +1532,9 @@ QByteArray QByteArray::nulTerminated() const QByteArray &QByteArray::prepend(const QByteArray &ba) { - if ((d == &shared_null || d == &shared_empty) && !IS_RAW_DATA(ba.d)) { + if ((d == &shared_null.ba || d == &shared_empty.ba) && !IS_RAW_DATA(ba.d)) { *this = ba; - } else if (ba.d != &shared_null) { + } else if (ba.d != &shared_null.ba) { QByteArray tmp = *this; *this = ba; append(tmp); @@ -1550,10 +1565,10 @@ QByteArray &QByteArray::prepend(const char *str, int len) if (str) { if (d->ref != 1 || d->size + len > d->alloc) realloc(qAllocMore(d->size + len, sizeof(Data))); - memmove(d->data+len, d->data, d->size); - memcpy(d->data, str, len); + memmove(d->data()+len, d->data(), d->size); + memcpy(d->data(), str, len); d->size += len; - d->data[d->size] = '\0'; + d->data()[d->size] = '\0'; } return *this; } @@ -1568,10 +1583,10 @@ QByteArray &QByteArray::prepend(char ch) { if (d->ref != 1 || d->size + 1 > d->alloc) realloc(qAllocMore(d->size + 1, sizeof(Data))); - memmove(d->data+1, d->data, d->size); - d->data[0] = ch; + memmove(d->data()+1, d->data(), d->size); + d->data()[0] = ch; ++d->size; - d->data[d->size] = '\0'; + d->data()[d->size] = '\0'; return *this; } @@ -1601,14 +1616,14 @@ QByteArray &QByteArray::prepend(char ch) QByteArray &QByteArray::append(const QByteArray &ba) { - if ((d == &shared_null || d == &shared_empty) && !IS_RAW_DATA(ba.d)) { + if ((d == &shared_null.ba || d == &shared_empty.ba) && !IS_RAW_DATA(ba.d)) { *this = ba; - } else if (ba.d != &shared_null) { + } else if (ba.d != &shared_null.ba) { if (d->ref != 1 || d->size + ba.d->size > d->alloc) realloc(qAllocMore(d->size + ba.d->size, sizeof(Data))); - memcpy(d->data + d->size, ba.d->data, ba.d->size); + memcpy(d->data() + d->size, ba.d->data(), ba.d->size); d->size += ba.d->size; - d->data[d->size] = '\0'; + d->data()[d->size] = '\0'; } return *this; } @@ -1640,7 +1655,7 @@ QByteArray& QByteArray::append(const char *str) int len = qstrlen(str); if (d->ref != 1 || d->size + len > d->alloc) realloc(qAllocMore(d->size + len, sizeof(Data))); - memcpy(d->data + d->size, str, len + 1); // include null terminator + memcpy(d->data() + d->size, str, len + 1); // include null terminator d->size += len; } return *this; @@ -1665,9 +1680,9 @@ QByteArray &QByteArray::append(const char *str, int len) if (str && len) { if (d->ref != 1 || d->size + len > d->alloc) realloc(qAllocMore(d->size + len, sizeof(Data))); - memcpy(d->data + d->size, str, len); // include null terminator + memcpy(d->data() + d->size, str, len); // include null terminator d->size += len; - d->data[d->size] = '\0'; + d->data()[d->size] = '\0'; } return *this; } @@ -1682,8 +1697,8 @@ QByteArray& QByteArray::append(char ch) { if (d->ref != 1 || d->size + 1 > d->alloc) realloc(qAllocMore(d->size + 1, sizeof(Data))); - d->data[d->size++] = ch; - d->data[d->size] = '\0'; + d->data()[d->size++] = ch; + d->data()[d->size] = '\0'; return *this; } @@ -1724,7 +1739,7 @@ static inline QByteArray &qbytearray_insert(QByteArray *ba, QByteArray &QByteArray::insert(int i, const QByteArray &ba) { QByteArray copy(ba); - return qbytearray_insert(this, i, copy.d->data, copy.d->size); + return qbytearray_insert(this, i, copy.d->data(), copy.d->size); } /*! @@ -1812,7 +1827,7 @@ QByteArray &QByteArray::remove(int pos, int len) if (pos + len >= d->size) { resize(pos); } else { - memmove(d->data + pos, d->data + pos + len, d->size - pos - len); + memmove(d->data() + pos, d->data() + pos + len, d->size - pos - len); resize(d->size - len); } return *this; @@ -1832,7 +1847,7 @@ QByteArray &QByteArray::replace(int pos, int len, const QByteArray &after) { if (len == after.d->size && (pos + len <= d->size)) { detach(); - memmove(d->data + pos, after.d->data, len*sizeof(char)); + memmove(d->data() + pos, after.d->data(), len*sizeof(char)); return *this; } else { QByteArray copy(after); @@ -1869,7 +1884,7 @@ QByteArray &QByteArray::replace(int pos, int len, const char *after, int alen) { if (len == alen && (pos + len <= d->size)) { detach(); - memcpy(d->data + pos, after, len*sizeof(char)); + memcpy(d->data() + pos, after, len*sizeof(char)); return *this; } else { remove(pos, len); @@ -1936,13 +1951,13 @@ QByteArray &QByteArray::replace(const char *before, int bsize, const char *after // protect against before or after being part of this const char *a = after; const char *b = before; - if (after >= d->data && after < d->data + d->size) { + if (after >= d->data() && after < d->data() + d->size) { char *copy = (char *)malloc(asize); Q_CHECK_PTR(copy); memcpy(copy, after, asize); a = copy; } - if (before >= d->data && before < d->data + d->size) { + if (before >= d->data() && before < d->data() + d->size) { char *copy = (char *)malloc(bsize); Q_CHECK_PTR(copy); memcpy(copy, before, bsize); @@ -2019,7 +2034,7 @@ QByteArray &QByteArray::replace(const char *before, int bsize, const char *after resize(newlen); len = newlen; } - d = this->d->data; + d = this->d->data(); while(pos) { pos--; @@ -2192,19 +2207,19 @@ QByteArray QByteArray::repeated(int times) const if (result.d->alloc != resultSize) return QByteArray(); // not enough memory - memcpy(result.d->data, d->data, d->size); + memcpy(result.d->data(), d->data(), d->size); int sizeSoFar = d->size; - char *end = result.d->data + sizeSoFar; + char *end = result.d->data() + sizeSoFar; const int halfResultSize = resultSize >> 1; while (sizeSoFar <= halfResultSize) { - memcpy(end, result.d->data, sizeSoFar); + memcpy(end, result.d->data(), sizeSoFar); end += sizeSoFar; sizeSoFar <<= 1; } - memcpy(end, result.d->data, resultSize - sizeSoFar); - result.d->data[resultSize] = '\0'; + memcpy(end, result.d->data(), resultSize - sizeSoFar); + result.d->data()[resultSize] = '\0'; result.d->size = resultSize; return result; } @@ -2231,13 +2246,13 @@ int QByteArray::indexOf(const QByteArray &ba, int from) const if (ol == 0) return from; if (ol == 1) - return indexOf(*ba.d->data, from); + return indexOf(*ba.d->data(), from); const int l = d->size; if (from > d->size || ol + from > l) return -1; - return qFindByteArray(d->data, d->size, from, ba.d->data, ol); + return qFindByteArray(d->data(), d->size, from, ba.d->data(), ol); } /*! \fn int QByteArray::indexOf(const QString &str, int from) const @@ -2279,7 +2294,7 @@ int QByteArray::indexOf(const char *c, int from) const if (ol == 0) return from; - return qFindByteArray(d->data, d->size, from, c, ol); + return qFindByteArray(d->data(), d->size, from, c, ol); } /*! @@ -2300,11 +2315,11 @@ int QByteArray::indexOf(char ch, int from) const if (from < 0) from = qMax(from + d->size, 0); if (from < d->size) { - const char *n = d->data + from - 1; - const char *e = d->data + d->size; + const char *n = d->data() + from - 1; + const char *e = d->data() + d->size; while (++n != e) if (*n == ch) - return n - d->data; + return n - d->data(); } return -1; } @@ -2361,9 +2376,9 @@ int QByteArray::lastIndexOf(const QByteArray &ba, int from) const { const int ol = ba.d->size; if (ol == 1) - return lastIndexOf(*ba.d->data, from); + return lastIndexOf(*ba.d->data(), from); - return lastIndexOfHelper(d->data, d->size, ba.d->data, ol, from); + return lastIndexOfHelper(d->data(), d->size, ba.d->data(), ol, from); } /*! \fn int QByteArray::lastIndexOf(const QString &str, int from) const @@ -2400,7 +2415,7 @@ int QByteArray::lastIndexOf(const char *str, int from) const if (ol == 1) return lastIndexOf(*str, from); - return lastIndexOfHelper(d->data, d->size, str, ol, from); + return lastIndexOfHelper(d->data(), d->size, str, ol, from); } /*! @@ -2424,8 +2439,8 @@ int QByteArray::lastIndexOf(char ch, int from) const else if (from > d->size) from = d->size-1; if (from >= 0) { - const char *b = d->data; - const char *n = d->data + from + 1; + const char *b = d->data(); + const char *n = d->data() + from + 1; while (n-- != b) if (*n == ch) return n - b; @@ -2479,8 +2494,8 @@ int QByteArray::count(const char *str) const int QByteArray::count(char ch) const { int num = 0; - const char *i = d->data + d->size; - const char *b = d->data; + const char *i = d->data() + d->size; + const char *b = d->data(); while (i != b) if (*--i == ch) ++num; @@ -2509,7 +2524,7 @@ bool QByteArray::startsWith(const QByteArray &ba) const return true; if (d->size < ba.d->size) return false; - return memcmp(d->data, ba.d->data, ba.d->size) == 0; + return memcmp(d->data(), ba.d->data(), ba.d->size) == 0; } /*! \overload @@ -2524,7 +2539,7 @@ bool QByteArray::startsWith(const char *str) const int len = qstrlen(str); if (d->size < len) return false; - return qstrncmp(d->data, str, len) == 0; + return qstrncmp(d->data(), str, len) == 0; } /*! \overload @@ -2536,7 +2551,7 @@ bool QByteArray::startsWith(char ch) const { if (d->size == 0) return false; - return d->data[0] == ch; + return d->data()[0] == ch; } /*! @@ -2554,7 +2569,7 @@ bool QByteArray::endsWith(const QByteArray &ba) const return true; if (d->size < ba.d->size) return false; - return memcmp(d->data + d->size - ba.d->size, ba.d->data, ba.d->size) == 0; + return memcmp(d->data() + d->size - ba.d->size, ba.d->data(), ba.d->size) == 0; } /*! \overload @@ -2569,7 +2584,7 @@ bool QByteArray::endsWith(const char *str) const int len = qstrlen(str); if (d->size < len) return false; - return qstrncmp(d->data + d->size - len, str, len) == 0; + return qstrncmp(d->data() + d->size - len, str, len) == 0; } /*! \overload @@ -2581,7 +2596,7 @@ bool QByteArray::endsWith(char ch) const { if (d->size == 0) return false; - return d->data[d->size - 1] == ch; + return d->data()[d->size - 1] == ch; } /*! @@ -2603,7 +2618,7 @@ QByteArray QByteArray::left(int len) const return *this; if (len < 0) len = 0; - return QByteArray(d->data, len); + return QByteArray(d->data(), len); } /*! @@ -2625,7 +2640,7 @@ QByteArray QByteArray::right(int len) const return *this; if (len < 0) len = 0; - return QByteArray(d->data + d->size - len, len); + return QByteArray(d->data() + d->size - len, len); } /*! @@ -2644,7 +2659,7 @@ QByteArray QByteArray::right(int len) const QByteArray QByteArray::mid(int pos, int len) const { - if (d == &shared_null || d == &shared_empty || pos >= d->size) + if (d == &shared_null.ba || d == &shared_empty.ba || pos >= d->size) return QByteArray(); if (len < 0) len = d->size - pos; @@ -2656,7 +2671,7 @@ QByteArray QByteArray::mid(int pos, int len) const len = d->size - pos; if (pos == 0 && len == d->size) return *this; - return QByteArray(d->data + pos, len); + return QByteArray(d->data() + pos, len); } /*! @@ -2715,7 +2730,7 @@ void QByteArray::clear() { if (!d->ref.deref()) qFree(d); - d = &shared_null; + d = const_cast(&shared_null.ba); d->ref.ref(); } @@ -3106,10 +3121,10 @@ QByteArray QByteArray::simplified() const if (d->size == 0) return *this; QByteArray result(d->size, Qt::Uninitialized); - const char *from = d->data; + const char *from = d->data(); const char *fromend = from + d->size; int outc=0; - char *to = result.d->data; + char *to = result.d->data(); for (;;) { while (from!=fromend && isspace(uchar(*from))) from++; @@ -3145,7 +3160,7 @@ QByteArray QByteArray::trimmed() const { if (d->size == 0) return *this; - const char *s = d->data; + const char *s = d->data(); if (!isspace(uchar(*s)) && !isspace(uchar(s[d->size-1]))) return *this; int start = 0; @@ -3158,8 +3173,7 @@ QByteArray QByteArray::trimmed() const } int l = end - start + 1; if (l <= 0) { - shared_empty.ref.ref(); - return QByteArray(&shared_empty, 0, 0); + return QByteArray(const_cast(&shared_empty.ba), 0, 0); } return QByteArray(s+start, l); } @@ -3190,8 +3204,8 @@ QByteArray QByteArray::leftJustified(int width, char fill, bool truncate) const if (padlen > 0) { result.resize(len+padlen); if (len) - memcpy(result.d->data, d->data, len); - memset(result.d->data+len, fill, padlen); + memcpy(result.d->data(), d->data(), len); + memset(result.d->data()+len, fill, padlen); } else { if (truncate) result = left(width); @@ -3227,8 +3241,8 @@ QByteArray QByteArray::rightJustified(int width, char fill, bool truncate) const if (padlen > 0) { result.resize(len+padlen); if (len) - memcpy(result.d->data+padlen, data(), len); - memset(result.d->data, fill, padlen); + memcpy(result.d->data()+padlen, data(), len); + memset(result.d->data(), fill, padlen); } else { if (truncate) result = left(width); @@ -3238,7 +3252,7 @@ QByteArray QByteArray::rightJustified(int width, char fill, bool truncate) const return result; } -bool QByteArray::isNull() const { return d == &shared_null; } +bool QByteArray::isNull() const { return d == &shared_null.ba; } /*! @@ -3562,13 +3576,13 @@ QByteArray QByteArray::toBase64() const char *out = tmp.data(); while (i < d->size) { int chunk = 0; - chunk |= int(uchar(d->data[i++])) << 16; + chunk |= int(uchar(d->data()[i++])) << 16; if (i == d->size) { padlen = 2; } else { - chunk |= int(uchar(d->data[i++])) << 8; + chunk |= int(uchar(d->data()[i++])) << 8; if (i == d->size) padlen = 1; - else chunk |= int(uchar(d->data[i++])); + else chunk |= int(uchar(d->data()[i++])); } int j = (chunk & 0x00fc0000) >> 18; @@ -3864,17 +3878,20 @@ QByteArray QByteArray::number(double n, char f, int prec) QByteArray QByteArray::fromRawData(const char *data, int size) { - Data *x = static_cast(qMalloc(sizeof(Data))); - Q_CHECK_PTR(x); - if (data) { - x->data = const_cast(data); + Data *x; + if (!data) { + x = const_cast(&shared_null.ba); + } else if (!size) { + x = const_cast(&shared_empty.ba); } else { - x->data = x->array; - size = 0; + x = static_cast(qMalloc(sizeof(Data) + 1)); + Q_CHECK_PTR(x); + x->ref = 1; + x->size = size; + x->alloc = 0; + x->capacityReserved = false; + x->offset = data - (x->d + sizeof(qptrdiff)); } - x->ref = 1; - x->alloc = x->size = size; - *x->array = '\0'; return QByteArray(x, 0, 0); } @@ -3898,13 +3915,13 @@ QByteArray &QByteArray::setRawData(const char *data, uint size) *this = fromRawData(data, size); } else { if (data) { - d->data = const_cast(data); + d->size = size; + d->offset = data - (d->d + sizeof(qptrdiff)); } else { - d->data = d->array; - size = 0; + d->offset = 0; + d->size = 0; + *d->data() = 0; } - d->alloc = d->size = size; - *d->array = '\0'; } return *this; } @@ -4013,7 +4030,7 @@ QByteArray QByteArray::toHex() const { QByteArray hex(d->size * 2, Qt::Uninitialized); char *hexData = hex.data(); - const uchar *data = (const uchar *)d->data; + const uchar *data = (const uchar *)d->data(); for (int i = 0; i < d->size; ++i) { int j = (data[i] >> 4) & 0xf; if (j <= 9) diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 26cf2af59c..dbac302d05 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -42,7 +42,7 @@ #ifndef QBYTEARRAY_H #define QBYTEARRAY_H -#include +#include #include #include @@ -119,18 +119,65 @@ class QString; class QDataStream; template class QList; +struct QByteArrayData +{ + QtPrivate::RefCount ref; + int size; + uint alloc : 31; + uint capacityReserved : 1; + union { + qptrdiff offset; // will always work as we add/subtract from a ushort ptr + char d[sizeof(qptrdiff)]; + }; + inline char *data() { return d + sizeof(qptrdiff) + offset; } + inline const char *data() const { return d + sizeof(qptrdiff) + offset; } +}; + +template struct QConstByteArrayData +{ + const QByteArrayData ba; + const char data[n]; +}; + +template struct QConstByteArrayDataPtr +{ + const QConstByteArrayData *ptr; +}; + + +#if defined(Q_COMPILER_LAMBDA) +# define QByteArrayLiteral(str) ([]() { \ + enum { Size = sizeof(str) }; \ + static const QConstByteArrayData qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZER(-1), Size -1, 0, 0, { 0 } }, str }; \ + QConstByteArrayDataPtr holder = { &qbytearray_literal }; \ + return holder; }()) + +#elif defined(Q_CC_GNU) +// We need to create a QByteArrayData in the .rodata section of memory +// and the only way to do that is to create a "static const" variable. +// To do that, we need the __extension__ {( )} trick which only GCC supports + +# define QByteArrayLiteral(str) \ + __extension__ ({ \ + enum { Size = sizeof(str) }; \ + static const QConstByteArrayData qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZER(-1), Size -1, 0, 0, { 0 } }, str }; \ + QConstByteArrayDataPtr holder = { &qbytearray_literal }; \ + holder; }) +#endif + +#ifndef QByteArrayLiteral +// no lambdas, not GCC, use const char * instead + +# define QByteArrayLiteral(str) (str) +#endif + + class Q_CORE_EXPORT QByteArray { private: - struct Data { - QBasicAtomicInt ref; - int alloc, size; - // ### Qt 5.0: We need to add the missing capacity bit - // (like other tool classes have), to maintain the - // reserved memory on resize. - char *data; - char array[1]; - }; + typedef QByteArrayData Data; public: inline QByteArray(); @@ -330,10 +377,17 @@ public: int length() const { return d->size; } bool isNull() const; + template + inline QByteArray(const QConstByteArrayData &dd) + : d(const_cast(&dd.str)) {} + template + Q_DECL_CONSTEXPR inline QByteArray(QConstByteArrayDataPtr dd) + : d(const_cast(&dd.ptr->ba)) {} + private: operator QNoImplicitBoolCast() const; - static Data shared_null; - static Data shared_empty; + static QConstByteArrayData<1> shared_null; + static QConstByteArrayData<1> shared_empty; Data *d; QByteArray(Data *dd, int /*dummy*/, int /*dummy*/) : d(dd) {} void realloc(int alloc); @@ -348,34 +402,34 @@ public: inline DataPtr &data_ptr() { return d; } }; -inline QByteArray::QByteArray(): d(&shared_null) { d->ref.ref(); } +inline QByteArray::QByteArray(): d(const_cast(&shared_null.ba)) { d->ref.ref(); } inline QByteArray::~QByteArray() { if (!d->ref.deref()) qFree(d); } inline int QByteArray::size() const { return d->size; } inline char QByteArray::at(int i) const -{ Q_ASSERT(i >= 0 && i < size()); return d->data[i]; } +{ Q_ASSERT(i >= 0 && i < size()); return d->data()[i]; } inline char QByteArray::operator[](int i) const -{ Q_ASSERT(i >= 0 && i < size()); return d->data[i]; } +{ Q_ASSERT(i >= 0 && i < size()); return d->data()[i]; } inline char QByteArray::operator[](uint i) const -{ Q_ASSERT(i < uint(size())); return d->data[i]; } +{ Q_ASSERT(i < uint(size())); return d->data()[i]; } inline bool QByteArray::isEmpty() const { return d->size == 0; } #ifndef QT_NO_CAST_FROM_BYTEARRAY inline QByteArray::operator const char *() const -{ return d->data; } +{ return d->data(); } inline QByteArray::operator const void *() const -{ return d->data; } +{ return d->data(); } #endif inline char *QByteArray::data() -{ detach(); return d->data; } +{ detach(); return d->data(); } inline const char *QByteArray::data() const -{ return d->data; } +{ return d->data(); } inline const char *QByteArray::constData() const -{ return d->data; } +{ return d->data(); } inline void QByteArray::detach() -{ if (d->ref != 1 || d->data != d->array) realloc(d->size); } +{ if (d->ref != 1 || d->offset) realloc(d->size); } inline bool QByteArray::isDetached() const { return d->ref == 1; } inline QByteArray::QByteArray(const QByteArray &a) : d(a.d) @@ -385,10 +439,10 @@ inline int QByteArray::capacity() const { return d->alloc; } inline void QByteArray::reserve(int asize) -{ if (d->ref != 1 || asize > d->alloc) realloc(asize); } +{ if (d->ref != 1 || asize > d->alloc) realloc(asize); d->capacityReserved = true; } inline void QByteArray::squeeze() -{ if (d->size < d->alloc) realloc(d->size); } +{ if (d->size < d->alloc) realloc(d->size); d->capacityReserved = false; } class Q_CORE_EXPORT QByteRef { QByteArray &a; @@ -398,25 +452,25 @@ class Q_CORE_EXPORT QByteRef { friend class QByteArray; public: inline operator char() const - { return i < a.d->size ? a.d->data[i] : char(0); } + { return i < a.d->size ? a.d->data()[i] : char(0); } inline QByteRef &operator=(char c) { if (i >= a.d->size) a.expand(i); else a.detach(); - a.d->data[i] = c; return *this; } + a.d->data()[i] = c; return *this; } inline QByteRef &operator=(const QByteRef &c) { if (i >= a.d->size) a.expand(i); else a.detach(); - a.d->data[i] = c.a.d->data[c.i]; return *this; } + a.d->data()[i] = c.a.d->data()[c.i]; return *this; } inline bool operator==(char c) const - { return a.d->data[i] == c; } + { return a.d->data()[i] == c; } inline bool operator!=(char c) const - { return a.d->data[i] != c; } + { return a.d->data()[i] != c; } inline bool operator>(char c) const - { return a.d->data[i] > c; } + { return a.d->data()[i] > c; } inline bool operator>=(char c) const - { return a.d->data[i] >= c; } + { return a.d->data()[i] >= c; } inline bool operator<(char c) const - { return a.d->data[i] < c; } + { return a.d->data()[i] < c; } inline bool operator<=(char c) const - { return a.d->data[i] <= c; } + { return a.d->data()[i] <= c; } }; inline QByteRef QByteArray::operator[](int i) @@ -424,17 +478,17 @@ inline QByteRef QByteArray::operator[](int i) inline QByteRef QByteArray::operator[](uint i) { return QByteRef(*this, i); } inline QByteArray::iterator QByteArray::begin() -{ detach(); return d->data; } +{ detach(); return d->data(); } inline QByteArray::const_iterator QByteArray::begin() const -{ return d->data; } +{ return d->data(); } inline QByteArray::const_iterator QByteArray::constBegin() const -{ return d->data; } +{ return d->data(); } inline QByteArray::iterator QByteArray::end() -{ detach(); return d->data + d->size; } +{ detach(); return d->data() + d->size; } inline QByteArray::const_iterator QByteArray::end() const -{ return d->data + d->size; } +{ return d->data() + d->size; } inline QByteArray::const_iterator QByteArray::constEnd() const -{ return d->data + d->size; } +{ return d->data() + d->size; } inline QByteArray &QByteArray::operator+=(char c) { return append(c); } inline QByteArray &QByteArray::operator+=(const char *s) diff --git a/tests/auto/qbytearray/tst_qbytearray.cpp b/tests/auto/qbytearray/tst_qbytearray.cpp index e2f64b84f8..78b655419e 100644 --- a/tests/auto/qbytearray/tst_qbytearray.cpp +++ b/tests/auto/qbytearray/tst_qbytearray.cpp @@ -145,6 +145,8 @@ private slots: void byteRefDetaching() const; void reserve(); + + void literals(); }; tst_QByteArray::tst_QByteArray() @@ -1527,14 +1529,38 @@ void tst_QByteArray::reserve() QVERIFY(qba.capacity() == capacity); char *data = qba.data(); - // FIXME count from 0 to make it fail - for (int i = 1; i < capacity; i++) { + for (int i = 0; i < capacity; i++) { qba.resize(i); QVERIFY(capacity == qba.capacity()); QVERIFY(data == qba.data()); } } +void tst_QByteArray::literals() +{ +#if defined(Q_COMPILER_LAMBDA) || defined(Q_CC_GNU) + QByteArray str(QByteArrayLiteral("abcd")); + + QVERIFY(str.length() == 4); + QVERIFY(str == "abcd"); + QVERIFY(str.data_ptr()->ref == -1); + QVERIFY(str.data_ptr()->offset == 0); + + const char *s = str.constData(); + QByteArray str2 = str; + QVERIFY(str2.constData() == s); + + // detach on non const access + QVERIFY(str.data() != s); + + QVERIFY(str2.constData() == s); + QVERIFY(str2.data() != s); + +#else + QSKIP("Only tested on c++0x compliant compiler or gcc", SkipAll); +#endif +} + const char globalChar = '1'; QTEST_APPLESS_MAIN(tst_QByteArray) From 451f3b3785ea5d08f5092978b6ebe17f25ef7a88 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Sat, 18 Jun 2011 15:53:53 +0100 Subject: [PATCH 04/12] Add the ability to verify a chain of certificates Currently it is only possible to verify a certificate chain when connecting to a server. This change makes it possible to verify a chain at any time. Change-Id: Ib70ad7b81418f880e995f391b82ce59561ededb8 Merge-request: 11 Reviewed-by: Peter Hartmann Reviewed-on: http://codereview.qt.nokia.com/1509 --- src/network/ssl/qsslcertificate.cpp | 12 ++ src/network/ssl/qsslcertificate.h | 3 + src/network/ssl/qsslsocket_openssl.cpp | 181 +++++++++++++++--- src/network/ssl/qsslsocket_openssl_p.h | 2 + .../ssl/qsslsocket_openssl_symbols.cpp | 6 + .../ssl/qsslsocket_openssl_symbols_p.h | 4 + .../qsslcertificate/tst_qsslcertificate.cpp | 66 +++++++ .../auto/qsslcertificate/verify-certs/README | 2 + .../qsslcertificate/verify-certs/cacert.pem | 23 +++ .../test-addons-mozilla-org-cert.pem | 34 ++++ .../test-intermediate-ca-cert.pem | 66 +++++++ .../test-intermediate-is-ca-cert.pem | 53 +++++ .../test-intermediate-not-ca-cert.pem | 54 ++++++ .../verify-certs/test-ocsp-good-cert.pem | 67 +++++++ 14 files changed, 547 insertions(+), 26 deletions(-) create mode 100644 tests/auto/qsslcertificate/verify-certs/README create mode 100644 tests/auto/qsslcertificate/verify-certs/cacert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem create mode 100644 tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 839a253b99..41e54b2eb6 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -121,6 +121,7 @@ #include #include #include +#include #include #include #include @@ -666,6 +667,17 @@ QList QSslCertificate::fromData(const QByteArray &data, QSsl::E : QSslCertificatePrivate::certificatesFromDer(data); } +/*! + Verifies a certificate chain. If a hostName is specified then the certificate is + also checked to see if it is valid for the specified hostName. + Note that the first certificate in the list should be the leaf certificate of + the chain to be verified. + */ +QList QSslCertificate::verify(QList certificateChain, const QString &hostName) +{ + return QSslSocketBackendPrivate::verify(certificateChain, hostName); +} + void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) { if (!data.isEmpty()) { diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h index 4de84dd4ba..a057d7a17d 100644 --- a/src/network/ssl/qsslcertificate.h +++ b/src/network/ssl/qsslcertificate.h @@ -62,6 +62,7 @@ QT_MODULE(Network) class QDateTime; class QIODevice; +class QSslError; class QSslKey; class QStringList; template class QMultiMap; @@ -122,6 +123,8 @@ public: static QList fromData( const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); + static QList verify(QList certificateChain, const QString &hostName = QString()); + Qt::HANDLE handle() const; private: diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index c7e938a705..84e5200043 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1283,33 +1283,14 @@ bool QSslSocketBackendPrivate::startHandshake() // if we're the server, don't check CN if (mode == QSslSocket::SslClientMode) { QString peerName = (verificationPeerName.isEmpty () ? q->peerName() : verificationPeerName); - QStringList commonNameList = configuration.peerCertificate.subjectInfo(QSslCertificate::CommonName); - bool matched = false; - foreach (const QString &commonName, commonNameList) { - if (isMatchingHostname(commonName.toLower(), peerName.toLower())) { - matched = true; - break; - } - } - - if (!matched) { - foreach (const QString &altName, configuration.peerCertificate - .alternateSubjectNames().values(QSsl::DnsEntry)) { - if (isMatchingHostname(altName.toLower(), peerName.toLower())) { - matched = true; - break; - } - } - } - - if (!matched) { - // No matches in common names or alternate names. - QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); - errors << error; - emit q->peerVerifyError(error); - if (q->state() != QAbstractSocket::ConnectedState) - return false; + if (!isMatchingHostname(configuration.peerCertificate, peerName)) { + // No matches in common names or alternate names. + QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); + errors << error; + emit q->peerVerifyError(error); + if (q->state() != QAbstractSocket::ConnectedState) + return false; } } } else { @@ -1444,6 +1425,25 @@ QString QSslSocketBackendPrivate::getErrorsFromOpenSsl() return errorString; } +bool QSslSocketBackendPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName) +{ + QStringList commonNameList = cert.subjectInfo(QSslCertificate::CommonName); + + foreach (const QString &commonName, commonNameList) { + if (isMatchingHostname(commonName.toLower(), peerName.toLower())) { + return true; + } + } + + foreach (const QString &altName, cert.alternateSubjectNames().values(QSsl::DnsEntry)) { + if (isMatchingHostname(altName.toLower(), peerName.toLower())) { + return true; + } + } + + return false; +} + bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QString &hostname) { int wildcard = cn.indexOf(QLatin1Char('*')); @@ -1484,4 +1484,133 @@ bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QStri return true; } +QList QSslSocketBackendPrivate::verify(QList certificateChain, const QString &hostName) +{ + QList errors; + if (certificateChain.count() <= 0) { + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + // Setup the store with the default CA certificates + X509_STORE *certStore = q_X509_STORE_new(); + if (!certStore) { + qWarning() << "Unable to create certificate store"; + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + QList expiredCerts; + + foreach (const QSslCertificate &caCertificate, QSslSocket::defaultCaCertificates()) { + // add expired certs later, so that the + // valid ones are used before the expired ones + if (!caCertificate.isValid()) { + expiredCerts.append(caCertificate); + } else { + q_X509_STORE_add_cert(certStore, (X509 *)caCertificate.handle()); + } + } + + bool addExpiredCerts = true; +#if defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5) + //On Leopard SSL does not work if we add the expired certificates. + if (QSysInfo::MacintoshVersion == QSysInfo::MV_10_5) + addExpiredCerts = false; +#endif + // now add the expired certs + if (addExpiredCerts) { + foreach (const QSslCertificate &caCertificate, expiredCerts) { + q_X509_STORE_add_cert(certStore, (X509 *)caCertificate.handle()); + } + } + + _q_sslErrorList()->mutex.lock(); + + // Register a custom callback to get all verification errors. + X509_STORE_set_verify_cb_func(certStore, q_X509Callback); + + // Build the chain of intermediate certificates + STACK_OF(X509) *intermediates = 0; + if (certificateChain.length() > 1) { + intermediates = (STACK_OF(X509) *) q_sk_new_null(); + + if (!intermediates) { + q_X509_STORE_free(certStore); + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + bool first = true; + foreach (const QSslCertificate &cert, certificateChain) { + if (first) { + first = false; + continue; + } +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + q_sk_push( (_STACK *)intermediates, (X509 *)cert.handle()); +#else + q_sk_push( (STACK *)intermediates, (X509 *)cert.handle()); +#endif + } + } + + X509_STORE_CTX *storeContext = q_X509_STORE_CTX_new(); + if (!storeContext) { + q_X509_STORE_free(certStore); + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + if (!q_X509_STORE_CTX_init(storeContext, certStore, (X509 *)certificateChain[0].handle(), intermediates)) { + q_X509_STORE_CTX_free(storeContext); + q_X509_STORE_free(certStore); + errors << QSslError(QSslError::UnspecifiedError); + return errors; + } + + // Now we can actually perform the verification of the chain we have built. + // We ignore the result of this function since we process errors via the + // callback. + (void) q_X509_verify_cert(storeContext); + + q_X509_STORE_CTX_free(storeContext); +#if OPENSSL_VERSION_NUMBER >= 0x10000000L + q_sk_free( (_STACK *) intermediates); +#else + q_sk_free( (STACK *) intermediates); +#endif + + // Now process the errors + const QList > errorList = _q_sslErrorList()->errors; + _q_sslErrorList()->errors.clear(); + + _q_sslErrorList()->mutex.unlock(); + + // Translate the errors + if (QSslCertificatePrivate::isBlacklisted(certificateChain[0])) { + QSslError error(QSslError::CertificateBlacklisted, certificateChain[0]); + errors << error; + } + + // Check the certificate name against the hostname if one was specified + if ((!hostName.isEmpty()) && (!isMatchingHostname(certificateChain[0], hostName))) { + // No matches in common names or alternate names. + QSslError error(QSslError::HostNameMismatch, certificateChain[0]); + errors << error; + } + + // Translate errors from the error list into QSslErrors. + for (int i = 0; i < errorList.size(); ++i) { + const QPair &errorAndDepth = errorList.at(i); + int err = errorAndDepth.first; + int depth = errorAndDepth.second; + errors << _q_OpenSSL_to_QSslError(err, certificateChain.value(depth)); + } + + q_X509_STORE_free(certStore); + + return errors; +} + QT_END_NAMESPACE diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index 7e489a4eff..080ad0064c 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -120,7 +120,9 @@ public: static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher); static QList STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); + static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); + static QList verify(QList certificateChain, const QString &hostName); static QString getErrorsFromOpenSsl(); }; diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp index 31afab003f..be8da0eaf0 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols.cpp +++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp @@ -157,9 +157,13 @@ DEFINEFUNC(void, RSA_free, RSA *a, a, return, DUMMYARG) DEFINEFUNC(int, sk_num, STACK *a, a, return -1, return) DEFINEFUNC2(void, sk_pop_free, STACK *a, a, void (*b)(void*), b, return, DUMMYARG) #if OPENSSL_VERSION_NUMBER >= 0x10000000L +DEFINEFUNC(_STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(void, sk_push, _STACK *a, a, void *b, b, return, DUMMYARG) DEFINEFUNC(void, sk_free, _STACK *a, a, return, DUMMYARG) DEFINEFUNC2(void *, sk_value, STACK *a, a, int b, b, return 0, return) #else +DEFINEFUNC(STACK *, sk_new_null, DUMMYARG, DUMMYARG, return 0, return) +DEFINEFUNC2(void, sk_push, STACK *a, a, void *b, b, return, DUMMYARG) DEFINEFUNC(void, sk_free, STACK *a, a, return, DUMMYARG) DEFINEFUNC2(char *, sk_value, STACK *a, a, int b, b, return 0, return) #endif @@ -710,6 +714,8 @@ bool q_resolveOpenSslSymbols() RESOLVEFUNC(RAND_seed) RESOLVEFUNC(RAND_status) RESOLVEFUNC(RSA_free) + RESOLVEFUNC(sk_new_null) + RESOLVEFUNC(sk_push) RESOLVEFUNC(sk_free) RESOLVEFUNC(sk_num) RESOLVEFUNC(sk_pop_free) diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h index cd3aa07bc0..a1db6d9320 100644 --- a/src/network/ssl/qsslsocket_openssl_symbols_p.h +++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h @@ -263,9 +263,13 @@ void q_RSA_free(RSA *a); int q_sk_num(STACK *a); void q_sk_pop_free(STACK *a, void (*b)(void *)); #if OPENSSL_VERSION_NUMBER >= 0x10000000L +_STACK *q_sk_new_null(); +void q_sk_push(_STACK *st, void *data); void q_sk_free(_STACK *a); void * q_sk_value(STACK *a, int b); #else +STACK *q_sk_new_null(); +void q_sk_push(STACK *st, void *data); void q_sk_free(STACK *a); char * q_sk_value(STACK *a, int b); #endif diff --git a/tests/auto/qsslcertificate/tst_qsslcertificate.cpp b/tests/auto/qsslcertificate/tst_qsslcertificate.cpp index f12af0275c..451465df0c 100644 --- a/tests/auto/qsslcertificate/tst_qsslcertificate.cpp +++ b/tests/auto/qsslcertificate/tst_qsslcertificate.cpp @@ -116,6 +116,7 @@ private slots: void toText(); void multipleCommonNames(); void subjectAndIssuerAttributes(); + void verify(); // ### add tests for certificate bundles (multiple certificates concatenated into a single // structure); both PEM and DER formatted @@ -901,6 +902,71 @@ void tst_QSslCertificate::subjectAndIssuerAttributes() QVERIFY(attributes.contains(QByteArray("1.3.6.1.4.1.311.60.2.1.3"))); } +void tst_QSslCertificate::verify() +{ + QList errors; + QList toVerify; + + // Empty chain is unspecified error + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.count() == 1); + QVERIFY(errors[0] == QSslError(QSslError::UnspecifiedError)); + errors.clear(); + + // Verify a valid cert signed by a CA + QList caCerts = QSslCertificate::fromPath(SRCDIR "verify-certs/cacert.pem"); + QSslSocket::addDefaultCaCertificate(caCerts.first()); + + toVerify = QSslCertificate::fromPath(SRCDIR "verify-certs/test-ocsp-good-cert.pem"); + + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.count() == 0); + errors.clear(); + + // Test a blacklisted certificate + toVerify = QSslCertificate::fromPath(SRCDIR "verify-certs/test-addons-mozilla-org-cert.pem"); + errors = QSslCertificate::verify(toVerify); + bool foundBlack = false; + foreach (const QSslError &error, errors) { + if (error.error() == QSslError::CertificateBlacklisted) { + foundBlack = true; + break; + } + } + QVERIFY(foundBlack); + errors.clear(); + + // This one is expired and untrusted + toVerify = QSslCertificate::fromPath(SRCDIR "more-certificates/cert-large-serial-number.pem"); + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.contains(QSslError(QSslError::SelfSignedCertificate, toVerify[0]))); + QVERIFY(errors.contains(QSslError(QSslError::CertificateExpired, toVerify[0]))); + errors.clear(); + toVerify.clear(); + + // This one is signed by a valid cert, but the signer is not a valid CA + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-not-ca-cert.pem").first(); + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-ocsp-good-cert.pem").first(); + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.contains(QSslError(QSslError::InvalidCaCertificate, toVerify[1]))); + toVerify.clear(); + + // This one is signed by a valid cert, and the signer is a valid CA + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-is-ca-cert.pem").first(); + toVerify << QSslCertificate::fromPath(SRCDIR "verify-certs/test-intermediate-ca-cert.pem").first(); + errors = QSslCertificate::verify(toVerify); + QVERIFY(errors.length() == 0); + + // Recheck the above with hostname validation + errors = QSslCertificate::verify(toVerify, QLatin1String("example.com")); + QVERIFY(errors.length() == 0); + + // Recheck the above with a bad hostname + errors = QSslCertificate::verify(toVerify, QLatin1String("fail.example.com")); + QVERIFY(errors.contains(QSslError(QSslError::HostNameMismatch, toVerify[0]))); + toVerify.clear(); +} + #endif // QT_NO_OPENSSL QTEST_MAIN(tst_QSslCertificate) diff --git a/tests/auto/qsslcertificate/verify-certs/README b/tests/auto/qsslcertificate/verify-certs/README new file mode 100644 index 0000000000..87cb293ef6 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/README @@ -0,0 +1,2 @@ +openssl verify -CAfile cacert.pem -untrusted test-intermediate-ca-cert.pem test-intermediate-is-ca-cert.pem +openssl verify -CAfile cacert.pem -untrusted test-ocsp-good-cert.pem test-intermediate-not-ca-cert.pem diff --git a/tests/auto/qsslcertificate/verify-certs/cacert.pem b/tests/auto/qsslcertificate/verify-certs/cacert.pem new file mode 100644 index 0000000000..0e06285766 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/cacert.pem @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID6zCCAtOgAwIBAgIJALIOhqebAhS9MA0GCSqGSIb3DQEBBQUAMIGrMSYwJAYD +VQQDEx1XZXN0cG9pbnQgQ2VydGlmaWNhdGUgVGVzdCBDQTETMBEGA1UECBMKTGFu +Y2FzaGlyZTELMAkGA1UEBhMCVUsxHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUu +Y29tMUAwPgYDVQQKEzdXZXN0cG9pbnQgQ2VydGlmaWNhdGUgVGVzdCBSb290IENl +cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTExMDYyNjE5MzYzOVoXDTExMDcyNjE5 +MzYzOVowgasxJjAkBgNVBAMTHVdlc3Rwb2ludCBDZXJ0aWZpY2F0ZSBUZXN0IENB +MRMwEQYDVQQIEwpMYW5jYXNoaXJlMQswCQYDVQQGEwJVSzEdMBsGCSqGSIb3DQEJ +ARYOY2FAZXhhbXBsZS5jb20xQDA+BgNVBAoTN1dlc3Rwb2ludCBDZXJ0aWZpY2F0 +ZSBUZXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQCoP3znXuxcIAWkyu51aPWwXYX0kSPnBO1gcJj6 +xQa4ycOHv9Cs9XTVTGvpj4aoc6lP+6/jUe14cVCR7018zHRe7u5g4ozO1aZqISqS +Y4hdWkTSFPmFoiyXkACl0ZGwcfv6QdFhNnK4COBrff4D6lndfQUZu8CnRYxlKGuR +1vGiUcJ88t0dDmMEFEdYNtlDnYlxXHbTS4VdRb2u3EGFzV24ENJwgqYuFrBAG/+N +TRXahWMsdfP0whCYJOsaNBwXaoeoxGlYz35gMU8A8AFmYOJLohsWqHcHmMV3X6hn +aKGnL3nOa8zlNKNr948Dwenucaggf5KquDCHVf2Ms+ROxlfTAgMBAAGjEDAOMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAGmY4+AeMyx+FkpPxeNY3DAH +Jys0WfndLZFABARYfKdJE1dqQi3uXaRkKMkV7npb46cw92jmwFT+v9rHd88UmgMs +KGJNWjARD6Ai1dzenMNYBJz9GFkDJ0Pr4Gqj2tR9JuzEOdxss+nZ4r6vhC+/yeAB +4jGT4QMuYU+14Rfsv5Aw0HjbcH955zTy1pJ6ck9OWWyzET8ALxz+RTFOok/4r4++ +yhE5Hh8+2aE52AcZqKa4hKXdVBCb9oewl93h3rmYcA/Yz36w+GRkSnOPZUgDDL5D +HKnICDidtf9ZZBZ4iJxaBg4iCraeuei20V+0g+9/1aoIWJ5TrelVYVCx8O0x+E0= +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem new file mode 100644 index 0000000000..07123e8577 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-addons-mozilla-org-cert.pem @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF+DCCBOCgAwIBAgIRAJI51TSPQNFpWnRUcOHyP0MwDQYJKoZIhvcNAQEFBQAw +gZcxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtl +IENpdHkxHjAcBgNVBAoTFVRoZSBVU0VSVFJVU1QgTmV0d29yazEhMB8GA1UECxMY +aHR0cDovL3d3dy51c2VydHJ1c3QuY29tMR8wHQYDVQQDExZVVE4tVVNFUkZpcnN0 +LUhhcmR3YXJlMB4XDTExMDMxNTAwMDAwMFoXDTE0MDMxNDIzNTk1OVowgeIxCzAJ +BgNVBAYTAlVTMQ4wDAYDVQQREwUzODQ3NzEQMA4GA1UECBMHRmxvcmlkYTEQMA4G +A1UEBxMHRW5nbGlzaDEXMBUGA1UECRMOU2VhIFZpbGxhZ2UgMTAxFDASBgNVBAoT +C0dvb2dsZSBMdGQuMRMwEQYDVQQLEwpUZWNoIERlcHQuMSgwJgYDVQQLEx9Ib3N0 +ZWQgYnkgR1RJIEdyb3VwIENvcnBvcmF0aW9uMRQwEgYDVQQLEwtQbGF0aW51bVNT +TDEbMBkGA1UEAxMSYWRkb25zLm1vemlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAq8ZtNvMVc3iDc850hdWu7LLw4CQfE4O4IKy7mv6Iu6uh +HQsfRQCqSbc1Nwxq70dMudG+41cSBI2Sx7bsAby22seBOCCtcoXmDvyBbAetaHY4 +xUTXzMZKxZc+ZPRR5vB+suxW9yWCTUmYyxaY3SPxiZHRF5dAmSbW4qIrXt+9ifIb +GlMtzFBBetA9KgxVcBQB6VhJEHoLk4KL4R7tOoAQgs6WijTwzNfTubRQh1VUCbid +QihVAOWMNVS/3SWRRrcN5V2DqOWL+4TkPK522sRDK1t0C/i+XWjxeFu1zn3xXZlA +2sruOIFQvpihbLgkrfOvjA/XESgshBhMfbXZjzC1GwIDAQABo4IB8DCCAewwHwYD +VR0jBBgwFoAUoXJfJhsomEOVXQc31YWWnUvSw0UwHQYDVR0OBBYEFN2A0lQ990xw +yqOw3TR6MuToO1o7MA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB0GA1Ud +JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBGBgNVHSAEPzA9MDsGDCsGAQQBsjEB +AgEDBDArMCkGCCsGAQUFBwIBFh1odHRwczovL3NlY3VyZS5jb21vZG8uY29tL0NQ +UzB7BgNVHR8EdDByMDigNqA0hjJodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9VVE4t +VVNFUkZpcnN0LUhhcmR3YXJlLmNybDA2oDSgMoYwaHR0cDovL2NybC5jb21vZG8u +bmV0L1VUTi1VU0VSRmlyc3QtSGFyZHdhcmUuY3JsMHEGCCsGAQUFBwEBBGUwYzA7 +BggrBgEFBQcwAoYvaHR0cDovL2NydC5jb21vZG9jYS5jb20vVVROQWRkVHJ1c3RT +ZXJ2ZXJDQS5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmNvbW9kb2NhLmNv +bTA1BgNVHREELjAsghJhZGRvbnMubW96aWxsYS5vcmeCFnd3dy5hZGRvbnMubW96 +aWxsYS5vcmcwDQYJKoZIhvcNAQEFBQADggEBADM7YxX8sewULJPddZTegVrZTpm+ ++0qkOVVNoUB63hMqh6k3z+jV+63Re21vjCCHglTmV0m8ICiEzdYB2ZOLF24jZuWE +yIA/xqFwgOTsTR35/JFac2IpmvcgHGHgizmfyrx+jd282bHjn57fFVORIVIL2Roj +D2Y226yTlkqjpSLPKfeimaj2ttlArtl+tvZYLpusNspkj2VS3IacgqtuUEvaX/oF +AIgwDt6NVr+BR409BuKyYpJnj57ImrLlBrhwJLh3fCMKOMN5CNixUZ2slRHHQBee +oxyP8hGnaCfaSQWEGHxYLQFnXOWfoSm7SjlFL78Rqnmi7bTUtWVDt5NGitM= +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem new file mode 100644 index 0000000000..ab4c2dacf6 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-intermediate-ca-cert.pem @@ -0,0 +1,66 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 28 (0x1c) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jun 26 19:36:42 2011 GMT + Not After : Jun 23 19:36:42 2021 GMT + Subject: ST=Lancashire, C=UK/emailAddress=test@example.com, O=Test intermediate CA + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:a7:ed:3f:86:ee:ad:f8:b7:60:44:63:fc:1b:2c: + 79:3c:90:8b:47:b8:51:b5:3e:96:6d:5d:f1:97:b3: + de:90:b2:9a:aa:9e:54:7e:2b:b1:6d:91:ae:20:29: + b6:69:7b:8a:e5:6c:41:10:b6:d3:73:4c:20:30:38: + 57:c9:c8:ef:67:7e:76:39:c4:1c:df:6b:73:4e:2b: + 21:2d:79:5a:c5:60:5e:85:11:52:3b:8e:ef:b0:e5: + 2f:0c:e8:a3:fc:05:27:91:08:64:ea:2e:5a:f2:82: + 0c:08:48:bc:bc:ca:60:02:1c:6a:38:eb:c8:02:a6: + f7:e8:c8:31:20:29:e6:e4:8d + Exponent: 65537 (0x10001) + X509v3 extensions: + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 94:1a:91:6a:05:41:0c:40:f9:46:35:e9:46:15:f1:3e:e4:d9: + 50:d6:0e:14:d8:1f:85:9e:a6:98:a2:db:db:ee:ff:56:55:6c: + 46:fd:0e:b0:03:79:a6:96:0f:c1:85:c3:3e:68:e0:17:10:a6: + 9c:10:34:88:96:f7:c8:ef:32:31:24:f4:3c:2d:eb:51:08:d6: + 87:83:f7:db:68:43:ed:d9:af:46:a2:48:74:d2:9d:c9:af:0f: + 29:42:ad:a2:cf:1e:ab:50:6f:ef:33:18:d3:07:ef:13:13:10: + 50:db:a9:56:db:f4:38:c1:db:05:fa:5a:67:92:72:69:fb:7a: + 5b:ec:d4:dd:fd:a2:21:06:59:b0:0d:48:5f:53:c1:65:94:aa: + d4:4e:1c:e8:9f:b4:7d:9b:10:85:4a:b6:be:df:d8:33:b5:72: + b0:ac:46:a9:67:55:1e:3e:58:a5:52:ed:b6:4a:cb:e9:d2:e5: + f8:fe:56:b4:2c:5e:9f:3c:d1:7f:b4:eb:05:8d:46:1f:47:32: + 2b:4f:2e:ac:8d:c3:3c:eb:f1:0c:2f:3a:e0:fa:46:0b:e4:c5: + f2:03:24:70:fc:ef:1a:fd:7b:a9:9c:d9:b6:4f:0e:74:07:52: + 23:eb:cd:66:61:67:a1:91:7f:76:a2:9d:42:54:d4:c6:5e:10: + 26:eb:37:e7 +-----BEGIN CERTIFICATE----- +MIIDUDCCAjigAwIBAgIBHDANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA2MjYxOTM2NDJaFw0yMTA2MjMxOTM2NDJaMGIx +EzARBgNVBAgTCkxhbmNhc2hpcmUxCzAJBgNVBAYTAlVLMR8wHQYJKoZIhvcNAQkB +FhB0ZXN0QGV4YW1wbGUuY29tMR0wGwYDVQQKExRUZXN0IGludGVybWVkaWF0ZSBD +QTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAp+0/hu6t+LdgRGP8Gyx5PJCL +R7hRtT6WbV3xl7PekLKaqp5UfiuxbZGuICm2aXuK5WxBELbTc0wgMDhXycjvZ352 +OcQc32tzTishLXlaxWBehRFSO47vsOUvDOij/AUnkQhk6i5a8oIMCEi8vMpgAhxq +OOvIAqb36MgxICnm5I0CAwEAAaNLMEkwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUF +BzABhh1odHRwOi8vb2NzcC5leGFtcGxlLmNvbTo4ODg4LzAMBgNVHRMEBTADAQH/ +MA0GCSqGSIb3DQEBBQUAA4IBAQCUGpFqBUEMQPlGNelGFfE+5NlQ1g4U2B+FnqaY +otvb7v9WVWxG/Q6wA3mmlg/BhcM+aOAXEKacEDSIlvfI7zIxJPQ8LetRCNaHg/fb +aEPt2a9Gokh00p3Jrw8pQq2izx6rUG/vMxjTB+8TExBQ26lW2/Q4wdsF+lpnknJp ++3pb7NTd/aIhBlmwDUhfU8FllKrUThzon7R9mxCFSra+39gztXKwrEapZ1UePlil +Uu22Ssvp0uX4/la0LF6fPNF/tOsFjUYfRzIrTy6sjcM86/EMLzrg+kYL5MXyAyRw +/O8a/XupnNm2Tw50B1Ij681mYWehkX92op1CVNTGXhAm6zfn +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem new file mode 100644 index 0000000000..27945856b7 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-intermediate-is-ca-cert.pem @@ -0,0 +1,53 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 29 (0x1d) + Signature Algorithm: sha1WithRSAEncryption + Issuer: ST=Lancashire, C=UK/emailAddress=test@example.com, O=Test intermediate CA + Validity + Not Before: Jun 26 19:36:42 2011 GMT + Not After : Jun 23 19:36:42 2021 GMT + Subject: CN=example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:ba:f2:dc:f9:77:b0:ad:fd:9d:42:4f:22:15:6d: + 87:40:ed:30:8a:04:ad:ac:7a:0b:d4:7a:a4:a7:ef: + e0:e7:9b:f2:5e:62:56:24:ed:88:bd:bd:e3:64:d2: + d4:b4:01:39:b8:9e:6f:c7:b0:fc:b9:fd:a8:4d:46: + c8:9e:6a:43:82:ca:56:83:d4:4b:ea:63:d5:56:d1: + 99:46:4f:8b:28:d0:2f:db:bf:04:65:64:82:c2:61: + aa:66:50:27:e5:7a:57:e3:72:e3:ae:22:8d:92:7e: + 25:90:a2:7c:0c:04:79:c5:ab:64:58:a9:83:79:67: + 7f:72:33:cc:5f:5b:cd:74:bb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 19:e8:ce:9d:4d:ef:cc:4f:6c:e5:ab:df:aa:e2:3c:d8:6b:a8: + dd:b8:fa:50:e4:b6:04:fc:66:92:fb:e8:11:73:81:60:1a:88: + b2:18:0b:8c:89:05:47:16:50:09:be:bc:a6:3c:fe:2e:45:01: + 00:e3:27:30:72:f6:93:49:7f:d0:3b:a8:0e:cb:e7:01:95:28: + 8b:40:95:f7:b1:5b:c9:ff:26:ff:ad:4a:c4:e4:99:f7:65:fc: + e4:5e:d2:56:ea:98:42:dc:93:62:46:1a:33:53:0d:43:9d:ef: + 14:03:35:a7:13:fa:27:24:92:2f:9a:f9:0a:62:99:cc:c0:80: + 79:10 +-----BEGIN CERTIFICATE----- +MIICNjCCAZ+gAwIBAgIBHTANBgkqhkiG9w0BAQUFADBiMRMwEQYDVQQIEwpMYW5j +YXNoaXJlMQswCQYDVQQGEwJVSzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl +LmNvbTEdMBsGA1UEChMUVGVzdCBpbnRlcm1lZGlhdGUgQ0EwHhcNMTEwNjI2MTkz +NjQyWhcNMjEwNjIzMTkzNjQyWjAWMRQwEgYDVQQDEwtleGFtcGxlLmNvbTCBnzAN +BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAuvLc+Xewrf2dQk8iFW2HQO0wigStrHoL +1Hqkp+/g55vyXmJWJO2Ivb3jZNLUtAE5uJ5vx7D8uf2oTUbInmpDgspWg9RL6mPV +VtGZRk+LKNAv278EZWSCwmGqZlAn5XpX43LjriKNkn4lkKJ8DAR5xatkWKmDeWd/ +cjPMX1vNdLsCAwEAAaNIMEYwCQYDVR0TBAIwADA5BggrBgEFBQcBAQQtMCswKQYI +KwYBBQUHMAGGHWh0dHA6Ly9vY3NwLmV4YW1wbGUuY29tOjg4ODgvMA0GCSqGSIb3 +DQEBBQUAA4GBABnozp1N78xPbOWr36riPNhrqN24+lDktgT8ZpL76BFzgWAaiLIY +C4yJBUcWUAm+vKY8/i5FAQDjJzBy9pNJf9A7qA7L5wGVKItAlfexW8n/Jv+tSsTk +mfdl/ORe0lbqmELck2JGGjNTDUOd7xQDNacT+ickki+a+QpimczAgHkQ +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem new file mode 100644 index 0000000000..704346c958 --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-intermediate-not-ca-cert.pem @@ -0,0 +1,54 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 27 (0x1b) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=example.com, ST=Lancashire, C=UK/emailAddress=test@example.com, O=Some organisation + Validity + Not Before: Jun 26 19:36:42 2011 GMT + Not After : Jun 23 19:36:42 2021 GMT + Subject: CN=example.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:d7:b6:a8:07:83:27:b8:72:da:85:ad:50:bf:c2: + 3f:cc:d4:e7:97:55:b5:45:aa:d2:cb:df:b6:34:6f: + c2:8f:86:e2:15:8a:cd:3e:af:f5:c0:f9:2e:61:80: + 70:17:d3:db:0f:8a:e6:2c:a8:e3:12:2d:92:e1:8d: + 10:e0:e7:30:94:98:ec:b0:21:c3:86:f7:ff:29:58: + 2b:ab:b1:23:e4:ca:66:66:6a:18:b5:73:dc:c7:44: + 04:30:55:bf:f9:3b:74:f6:de:bd:d9:ef:46:b0:15: + 56:3b:43:cc:55:c2:cc:2e:5d:17:f8:04:dc:3d:bf: + 1b:cc:0a:41:61:c8:35:02:1b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 18:e2:06:f3:4b:42:46:b6:29:d1:8d:50:ef:5b:8b:e5:33:24: + f3:f1:3e:58:4d:7f:3e:51:e9:c4:a2:cb:64:5f:d1:51:ce:8d: + b6:15:63:23:30:4b:7c:70:30:61:ce:1f:70:bb:99:63:5b:15: + d9:ce:aa:da:65:87:66:ab:ad:64:e8:09:37:ea:79:d0:3a:a2: + e0:cf:0b:1b:a7:35:3d:f8:45:4c:4b:96:15:ec:fe:64:9f:e0: + 1d:04:52:35:a1:b4:7e:31:34:84:7e:e6:e0:58:1e:14:02:df: + 68:f6:b6:eb:dc:10:eb:da:fe:8e:06:ab:52:b7:ca:15:c3:8d: + 5a:8a +-----BEGIN CERTIFICATE----- +MIICSTCCAbKgAwIBAgIBGzANBgkqhkiG9w0BAQUFADB1MRQwEgYDVQQDEwtleGFt +cGxlLmNvbTETMBEGA1UECBMKTGFuY2FzaGlyZTELMAkGA1UEBhMCVUsxHzAdBgkq +hkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20xGjAYBgNVBAoTEVNvbWUgb3JnYW5p +c2F0aW9uMB4XDTExMDYyNjE5MzY0MloXDTIxMDYyMzE5MzY0MlowFjEUMBIGA1UE +AxMLZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANe2qAeD +J7hy2oWtUL/CP8zU55dVtUWq0svftjRvwo+G4hWKzT6v9cD5LmGAcBfT2w+K5iyo +4xItkuGNEODnMJSY7LAhw4b3/ylYK6uxI+TKZmZqGLVz3MdEBDBVv/k7dPbevdnv +RrAVVjtDzFXCzC5dF/gE3D2/G8wKQWHINQIbAgMBAAGjSDBGMAkGA1UdEwQCMAAw +OQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8vb2NzcC5leGFtcGxl +LmNvbTo4ODg4LzANBgkqhkiG9w0BAQUFAAOBgQAY4gbzS0JGtinRjVDvW4vlMyTz +8T5YTX8+UenEostkX9FRzo22FWMjMEt8cDBhzh9wu5ljWxXZzqraZYdmq61k6Ak3 +6nnQOqLgzwsbpzU9+EVMS5YV7P5kn+AdBFI1obR+MTSEfubgWB4UAt9o9rbr3BDr +2v6OBqtSt8oVw41aig== +-----END CERTIFICATE----- diff --git a/tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem b/tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem new file mode 100644 index 0000000000..1e138cef8a --- /dev/null +++ b/tests/auto/qsslcertificate/verify-certs/test-ocsp-good-cert.pem @@ -0,0 +1,67 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 1 (0x1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: CN=Westpoint Certificate Test CA, ST=Lancashire, C=UK/emailAddress=ca@example.com, O=Westpoint Certificate Test Root Certification Authority + Validity + Not Before: Jun 26 19:36:39 2011 GMT + Not After : Jun 23 19:36:39 2021 GMT + Subject: CN=example.com, ST=Lancashire, C=UK/emailAddress=test@example.com, O=Some organisation + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:c7:d8:d4:a7:c0:a0:e7:82:b4:ec:67:52:bf:50: + bf:b9:a6:f2:10:19:67:53:8c:99:5e:c8:fc:03:07: + 71:24:a3:a9:c4:49:f8:15:34:4a:45:ee:92:81:aa: + 3c:5a:1a:42:2b:db:d7:30:9e:85:e6:b8:74:bc:ae: + f0:ae:7d:05:4e:c9:0f:00:33:b2:86:ba:b6:49:9a: + 07:18:92:f9:35:69:d2:ac:39:b9:85:ac:78:99:81: + 06:f5:fa:2e:5b:18:f7:6a:16:d0:e6:f9:71:0f:b0: + 05:c4:f0:5f:ed:90:81:3c:96:f5:e3:45:73:72:5f: + ce:dc:ce:0b:56:2e:be:d2:eb + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + Authority Information Access: + OCSP - URI:http://ocsp.example.com:8888/ + + Signature Algorithm: sha1WithRSAEncryption + 76:84:40:52:b8:1d:6c:64:58:f1:10:30:03:db:16:91:da:b1: + 70:28:54:6f:0f:32:1c:84:65:b3:14:99:c6:59:f9:ec:f9:3f: + 46:1f:12:77:4e:f1:26:ad:46:4b:ee:48:78:bd:bf:2a:11:01: + 5b:02:cd:d2:6e:2d:8c:08:ea:a7:5a:18:16:71:e8:5d:c8:e7: + 7d:f2:4f:23:6c:59:3c:17:93:02:60:c0:d0:62:09:d2:a3:7d: + 90:77:6c:f7:0c:b2:e4:9e:73:d2:f8:dd:a3:0c:70:36:2f:5a: + 58:2b:2d:3e:0e:71:43:b2:14:00:e2:eb:2d:0e:09:91:47:83: + e8:02:d4:7d:5c:1f:ce:d8:f5:ad:1f:ee:82:4f:23:47:db:f4: + 71:48:1b:e1:82:f1:d1:86:db:0f:b6:bb:3a:8f:40:05:48:b7: + f2:a8:c7:c9:46:e1:ea:28:b2:02:00:90:04:00:19:32:6f:8d: + 3e:c2:67:ca:b0:80:3a:32:e3:35:92:18:a2:62:30:9d:7a:f5: + 13:3b:c7:00:3f:4e:17:a9:01:5d:a1:2c:71:76:d7:37:c8:16: + 92:f8:82:69:15:5f:7d:5e:b0:ba:0b:9f:bd:53:ad:e5:a6:b3: + bc:6b:e4:1a:79:29:31:ff:ff:a1:2d:0b:30:46:d3:a5:2d:62: + e6:be:68:df +-----BEGIN CERTIFICATE----- +MIIDYDCCAkigAwIBAgIBATANBgkqhkiG9w0BAQUFADCBqzEmMCQGA1UEAxMdV2Vz +dHBvaW50IENlcnRpZmljYXRlIFRlc3QgQ0ExEzARBgNVBAgTCkxhbmNhc2hpcmUx +CzAJBgNVBAYTAlVLMR0wGwYJKoZIhvcNAQkBFg5jYUBleGFtcGxlLmNvbTFAMD4G +A1UEChM3V2VzdHBvaW50IENlcnRpZmljYXRlIFRlc3QgUm9vdCBDZXJ0aWZpY2F0 +aW9uIEF1dGhvcml0eTAeFw0xMTA2MjYxOTM2MzlaFw0yMTA2MjMxOTM2MzlaMHUx +FDASBgNVBAMTC2V4YW1wbGUuY29tMRMwEQYDVQQIEwpMYW5jYXNoaXJlMQswCQYD +VQQGEwJVSzEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbTEaMBgGA1UE +ChMRU29tZSBvcmdhbmlzYXRpb24wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB +AMfY1KfAoOeCtOxnUr9Qv7mm8hAZZ1OMmV7I/AMHcSSjqcRJ+BU0SkXukoGqPFoa +Qivb1zCehea4dLyu8K59BU7JDwAzsoa6tkmaBxiS+TVp0qw5uYWseJmBBvX6LlsY +92oW0Ob5cQ+wBcTwX+2QgTyW9eNFc3JfztzOC1YuvtLrAgMBAAGjSDBGMAkGA1Ud +EwQCMAAwOQYIKwYBBQUHAQEELTArMCkGCCsGAQUFBzABhh1odHRwOi8vb2NzcC5l +eGFtcGxlLmNvbTo4ODg4LzANBgkqhkiG9w0BAQUFAAOCAQEAdoRAUrgdbGRY8RAw +A9sWkdqxcChUbw8yHIRlsxSZxln57Pk/Rh8Sd07xJq1GS+5IeL2/KhEBWwLN0m4t +jAjqp1oYFnHoXcjnffJPI2xZPBeTAmDA0GIJ0qN9kHds9wyy5J5z0vjdowxwNi9a +WCstPg5xQ7IUAOLrLQ4JkUeD6ALUfVwfztj1rR/ugk8jR9v0cUgb4YLx0YbbD7a7 +Oo9ABUi38qjHyUbh6iiyAgCQBAAZMm+NPsJnyrCAOjLjNZIYomIwnXr1EzvHAD9O +F6kBXaEscXbXN8gWkviCaRVffV6wugufvVOt5aazvGvkGnkpMf//oS0LMEbTpS1i +5r5o3w== +-----END CERTIFICATE----- From e11fac22c41f5f796bbfabc5088ff8a1cee3cd3b Mon Sep 17 00:00:00 2001 From: Peter Hartmann Date: Tue, 12 Jul 2011 11:52:10 +0200 Subject: [PATCH 05/12] QSslCertificate: extend documentation for new verify method Change-Id: I47e038299a7e6ef18206839ff59ecef9f0860415 Reviewed-on: http://codereview.qt.nokia.com/1510 Reviewed-by: Qt Sanity Bot Reviewed-by: Markus Goetz --- src/network/ssl/qsslcertificate.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 41e54b2eb6..94273b0754 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -668,10 +668,14 @@ QList QSslCertificate::fromData(const QByteArray &data, QSsl::E } /*! - Verifies a certificate chain. If a hostName is specified then the certificate is - also checked to see if it is valid for the specified hostName. + Verifies a certificate chain. If \a hostName is specified then the certificate is + also checked to see if it is valid for the specified host name. Note that the first certificate in the list should be the leaf certificate of the chain to be verified. + The root (CA) certificate should not be included in the list to be verified, + this will be looked up automatically either using the CA list specified by + QSslSocket::defaultCaCertificates() or, if possible, it will be loaded on demand + on Unix. */ QList QSslCertificate::verify(QList certificateChain, const QString &hostName) { From 37be55a86f78da6d177e387b79cbc5d2aacef773 Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Tue, 12 Jul 2011 12:45:02 +0200 Subject: [PATCH 06/12] QNAM HTTP: Do zero-copy for small HTTP replies by default Task-Number: QTBUG-19046 Change-Id: I34bf432c81d94787524124b7d110a00305a660c1 Reviewed-on: http://codereview.qt.nokia.com/1516 Reviewed-by: Qt Sanity Bot Reviewed-by: Martin Petersson Reviewed-by: Peter Hartmann --- .../access/qhttpnetworkconnectionchannel.cpp | 15 ++++++++++----- src/network/access/qhttpnetworkreply.cpp | 4 ++-- src/network/access/qnetworkreplyhttpimpl.cpp | 12 ++++++++++-- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index b8ed8ee567..aafdbf7774 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -448,12 +448,17 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() // the buffer in that size. // note that this call will read only from the still buffered data qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress); - bytes += haveRead; - replyPrivate->totalProgress += haveRead; - - // the user will get notified of it via progress signal - if (haveRead > 0) + if (haveRead > 0) { + bytes += haveRead; + replyPrivate->totalProgress += haveRead; + // the user will get notified of it via progress signal emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); + } else if (haveRead == 0) { + // Happens since this called in a loop. Currently no bytes available. + } else if (haveRead < 0) { + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError); + break; + } } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress && replyPrivate->bodyLength > 0) { // bulk files like images should fulfill these properties and diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index 00653b62e8..04bcd06908 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -211,7 +211,7 @@ void QHttpNetworkReply::setDownstreamLimited(bool dsl) bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer() { Q_D(QHttpNetworkReply); - return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0); + return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0 && d->statusCode == 200); } void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b) @@ -672,7 +672,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char qint64 haveRead = 0; haveRead = socket->read(b, bodyLength - contentRead); if (haveRead == -1) { - return 0; // ### error checking here; + return -1; } contentRead += haveRead; diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index df0a32dd6c..52cbaae5e0 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -777,8 +777,16 @@ void QNetworkReplyHttpImplPrivate::postRequest() if (!synchronous) { // Tell our zerocopy policy to the delegate - delegate->downloadBufferMaximumSize = - request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute).toLongLong(); + QVariant downloadBufferMaximumSizeAttribute = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute); + if (downloadBufferMaximumSizeAttribute.isValid()) { + delegate->downloadBufferMaximumSize = downloadBufferMaximumSizeAttribute.toLongLong(); + } else { + // If there is no MaximumDownloadBufferSizeAttribute set (which is for the majority + // of QNetworkRequest) then we can assume we'll do it anyway for small HTTP replies. + // This helps with performance and memory fragmentation. + delegate->downloadBufferMaximumSize = 128*1024; + } + // These atomic integers are used for signal compression delegate->pendingDownloadData = pendingDownloadDataEmissions; From 363c710bc438f9c0c4c0e1c3eeb9cc2d9d2af5db Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 12 Jul 2011 13:47:14 +0200 Subject: [PATCH 07/12] Add the #defines for C++0x features in GCC 4.6 Merge-request: 8 Reviewed-by: Olivier Goffart Change-Id: I9176f53bd98692c31e49d1d98f91641661a52044 Reviewed-on: http://codereview.qt.nokia.com/1522 Reviewed-by: Qt Sanity Bot Reviewed-by: Olivier Goffart --- src/corelib/global/qglobal.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 1358ea62df..e8e736d4f3 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -549,6 +549,7 @@ namespace QT_NAMESPACE {} # define Q_COMPILER_DEFAULT_DELETE_MEMBERS # define Q_COMPILER_CLASS_ENUM # define Q_COMPILER_INITIALIZER_LISTS +# define Q_COMPILER_ATOMICS # endif # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 /* C++0x features supported in GCC 4.5: */ @@ -556,7 +557,10 @@ namespace QT_NAMESPACE {} # endif # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 /* C++0x features supported in GCC 4.6: */ +# define Q_COMPILER_NULLPTR # define Q_COMPILER_CONSTEXPR +# define Q_COMPILER_UNRESTRICTED_UNIONS +# define Q_COMPILER_RANGE_FOR # endif # endif @@ -1129,6 +1133,12 @@ redefine to built-in booleans to make autotests work properly */ # define QT_FASTCALL #endif +#ifdef Q_COMPILER_NULLPTR +# define Q_NULLPTR nullptr +#else +# define Q_NULLPTR 0 +#endif + #ifdef Q_COMPILER_CONSTEXPR # define Q_DECL_CONSTEXPR constexpr #else From 2b5dcfcee118265ef7930cc7c5c16bff22d580fd Mon Sep 17 00:00:00 2001 From: Markus Goetz Date: Wed, 6 Jul 2011 16:08:59 +0200 Subject: [PATCH 08/12] QNAM HTTP: Re-write compression code This eliminates some code (header parsing) that can be done by zlib already. Add support for 'deflate' encoding. Also do less memory copying while uncompressing. Change-Id: I94de21e3c58b904dd91d004c375ed8cbea56cb0b Task-Number: QTBUG-13191 Reviewed-on: http://codereview.qt.nokia.com/1314 Reviewed-by: Qt Sanity Bot Reviewed-by: Martin Petersson Reviewed-by: Peter Hartmann Reviewed-by: Markus Goetz --- src/network/access/qhttpnetworkconnection.cpp | 2 +- src/network/access/qhttpnetworkconnection_p.h | 3 - .../access/qhttpnetworkconnectionchannel.cpp | 82 +----- .../access/qhttpnetworkconnectionchannel_p.h | 1 - src/network/access/qhttpnetworkreply.cpp | 235 ++++++------------ src/network/access/qhttpnetworkreply_p.h | 26 +- 6 files changed, 100 insertions(+), 249 deletions(-) diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index a8a4fd9ae7..0f1132e2a8 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -271,7 +271,7 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair) value = request.headerField("accept-encoding"); if (value.isEmpty()) { #ifndef QT_NO_COMPRESS - request.setHeaderField("Accept-Encoding", "gzip"); + request.setHeaderField("Accept-Encoding", "gzip, deflate"); request.d->autoDecompress = true; #else // if zlib is not available set this to false always diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index 0c86fd94b9..0652436b35 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -202,9 +202,6 @@ public: QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail = QString()); -#ifndef QT_NO_COMPRESS - bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete); -#endif void removeReply(QHttpNetworkReply *reply); QString hostName; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index aafdbf7774..8be876dd48 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -403,7 +403,7 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() bytes += headerBytes; // If headers were parsed successfully now it is the ReadingDataState if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) { - if (replyPrivate->isGzipped() && replyPrivate->autoDecompress) { + if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) { // remove the Content-Length from header replyPrivate->removeAutoDecompressHeader(); } else { @@ -475,30 +475,18 @@ void QHttpNetworkConnectionChannel::_q_receiveReply() { // use the traditional slower reading (for compressed encoding, chunked encoding, // no content-length etc) - QByteDataBuffer byteDatas; - qint64 haveRead = replyPrivate->readBody(socket, &byteDatas); - if (haveRead) { + qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData); + if (haveRead > 0) { bytes += haveRead; - if (replyPrivate->autoDecompress) - replyPrivate->appendCompressedReplyData(byteDatas); - else - replyPrivate->appendUncompressedReplyData(byteDatas); - - if (!replyPrivate->autoDecompress) { - replyPrivate->totalProgress += bytes; - if (replyPrivate->shouldEmitSignals()) { - // important: At the point of this readyRead(), the byteDatas list must be empty, - // else implicit sharing will trigger memcpy when the user is reading data! - emit reply->readyRead(); - emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); - } + replyPrivate->totalProgress += haveRead; + if (replyPrivate->shouldEmitSignals()) { + emit reply->readyRead(); + emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); } -#ifndef QT_NO_COMPRESS - else if (!expand(false)) { // expand a chunk if possible - // If expand() failed we can just return, it had already called connection->emitReplyError() - return; - } -#endif + } else if (haveRead == -1) { + // Some error occured + connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); + break; } } // still in ReadingDataState? This function will be called again by the socket's readyRead @@ -638,57 +626,9 @@ bool QHttpNetworkConnectionChannel::ensureConnection() return true; } - -#ifndef QT_NO_COMPRESS -bool QHttpNetworkConnectionChannel::expand(bool dataComplete) -{ - Q_ASSERT(socket); - Q_ASSERT(reply); - - qint64 total = reply->d_func()->compressedData.size(); - if (total >= CHUNK || dataComplete) { - // uncompress the data - QByteArray content, inflated; - content = reply->d_func()->compressedData; - reply->d_func()->compressedData.clear(); - - int ret = Z_OK; - if (content.size()) - ret = reply->d_func()->gunzipBodyPartially(content, inflated); - int retCheck = (dataComplete) ? Z_STREAM_END : Z_OK; - if (ret >= retCheck) { - if (inflated.size()) { - reply->d_func()->totalProgress += inflated.size(); - reply->d_func()->appendUncompressedReplyData(inflated); - if (reply->d_func()->shouldEmitSignals()) { - // important: At the point of this readyRead(), inflated must be cleared, - // else implicit sharing will trigger memcpy when the user is reading data! - emit reply->readyRead(); - emit reply->dataReadProgress(reply->d_func()->totalProgress, 0); - } - } - } else { - connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure); - return false; - } - } - return true; -} -#endif - - void QHttpNetworkConnectionChannel::allDone() { Q_ASSERT(reply); -#ifndef QT_NO_COMPRESS - // expand the whole data. - if (reply->d_func()->expectContent() && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd) { - bool expandResult = expand(true); - // If expand() failed we can just return, it had already called connection->emitReplyError() - if (!expandResult) - return; - } -#endif if (!reply) { qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt.nokia.com/"; diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h index c159f1a0c5..7a4dd07db1 100644 --- a/src/network/access/qhttpnetworkconnectionchannel_p.h +++ b/src/network/access/qhttpnetworkconnectionchannel_p.h @@ -149,7 +149,6 @@ public: bool ensureConnection(); - bool expand(bool dataComplete); void allDone(); // reply header + body have been read void handleStatus(); // called from allDone() diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp index 04bcd06908..5b174dbc05 100644 --- a/src/network/access/qhttpnetworkreply.cpp +++ b/src/network/access/qhttpnetworkreply.cpp @@ -65,6 +65,11 @@ QHttpNetworkReply::~QHttpNetworkReply() if (d->connection) { d->connection->d_func()->removeReply(this); } + +#ifndef QT_NO_COMPRESS + if (d->autoDecompress && d->isCompressed()) + inflateEnd(&d->inflateStrm); +#endif } QUrl QHttpNetworkReply::url() const @@ -252,7 +257,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl) chunkedTransferEncoding(false), connectionCloseEnabled(true), forceConnectionCloseEnabled(false), - currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false), + currentChunkSize(0), currentChunkRead(0), connection(0), autoDecompress(false), responseData(), requestIsPrepared(false) ,pipeliningUsed(false), downstreamLimited(false) ,userProvidedDownloadBuffer(0) @@ -274,11 +279,9 @@ void QHttpNetworkReplyPrivate::clearHttpLayerInformation() currentChunkRead = 0; connectionCloseEnabled = true; #ifndef QT_NO_COMPRESS - if (initInflate) + if (autoDecompress) inflateEnd(&inflateStrm); #endif - initInflate = false; - streamEnd = false; fields.clear(); } @@ -297,10 +300,10 @@ qint64 QHttpNetworkReplyPrivate::bytesAvailable() const return (state != ReadingDataState ? 0 : fragment.size()); } -bool QHttpNetworkReplyPrivate::isGzipped() +bool QHttpNetworkReplyPrivate::isCompressed() { QByteArray encoding = headerField("content-encoding"); - return qstricmp(encoding.constData(), "gzip") == 0; + return qstricmp(encoding.constData(), "gzip") == 0 || qstricmp(encoding.constData(), "deflate") == 0; } void QHttpNetworkReplyPrivate::removeAutoDecompressHeader() @@ -358,120 +361,6 @@ QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(boo return method; } -#ifndef QT_NO_COMPRESS -bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos) -{ - int method = 0; // method byte - int flags = 0; // flags byte - bool ret = false; - - // Assure two bytes in the buffer so we can peek ahead -- handle case - // where first byte of header is at the end of the buffer after the last - // gzip segment - pos = -1; - QByteArray &body = content; - int maxPos = body.size()-1; - if (maxPos < 1) { - return ret; - } - - // Peek ahead to check the gzip magic header - if (body[0] != char(gz_magic[0]) || - body[1] != char(gz_magic[1])) { - return ret; - } - pos += 2; - // Check the rest of the gzip header - if (++pos <= maxPos) - method = body[pos]; - if (pos++ <= maxPos) - flags = body[pos]; - if (method != Z_DEFLATED || (flags & RESERVED) != 0) { - return ret; - } - - // Discard time, xflags and OS code: - pos += 6; - if (pos > maxPos) - return ret; - if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field - unsigned len = (unsigned)body[++pos]; - len += ((unsigned)body[++pos])<<8; - pos += len; - if (pos > maxPos) - return ret; - } - if ((flags & ORIG_NAME) != 0) { // skip the original file name - while(++pos <= maxPos && body[pos]) {} - } - if ((flags & COMMENT) != 0) { // skip the .gz file comment - while(++pos <= maxPos && body[pos]) {} - } - if ((flags & HEAD_CRC) != 0) { // skip the header crc - pos += 2; - if (pos > maxPos) - return ret; - } - ret = (pos < maxPos); // return failed, if no more bytes left - return ret; -} - -int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated) -{ - int ret = Z_DATA_ERROR; - unsigned have; - unsigned char out[CHUNK]; - int pos = -1; - - if (!initInflate) { - // check the header - if (!gzipCheckHeader(compressed, pos)) - return ret; - // allocate inflate state - inflateStrm.zalloc = Z_NULL; - inflateStrm.zfree = Z_NULL; - inflateStrm.opaque = Z_NULL; - inflateStrm.avail_in = 0; - inflateStrm.next_in = Z_NULL; - ret = inflateInit2(&inflateStrm, -MAX_WBITS); - if (ret != Z_OK) - return ret; - initInflate = true; - streamEnd = false; - } - - //remove the header. - compressed.remove(0, pos+1); - // expand until deflate stream ends - inflateStrm.next_in = (unsigned char *)compressed.data(); - inflateStrm.avail_in = compressed.size(); - do { - inflateStrm.avail_out = sizeof(out); - inflateStrm.next_out = out; - ret = inflate(&inflateStrm, Z_NO_FLUSH); - switch (ret) { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; - // and fall through - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&inflateStrm); - initInflate = false; - return ret; - } - have = sizeof(out) - inflateStrm.avail_out; - inflated.append(QByteArray((const char *)out, have)); - } while (inflateStrm.avail_out == 0); - // clean up and return - if (ret <= Z_ERRNO || ret == Z_STREAM_END) { - inflateEnd(&inflateStrm); - initInflate = false; - } - streamEnd = (ret == Z_STREAM_END); - return ret; -} -#endif - qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket) { if (fragment.isEmpty()) { @@ -616,6 +505,24 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket) connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") || headerField("proxy-connection").toLower().contains("close")) || (majorVersion == 1 && minorVersion == 0 && connectionHeaderField.isEmpty()); + +#ifndef QT_NO_COMPRESS + if (autoDecompress && isCompressed()) { + // allocate inflate state + inflateStrm.zalloc = Z_NULL; + inflateStrm.zfree = Z_NULL; + inflateStrm.opaque = Z_NULL; + inflateStrm.avail_in = 0; + inflateStrm.next_in = Z_NULL; + // "windowBits can also be greater than 15 for optional gzip decoding. + // Add 32 to windowBits to enable zlib and gzip decoding with automatic header detection" + // http://www.zlib.net/manual.html + int ret = inflateInit2(&inflateStrm, MAX_WBITS+32); + if (ret != Z_OK) + return -1; + } +#endif + } return bytes; } @@ -712,22 +619,74 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteData qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out) { qint64 bytes = 0; + +#ifndef QT_NO_COMPRESS + // for gzip we'll allocate a temporary one that we then decompress + QByteDataBuffer *tempOutDataBuffer = (autoDecompress ? new QByteDataBuffer : out); +#else + QByteDataBuffer *tempOutDataBuffer = out; +#endif + + if (isChunked()) { // chunked transfer encoding (rfc 2616, sec 3.6) - bytes += readReplyBodyChunked(socket, out); + bytes += readReplyBodyChunked(socket, tempOutDataBuffer); } else if (bodyLength > 0) { // we have a Content-Length - bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead); + bytes += readReplyBodyRaw(socket, tempOutDataBuffer, bodyLength - contentRead); if (contentRead + bytes == bodyLength) state = AllDoneState; } else { // no content length. just read what's possible - bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable()); + bytes += readReplyBodyRaw(socket, tempOutDataBuffer, socket->bytesAvailable()); } + +#ifndef QT_NO_COMPRESS + // This is true if there is compressed encoding and we're supposed to use it. + if (autoDecompress) { + qint64 uncompressRet = uncompressBodyData(tempOutDataBuffer, out); + delete tempOutDataBuffer; + if (uncompressRet < 0) + return -1; + } +#endif + contentRead += bytes; return bytes; } +#ifndef QT_NO_COMPRESS +qint64 QHttpNetworkReplyPrivate::uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out) +{ + for (int i = 0; i < in->bufferCount(); i++) { + QByteArray &bIn = (*in)[i]; + + inflateStrm.avail_in = bIn.size(); + inflateStrm.next_in = reinterpret_cast(bIn.data()); + + do { + QByteArray bOut; + // make a wild guess about the uncompressed size. + bOut.reserve(inflateStrm.avail_in * 3 + 512); + inflateStrm.avail_out = bOut.capacity(); + inflateStrm.next_out = reinterpret_cast(bOut.data()); + + int ret = inflate(&inflateStrm, Z_NO_FLUSH); + switch (ret) { + case Z_NEED_DICT: + case Z_DATA_ERROR: + case Z_MEM_ERROR: + return -1; + } + bOut.resize(bOut.capacity() - inflateStrm.avail_out); + out->append(bOut); + } while (inflateStrm.avail_in > 0); + } + + return out->byteAmount(); +} +#endif + qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size) { // FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable() @@ -841,36 +800,6 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *c return bytes; } -void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba) -{ - responseData.append(qba); - - // clear the original! helps with implicit sharing and - // avoiding memcpy when the user is reading the data - qba.clear(); -} - -void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data) -{ - responseData.append(data); - - // clear the original! helps with implicit sharing and - // avoiding memcpy when the user is reading the data - data.clear(); -} - -void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data) -{ - // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer - // instead of one QByteArray. - for(int i = 0; i < data.bufferCount(); i++) { - QByteArray &byteData = data[i]; - compressedData.append(byteData.constData(), byteData.size()); - } - data.clear(); -} - - bool QHttpNetworkReplyPrivate::shouldEmitSignals() { // for 401 & 407 don't emit the data signals. Content along with these diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h index 583a256ada..14219d484b 100644 --- a/src/network/access/qhttpnetworkreply_p.h +++ b/src/network/access/qhttpnetworkreply_p.h @@ -56,15 +56,7 @@ #ifndef QT_NO_HTTP #ifndef QT_NO_COMPRESS -# include -static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header -// gzip flag byte -#define HEAD_CRC 0x02 // bit 1 set: header CRC present -#define EXTRA_FIELD 0x04 // bit 2 set: extra field present -#define ORIG_NAME 0x08 // bit 3 set: original file name present -#define COMMENT 0x10 // bit 4 set: file comment present -#define RESERVED 0xE0 // bits 5..7: reserved -#define CHUNK 16384 +#include #endif #include @@ -192,10 +184,6 @@ public: qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out); qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize); - void appendUncompressedReplyData(QByteArray &qba); - void appendUncompressedReplyData(QByteDataBuffer &data); - void appendCompressedReplyData(QByteDataBuffer &data); - bool shouldEmitSignals(); bool expectContent(); void eraseData(); @@ -203,11 +191,8 @@ public: qint64 bytesAvailable() const; bool isChunked(); bool isConnectionCloseEnabled(); - bool isGzipped(); -#ifndef QT_NO_COMPRESS - bool gzipCheckHeader(QByteArray &content, int &pos); - int gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated); -#endif + + bool isCompressed(); void removeAutoDecompressHeader(); enum ReplyState { @@ -236,11 +221,12 @@ public: qint64 currentChunkRead; QPointer connection; QPointer connectionChannel; - bool initInflate; - bool streamEnd; + #ifndef QT_NO_COMPRESS z_stream inflateStrm; + qint64 uncompressBodyData(QByteDataBuffer *in, QByteDataBuffer *out); #endif + bool autoDecompress; QByteDataBuffer responseData; // uncompressed body From 1bedbb3706e9b0427c019d5dedbefd3984517f33 Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Tue, 12 Jul 2011 16:13:26 +0200 Subject: [PATCH 09/12] Fix compilation with icc 11 Rvalues reference are not supported by icc11 yet. Disable all C++0x feature on that compiler. Task-number: QTBUG-19979 Change-Id: Ifca939b18aa933b047883584cdf91f5543b852b5 Reviewed-by: Thiago Reviewed-on: http://codereview.qt.nokia.com/1535 Reviewed-by: Qt Sanity Bot Reviewed-by: Olivier Goffart --- src/corelib/global/qglobal.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index e8e736d4f3..e91e8afdbc 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -812,11 +812,10 @@ namespace QT_NAMESPACE {} # define Q_NO_TEMPLATE_FRIENDS # endif # if defined(__GXX_EXPERIMENTAL_CXX0X__) || defined(__GXX_EXPERIMENTAL_CPP0X__) -# if __INTEL_COMPILER >= 1100 +# if __INTEL_COMPILER >= 1200 # define Q_COMPILER_RVALUE_REFS # define Q_COMPILER_EXTERN_TEMPLATES # define Q_COMPILER_DECLTYPE -# elif __INTEL_COMPILER >= 1200 # define Q_COMPILER_VARIADIC_TEMPLATES # define Q_COMPILER_AUTO_TYPE # define Q_COMPILER_DEFAULT_DELETE_MEMBERS From a8fd5b9d2afea4e8eba271fb658668f15dfb09d7 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Tue, 12 Jul 2011 17:08:35 +0200 Subject: [PATCH 10/12] Fix uses of qRound on non-floating-point types. It's silly to round integeres, since they are already round. At most, this would waste CPU cycles without any benefit. Change-Id: I6fc0152f4416d723b65966e5d48cc7a07957b83c Merge-request: 17 Reviewed-by: Olivier Goffart Reviewed-on: http://codereview.qt.nokia.com/1541 Reviewed-by: Qt Sanity Bot --- src/gui/text/qfontengine_ft.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index dd30a15030..25c7268a9c 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -1644,7 +1644,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) overall.y = qMin(overall.y, y); xmax = qMax(xmax, x + g->width); ymax = qMax(ymax, y + g->height); - overall.xoff += qRound(g->advance); + overall.xoff += g->advance; } else { int left = FLOOR(face->glyph->metrics.horiBearingX); int right = CEIL(face->glyph->metrics.horiBearingX + face->glyph->metrics.width); @@ -1657,7 +1657,7 @@ glyph_metrics_t QFontEngineFT::boundingBox(const QGlyphLayout &glyphs) overall.y = qMin(overall.y, y); xmax = qMax(xmax, x + TRUNC(right - left)); ymax = qMax(ymax, y + TRUNC(top - bottom)); - overall.xoff += qRound(TRUNC(ROUND(face->glyph->advance.x))); + overall.xoff += int(TRUNC(ROUND(face->glyph->advance.x))); } } overall.height = qMax(overall.height, ymax - overall.y); From 92c4b63ee1751e6b1c9a90eaee76d74c3f0d3a3e Mon Sep 17 00:00:00 2001 From: Pierre Rossi Date: Tue, 12 Jul 2011 17:29:46 +0200 Subject: [PATCH 11/12] Fix a crash with QGraphicsScene. It happened when the scene gets deleted after ~QApplication has been called. test case: int main(int argc, char *argv[]) { QApplication a(argc, argv); QGraphicsScene *scene = new QGraphicsScene(&a); return 0; } Change-Id: I74d4023c9575242a2e334b2c786e00d0686c452b Reviewed-on: http://codereview.qt.nokia.com/1544 Reviewed-by: Qt Sanity Bot Reviewed-by: Gabriel de Dietrich --- src/gui/graphicsview/qgraphicsscene.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp index 655725b8e9..1551944a09 100644 --- a/src/gui/graphicsview/qgraphicsscene.cpp +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -1639,7 +1639,8 @@ QGraphicsScene::~QGraphicsScene() Q_D(QGraphicsScene); // Remove this scene from qApp's global scene list. - qApp->d_func()->scene_list.removeAll(this); + if (!QApplicationPrivate::is_app_closing) + qApp->d_func()->scene_list.removeAll(this); clear(); From 718153cfa03f336be2557da058c879de63cfa792 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Wed, 13 Jul 2011 10:21:23 +0200 Subject: [PATCH 12/12] Fix compilation on MSVC MSVC apparently doesn't like the way the QString::Data pointers got initialized. Also fixed a few warnings about signed/unsigned conversions. Change-Id: I1267979af7601129e5483f8785d4982a1f2f8182 Reviewed-on: http://codereview.qt.nokia.com/1558 Reviewed-by: Friedemann Kleint --- src/corelib/tools/qstring.cpp | 60 +++++++++++++++++++++++++++-------- src/corelib/tools/qstring.h | 8 ++--- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index dab281cdf8..b5efc85391 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -1051,7 +1051,11 @@ QString::QString(const QChar *unicode, int size) } else { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->ref = 1; + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = 0; memcpy(d->data(), unicode, size * sizeof(QChar)); d->data()[size] = '\0'; } @@ -1079,7 +1083,11 @@ QString::QString(const QChar *unicode) } else { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->ref = 1; + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = 0; memcpy(d->data(), unicode, size * sizeof(QChar)); d->data()[size] = '\0'; } @@ -1100,7 +1108,11 @@ QString::QString(int size, QChar ch) } else { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->ref = 1; + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = 0; d->data()[size] = '\0'; ushort *i = d->data() + size; ushort *b = d->data(); @@ -1120,7 +1132,11 @@ QString::QString(int size, Qt::Initialization) { d = (Data*) qMalloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->ref = 1; + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = 0; d->data()[size] = '\0'; } @@ -1138,7 +1154,11 @@ QString::QString(QChar ch) { d = (Data *) qMalloc(sizeof(Data) + 2*sizeof(QChar)); Q_CHECK_PTR(d); - *d = (Data) { Q_REFCOUNT_INITIALIZER(1), 1, 1, false, { 0 } }; + d->ref = 1; + d->size = 1; + d->alloc = 1; + d->capacityReserved = false; + d->offset = 0; d->data()[0] = ch.unicode(); d->data()[1] = '\0'; } @@ -1313,7 +1333,11 @@ void QString::realloc(int alloc) if (d->ref != 1 || d->offset) { Data *x = static_cast(qMalloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); - *x = (Data){ Q_REFCOUNT_INITIALIZER(1), qMin(alloc, d->size), alloc, d->capacityReserved, { 0 } }; + x->ref = 1; + x->size = qMin(alloc, d->size); + x->alloc = (uint) alloc; + x->capacityReserved = d->capacityReserved; + x->offset =0; ::memcpy(x->data(), d->data(), x->size * sizeof(QChar)); x->data()[x->size] = 0; if (!d->ref.deref()) @@ -3747,7 +3771,11 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) size = qstrlen(str); d = static_cast(qMalloc(sizeof(Data) + (size+1) * sizeof(QChar))); Q_CHECK_PTR(d); - *d = (Data){ Q_REFCOUNT_INITIALIZER(1), size, size, false, { 0 } }; + d->ref = 1; + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = 0; d->data()[size] = '\0'; ushort *dst = d->data(); /* SIMD: @@ -7071,13 +7099,19 @@ bool QString::isRightToLeft() const */ QString QString::fromRawData(const QChar *unicode, int size) { - Data *x = static_cast(qMalloc(sizeof(Data))); - *x = (Data){ Q_REFCOUNT_INITIALIZER(1), size, 0, false, { 0 } }; - Q_CHECK_PTR(x); - if (unicode) { - x->offset = (const ushort *)unicode - (x->d + sizeof(qptrdiff)/sizeof(ushort)); + Data *x; + if (!unicode) { + x = const_cast(&shared_null.str); + } else if (!size) { + x = const_cast(&shared_empty.str); } else { - size = 0; + x = static_cast(qMalloc(sizeof(Data) + sizeof(ushort))); + Q_CHECK_PTR(x); + x->ref = 1; + x->size = size; + x->alloc = 0; + x->capacityReserved = false; + x->offset = (const ushort *)unicode - (x->d + sizeof(qptrdiff)/sizeof(ushort)); } return QString(x, 0); } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 27948e04ea..45ff00211e 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -185,7 +185,7 @@ public: int capacity() const; inline void reserve(int size); - inline void squeeze() { if (d->size < d->alloc || d->ref != 1) realloc(); d->capacityReserved = false;} + inline void squeeze() { if (d->size < (int)d->alloc || d->ref != 1) realloc(); d->capacityReserved = false;} inline const QChar *unicode() const; inline QChar *data(); @@ -337,7 +337,7 @@ public: inline QString &prepend(const QLatin1String &s) { return insert(0, s); } inline QString &operator+=(QChar c) { - if (d->ref != 1 || d->size + 1 > d->alloc) + if (d->ref != 1 || d->size + 1 > (int)d->alloc) realloc(grow(d->size + 1)); d->data()[d->size++] = c.unicode(); d->data()[d->size] = '\0'; @@ -633,7 +633,7 @@ public: class Q_CORE_EXPORT QLatin1String { public: - inline explicit QLatin1String(const char *s) : m_size(s ? strlen(s) : 0), m_data(s) {} + inline explicit QLatin1String(const char *s) : m_size(s ? (int)strlen(s) : 0), m_data(s) {} inline const char *latin1() const { return m_data; } inline int size() const { return m_size; } @@ -842,7 +842,7 @@ inline void QCharRef::setCell(uchar acell) { QChar(*this).setCell(acell); } inline QString::QString() : d(const_cast(&shared_null.str)) {} inline QString::~QString() { if (!d->ref.deref()) free(d); } -inline void QString::reserve(int asize) { if (d->ref != 1 || asize > d->alloc) realloc(asize); d->capacityReserved = true;} +inline void QString::reserve(int asize) { if (d->ref != 1 || asize > (int)d->alloc) realloc(asize); d->capacityReserved = true;} inline QString &QString::setUtf16(const ushort *autf16, int asize) { return setUnicode(reinterpret_cast(autf16), asize); } inline QCharRef QString::operator[](int i)