diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2011-07-13 12:22:12 +0200 |
---|---|---|
committer | Lars Knoll <lars.knoll@nokia.com> | 2011-07-13 12:49:31 +0200 |
commit | a81093b9150b2f1727de6e9e77b8bdddb1c909ee (patch) | |
tree | 503ca465bbb633758c67e1f96987ed375f3c95ad | |
parent | 2770415f921d494e30e9a770c40b538d223351d6 (diff) | |
parent | 718153cfa03f336be2557da058c879de63cfa792 (diff) |
Merge remote-tracking branch 'origin/master' into refactor
Change-Id: I53c78056abde99198ffe1c67f7f9e90b4b08051d
31 files changed, 1039 insertions, 502 deletions
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 844fe0528e..4960d2a245 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -540,6 +540,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: */ @@ -547,7 +548,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 @@ -799,11 +803,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 @@ -1114,6 +1117,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 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 <string.h> #include <stdlib.h> -#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<QByteArray::Data *>(qRealloc(d.data(), sizeof(QByteArray::Data) + alloc)); + QByteArray::Data *p = static_cast<QByteArray::Data *>(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<QByteArray::Data *>(qRealloc(d.data(), sizeof(QByteArray::Data) + len)); + QByteArray::Data *p = static_cast<QByteArray::Data *>(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<Data *>(&shared_null.ba); } else if (!*str) { - x = &shared_empty; + x = const_cast<Data *>(&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<Data *>(&shared_null.ba); } else if (!*str) { - d = &shared_empty; + d = const_cast<Data *>(&shared_empty.ba); } else { int len = qstrlen(str); - d = static_cast<Data *>(qMalloc(sizeof(Data)+len)); + d = static_cast<Data *>(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<Data *>(&shared_null.ba); } else if (size <= 0) { - d = &shared_empty; + d = const_cast<Data *>(&shared_empty.ba); } else { - d = static_cast<Data *>(qMalloc(sizeof(Data) + size)); + d = static_cast<Data *>(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<Data *>(&shared_null.ba); } else { - d = static_cast<Data *>(qMalloc(sizeof(Data)+size)); + d = static_cast<Data *>(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<Data *>(qMalloc(sizeof(Data)+size)); + d = static_cast<Data *>(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<Data *>(&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<Data *>(qMalloc(sizeof(Data)+size)); + Data *x = static_cast<Data *>(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<Data *>(qMalloc(sizeof(Data) + alloc)); + if (d->ref != 1 || d->offset) { + Data *x = static_cast<Data *>(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<Data *>(qRealloc(d, sizeof(Data) + alloc)); + Data *x = static_cast<Data *>(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<Data *>(&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<Data *>(&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<Data *>(qMalloc(sizeof(Data))); - Q_CHECK_PTR(x); - if (data) { - x->data = const_cast<char *>(data); + Data *x; + if (!data) { + x = const_cast<Data *>(&shared_null.ba); + } else if (!size) { + x = const_cast<Data *>(&shared_empty.ba); } else { - x->data = x->array; - size = 0; + x = static_cast<Data *>(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<char *>(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 <QtCore/qatomic.h> +#include <QtCore/qrefcount.h> #include <QtCore/qnamespace.h> #include <string.h> @@ -119,18 +119,65 @@ class QString; class QDataStream; template <typename T> 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<int n> struct QConstByteArrayData +{ + const QByteArrayData ba; + const char data[n]; +}; + +template<int N> struct QConstByteArrayDataPtr +{ + const QConstByteArrayData<N> *ptr; +}; + + +#if defined(Q_COMPILER_LAMBDA) +# define QByteArrayLiteral(str) ([]() { \ + enum { Size = sizeof(str) }; \ + static const QConstByteArrayData<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZER(-1), Size -1, 0, 0, { 0 } }, str }; \ + QConstByteArrayDataPtr<Size> 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<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZER(-1), Size -1, 0, 0, { 0 } }, str }; \ + QConstByteArrayDataPtr<Size> 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 <int n> + inline QByteArray(const QConstByteArrayData<n> &dd) + : d(const_cast<QByteArrayData *>(&dd.str)) {} + template <int N> + Q_DECL_CONSTEXPR inline QByteArray(QConstByteArrayDataPtr<N> dd) + : d(const_cast<QByteArrayData *>(&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<Data *>(&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/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; 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<Data *>(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<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 *dst = d->data(); /* SIMD: @@ -7071,13 +7099,19 @@ bool QString::isRightToLeft() const */ QString QString::fromRawData(const QChar *unicode, int size) { - Data *x = static_cast<Data *>(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<Data *>(&shared_null.str); + } else if (!size) { + x = const_cast<Data *>(&shared_empty.str); } else { - size = 0; + x = static_cast<Data *>(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<Data *>(&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<const QChar *>(autf16), asize); } inline QCharRef QString::operator[](int i) 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); 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 b8ed8ee567..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 { @@ -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 @@ -470,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); - } - } -#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; + replyPrivate->totalProgress += haveRead; + if (replyPrivate->shouldEmitSignals()) { + emit reply->readyRead(); + emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength); } -#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 @@ -633,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 00653b62e8..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 @@ -211,7 +216,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) @@ -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; } @@ -672,7 +579,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; @@ -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<Bytef*>(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<Bytef*>(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 <zlib.h> -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 <zlib.h> #endif #include <QtNetwork/qtcpsocket.h> @@ -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<QHttpNetworkConnection> connection; QPointer<QHttpNetworkConnectionChannel> 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 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; diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 839a253b99..94273b0754 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -121,6 +121,7 @@ #include <QtCore/qfile.h> #include <QtCore/qfileinfo.h> #include <QtCore/qmap.h> +#include <QtCore/qmutex.h> #include <QtCore/qstring.h> #include <QtCore/qstringlist.h> #include <QtCore/qvarlengtharray.h> @@ -666,6 +667,21 @@ QList<QSslCertificate> 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 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<QSslError> QSslCertificate::verify(QList<QSslCertificate> 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 <typename T, typename U> class QMultiMap; @@ -122,6 +123,8 @@ public: static QList<QSslCertificate> fromData( const QByteArray &data, QSsl::EncodingFormat format = QSsl::Pem); + static QList<QSslError> verify(QList<QSslCertificate> 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<QSslError> QSslSocketBackendPrivate::verify(QList<QSslCertificate> certificateChain, const QString &hostName) +{ + QList<QSslError> 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<QSslCertificate> 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<QPair<int, int> > 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<int, int> &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<QSslCertificate> 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<QSslError> verify(QList<QSslCertificate> 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/src/widgets/graphicsview/qgraphicsscene.cpp b/src/widgets/graphicsview/qgraphicsscene.cpp index dc55ce0cd1..165096637b 100644 --- a/src/widgets/graphicsview/qgraphicsscene.cpp +++ b/src/widgets/graphicsview/qgraphicsscene.cpp @@ -1638,7 +1638,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(); 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() { 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) 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<QSslError> errors; + QList<QSslCertificate> 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<QSslCertificate> 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----- |