1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include "qnetworkcookie.h"
43 #include "qnetworkcookie_p.h"
45 #include "qnetworkrequest.h"
46 #include "qnetworkreply.h"
47 #include "QtCore/qbytearray.h"
48 #include "QtCore/qdebug.h"
49 #include "QtCore/qlist.h"
50 #include "QtCore/qlocale.h"
51 #include "QtCore/qstring.h"
52 #include "QtCore/qstringlist.h"
53 #include "QtCore/qurl.h"
54 #include "private/qobject_p.h"
64 \brief The QNetworkCookie class holds one network cookie.
66 Cookies are small bits of information that stateless protocols
67 like HTTP use to maintain some persistent information across
70 A cookie is set by a remote server when it replies to a request
71 and it expects the same cookie to be sent back when further
74 QNetworkCookie holds one such cookie as received from the
75 network. A cookie has a name and a value, but those are opaque to
76 the application (that is, the information stored in them has no
77 meaning to the application). A cookie has an associated path name
78 and domain, which indicate when the cookie should be sent again to
81 A cookie can also have an expiration date, indicating its
82 validity. If the expiration date is not present, the cookie is
83 considered a "session cookie" and should be discarded when the
84 application exits (or when its concept of session is over).
86 QNetworkCookie provides a way of parsing a cookie from the HTTP
87 header format using the QNetworkCookie::parseCookies()
88 function. However, when received in a QNetworkReply, the cookie is
91 This class implements cookies as described by the
92 \l{Netscape Cookie Specification}{initial cookie specification by
93 Netscape}, which is somewhat similar to the \l{http://www.rfc-editor.org/rfc/rfc2109.txt}{RFC 2109} specification,
94 plus the \l{Mitigating Cross-site Scripting With HTTP-only Cookies}
95 {"HttpOnly" extension}. The more recent \l{http://www.rfc-editor.org/rfc/rfc2965.txt}{RFC 2965} specification
96 (which uses the Set-Cookie2 header) is not supported.
98 \sa QNetworkCookieJar, QNetworkRequest, QNetworkReply
102 Create a new QNetworkCookie object, initializing the cookie name
103 to \a name and its value to \a value.
105 A cookie is only valid if it has a name. However, the value is
106 opaque to the application and being empty may have significance to
109 QNetworkCookie::QNetworkCookie(const QByteArray &name, const QByteArray &value)
110 : d(new QNetworkCookiePrivate)
112 qRegisterMetaType<QNetworkCookie>();
113 qRegisterMetaType<QList<QNetworkCookie> >();
120 Creates a new QNetworkCookie object by copying the contents of \a
123 QNetworkCookie::QNetworkCookie(const QNetworkCookie &other)
129 Destroys this QNetworkCookie object.
131 QNetworkCookie::~QNetworkCookie()
133 // QSharedDataPointer auto deletes
138 Copies the contents of the QNetworkCookie object \a other to this
141 QNetworkCookie &QNetworkCookie::operator=(const QNetworkCookie &other)
148 \fn void QNetworkCookie::swap(QNetworkCookie &other)
151 Swaps this cookie with \a other. This function is very fast and
156 \fn bool QNetworkCookie::operator!=(const QNetworkCookie &other) const
158 Returns true if this cookie is not equal to \a other.
165 Returns true if this cookie is equal to \a other. This function
166 only returns true if all fields of the cookie are the same.
168 However, in some contexts, two cookies of the same name could be
171 \sa operator!=(), hasSameIdentifier()
173 bool QNetworkCookie::operator==(const QNetworkCookie &other) const
177 return d->name == other.d->name &&
178 d->value == other.d->value &&
179 d->expirationDate.toUTC() == other.d->expirationDate.toUTC() &&
180 d->domain == other.d->domain &&
181 d->path == other.d->path &&
182 d->secure == other.d->secure &&
183 d->comment == other.d->comment;
187 Returns true if this cookie has the same identifier tuple as \a other.
188 The identifier tuple is composed of the name, domain and path.
192 bool QNetworkCookie::hasSameIdentifier(const QNetworkCookie &other) const
194 return d->name == other.d->name && d->domain == other.d->domain && d->path == other.d->path;
198 Returns true if the "secure" option was specified in the cookie
199 string, false otherwise.
201 Secure cookies may contain private information and should not be
202 resent over unencrypted connections.
206 bool QNetworkCookie::isSecure() const
212 Sets the secure flag of this cookie to \a enable.
214 Secure cookies may contain private information and should not be
215 resent over unencrypted connections.
219 void QNetworkCookie::setSecure(bool enable)
227 Returns true if the "HttpOnly" flag is enabled for this cookie.
229 A cookie that is "HttpOnly" is only set and retrieved by the
230 network requests and replies; i.e., the HTTP protocol. It is not
231 accessible from scripts running on browsers.
235 bool QNetworkCookie::isHttpOnly() const
243 Sets this cookie's "HttpOnly" flag to \a enable.
245 void QNetworkCookie::setHttpOnly(bool enable)
247 d->httpOnly = enable;
251 Returns true if this cookie is a session cookie. A session cookie
252 is a cookie which has no expiration date, which means it should be
253 discarded when the application's concept of session is over
254 (usually, when the application exits).
256 \sa expirationDate(), setExpirationDate()
258 bool QNetworkCookie::isSessionCookie() const
260 return !d->expirationDate.isValid();
264 Returns the expiration date for this cookie. If this cookie is a
265 session cookie, the QDateTime returned will not be valid. If the
266 date is in the past, this cookie has already expired and should
267 not be sent again back to a remote server.
269 The expiration date corresponds to the parameters of the "expires"
270 entry in the cookie string.
272 \sa isSessionCookie(), setExpirationDate()
274 QDateTime QNetworkCookie::expirationDate() const
276 return d->expirationDate;
280 Sets the expiration date of this cookie to \a date. Setting an
281 invalid expiration date to this cookie will mean it's a session
284 \sa isSessionCookie(), expirationDate()
286 void QNetworkCookie::setExpirationDate(const QDateTime &date)
288 d->expirationDate = date;
292 Returns the domain this cookie is associated with. This
293 corresponds to the "domain" field of the cookie string.
295 Note that the domain here may start with a dot, which is not a
296 valid hostname. However, it means this cookie matches all
297 hostnames ending with that domain name.
301 QString QNetworkCookie::domain() const
307 Sets the domain associated with this cookie to be \a domain.
311 void QNetworkCookie::setDomain(const QString &domain)
317 Returns the path associated with this cookie. This corresponds to
318 the "path" field of the cookie string.
322 QString QNetworkCookie::path() const
328 Sets the path associated with this cookie to be \a path.
332 void QNetworkCookie::setPath(const QString &path)
338 Returns the name of this cookie. The only mandatory field of a
339 cookie is its name, without which it is not considered valid.
341 \sa setName(), value()
343 QByteArray QNetworkCookie::name() const
349 Sets the name of this cookie to be \a cookieName. Note that
350 setting a cookie name to an empty QByteArray will make this cookie
355 void QNetworkCookie::setName(const QByteArray &cookieName)
357 d->name = cookieName;
361 Returns this cookies value, as specified in the cookie
362 string. Note that a cookie is still valid if its value is empty.
364 Cookie name-value pairs are considered opaque to the application:
365 that is, their values don't mean anything.
367 \sa setValue(), name()
369 QByteArray QNetworkCookie::value() const
375 Sets the value of this cookie to be \a value.
379 void QNetworkCookie::setValue(const QByteArray &value)
384 // ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
385 static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
390 // (3) token = quoted-string
391 const int length = text.length();
392 position = nextNonWhitespace(text, position);
394 int semiColonPosition = text.indexOf(';', position);
395 if (semiColonPosition < 0)
396 semiColonPosition = length; //no ';' means take everything to end of string
398 int equalsPosition = text.indexOf('=', position);
399 if (equalsPosition < 0 || equalsPosition > semiColonPosition) {
401 return qMakePair(QByteArray(), QByteArray()); //'=' is required for name-value-pair (RFC6265 section 5.2, rule 2)
402 equalsPosition = semiColonPosition; //no '=' means there is an attribute-name but no attribute-value
405 QByteArray first = text.mid(position, equalsPosition - position).trimmed();
407 int secondLength = semiColonPosition - equalsPosition - 1;
408 if (secondLength > 0)
409 second = text.mid(equalsPosition + 1, secondLength).trimmed();
411 position = semiColonPosition;
412 return qMakePair(first, second);
416 \enum QNetworkCookie::RawForm
418 This enum is used with the toRawForm() function to declare which
419 form of a cookie shall be returned.
421 \value NameAndValueOnly makes toRawForm() return only the
422 "NAME=VALUE" part of the cookie, as suitable for sending back
423 to a server in a client request's "Cookie:" header. Multiple
424 cookies are separated by a semi-colon in the "Cookie:" header
427 \value Full makes toRawForm() return the full
428 cookie contents, as suitable for sending to a client in a
429 server's "Set-Cookie:" header.
431 Note that only the Full form of the cookie can be parsed back into
432 its original contents.
434 \sa toRawForm(), parseCookies()
438 Returns the raw form of this QNetworkCookie. The QByteArray
439 returned by this function is suitable for an HTTP header, either
440 in a server response (the Set-Cookie header) or the client request
441 (the Cookie header). You can choose from one of two formats, using
446 QByteArray QNetworkCookie::toRawForm(RawForm form) const
449 if (d->name.isEmpty())
450 return result; // not a valid cookie
457 // same as above, but encoding everything back
459 result += "; secure";
461 result += "; HttpOnly";
462 if (!isSessionCookie()) {
463 result += "; expires=";
464 result += QLocale::c().toString(d->expirationDate.toUTC(),
465 QLatin1String("ddd, dd-MMM-yyyy hh:mm:ss 'GMT")).toLatin1();
467 if (!d->domain.isEmpty()) {
468 result += "; domain=";
469 QString domainNoDot = d->domain;
470 if (domainNoDot.startsWith(QLatin1Char('.'))) {
472 domainNoDot = domainNoDot.mid(1);
474 result += QUrl::toAce(domainNoDot);
476 if (!d->path.isEmpty()) {
478 result += d->path.toUtf8();
484 static const char zones[] =
502 static int zoneOffsets[] = {-8, -8, -7, -7, -6, -6, -5, -5, -4, -3, 0, 0, 0, 1, 2, 9 };
504 static const char months[] =
519 static inline bool isNumber(char s)
520 { return s >= '0' && s <= '9'; }
522 static inline bool isTerminator(char c)
523 { return c == '\n' || c == '\r'; }
525 static inline bool isValueSeparator(char c)
526 { return isTerminator(c) || c == ';'; }
528 static inline bool isWhitespace(char c)
529 { return c == ' ' || c == '\t'; }
531 static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
533 if (dateString[at] < 'a' || dateString[at] > 'z')
535 if (val == -1 && dateString.length() >= at + 3) {
539 const char *str = array + i;
540 if (str[0] == dateString[at]
541 && str[1] == dateString[at + 1]
542 && str[2] == dateString[at + 2]) {
546 i += int(strlen(str)) + 1;
553 //#define PARSEDATESTRINGDEBUG
560 Parse all the date formats that Firefox can.
562 The official format is:
563 expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT
565 But browsers have been supporting a very wide range of date
566 strings. To work on many sites we need to support more then
567 just the official date format.
569 For reference see Firefox's PR_ParseTimeStringToExplodedTime in
570 prtime.c. The Firefox date parser is coded in a very complex way
571 and is slightly over ~700 lines long. While this implementation
572 will be slightly slower for the non standard dates it is smaller,
573 more readable, and maintainable.
575 Or in their own words:
576 "} // else what the hell is this."
578 static QDateTime parseDateString(const QByteArray &dateString)
581 // placeholders for values when we are not sure it is a year, month or day
582 int unknown[3] = {-1, -1, -1};
588 // hour:minute:second.ms pm
589 QRegExp timeRx(QLatin1String("(\\d{1,2}):(\\d{1,2})(:(\\d{1,2})|)(\\.(\\d{1,3})|)((\\s{0,}(am|pm))|)"));
592 while (at < dateString.length()) {
593 #ifdef PARSEDATESTRINGDEBUG
594 qDebug() << dateString.mid(at);
596 bool isNum = isNumber(dateString[at]);
600 && checkStaticArray(month, dateString, at, months, sizeof(months)- 1)) {
602 #ifdef PARSEDATESTRINGDEBUG
603 qDebug() << "Month:" << month;
611 && checkStaticArray(zoneOffset, dateString, at, zones, sizeof(zones)- 1)) {
612 int sign = (at >= 0 && dateString[at - 1] == '-') ? -1 : 1;
613 zoneOffset = sign * zoneOffsets[zoneOffset] * 60 * 60;
614 #ifdef PARSEDATESTRINGDEBUG
615 qDebug() << "Zone:" << month;
622 && (zoneOffset == -1 || zoneOffset == 0) // Can only go after gmt
623 && (dateString[at] == '+' || dateString[at] == '-')
625 || isWhitespace(dateString[at - 1])
626 || dateString[at - 1] == ','
628 && (dateString[at - 3] == 'g')
629 && (dateString[at - 2] == 'm')
630 && (dateString[at - 1] == 't')))) {
633 while (end < 5 && dateString.length() > at+end
634 && dateString[at + end] >= '0' && dateString[at + end] <= '9')
640 minutes = atoi(dateString.mid(at + 3, 2).constData());
643 hours = atoi(dateString.mid(at + 1, 2).constData());
646 hours = atoi(dateString.mid(at + 1, 1).constData());
653 int sign = dateString[at] == '-' ? -1 : 1;
654 zoneOffset = sign * ((minutes * 60) + (hours * 60 * 60));
655 #ifdef PARSEDATESTRINGDEBUG
656 qDebug() << "Zone offset:" << zoneOffset << hours << minutes;
664 if (isNum && time.isNull()
665 && dateString.length() >= at + 3
666 && (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
667 // While the date can be found all over the string the format
668 // for the time is set and a nice regexp can be used.
669 int pos = timeRx.indexIn(QLatin1String(dateString), at);
671 QStringList list = timeRx.capturedTexts();
672 int h = atoi(list.at(1).toLatin1().constData());
673 int m = atoi(list.at(2).toLatin1().constData());
674 int s = atoi(list.at(4).toLatin1().constData());
675 int ms = atoi(list.at(6).toLatin1().constData());
676 if (h < 12 && !list.at(9).isEmpty())
677 if (list.at(9) == QLatin1String("pm"))
679 time = QTime(h, m, s, ms);
680 #ifdef PARSEDATESTRINGDEBUG
681 qDebug() << "Time:" << list << timeRx.matchedLength();
683 at += timeRx.matchedLength();
691 && dateString.length() > at + 3) {
692 if (isNumber(dateString[at + 1])
693 && isNumber(dateString[at + 2])
694 && isNumber(dateString[at + 3])) {
695 year = atoi(dateString.mid(at, 4).constData());
697 #ifdef PARSEDATESTRINGDEBUG
698 qDebug() << "Year:" << year;
704 // a one or two digit number
705 // Could be month, day or year
708 if (dateString.length() > at + 1
709 && isNumber(dateString[at + 1]))
711 int x = atoi(dateString.mid(at, length).constData());
712 if (year == -1 && (x > 31 || x == 0)) {
715 if (unknown[0] == -1) unknown[0] = x;
716 else if (unknown[1] == -1) unknown[1] = x;
717 else if (unknown[2] == -1) unknown[2] = x;
720 #ifdef PARSEDATESTRINGDEBUG
721 qDebug() << "Saving" << x;
726 // Unknown character, typically a weekday such as 'Mon'
730 // Once we are done parsing the string take the digits in unknown
731 // and determine which is the unknown year/month/day
733 int couldBe[3] = { 0, 0, 0 };
734 int unknownCount = 3;
735 for (int i = 0; i < unknownCount; ++i) {
736 if (unknown[i] == -1) {
737 couldBe[i] = ADAY | AYEAR | AMONTH;
745 if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
746 couldBe[i] |= AMONTH;
752 // For any possible day make sure one of the values that could be a month
753 // can contain that day.
754 // For any possible month make sure one of the values that can be a
755 // day that month can have.
757 // 31 can't be a day because 11 and 6 don't have 31 days
758 for (int i = 0; i < unknownCount; ++i) {
759 int currentValue = unknown[i];
760 bool findMatchingMonth = couldBe[i] & ADAY && currentValue >= 29;
761 bool findMatchingDay = couldBe[i] & AMONTH;
762 if (!findMatchingMonth || !findMatchingDay)
764 for (int j = 0; j < 3; ++j) {
767 for (int k = 0; k < 2; ++k) {
768 if (k == 0 && !(findMatchingMonth && (couldBe[j] & AMONTH)))
770 else if (k == 1 && !(findMatchingDay && (couldBe[j] & ADAY)))
772 int m = currentValue;
776 if (m == -1) m = month;
780 // When we get 29 and the year ends up having only 28
781 // See date.isValid below
782 // Example: 29 23 Feb
786 case 4: case 6: case 9: case 11:
791 if (d > 0 && d <= 31)
794 if (k == 0) findMatchingMonth = found;
795 else if (k == 1) findMatchingDay = found;
798 if (findMatchingMonth)
801 couldBe[i] &= ~AMONTH;
804 // First set the year/month/day that have been deduced
805 // and reduce the set as we go along to deduce more
806 for (int i = 0; i < unknownCount; ++i) {
808 for (int j = 0; j < 3; ++j) {
809 if (couldBe[j] == ADAY && day == -1) {
812 } else if (couldBe[j] == AMONTH && month == -1) {
815 } else if (couldBe[j] == AYEAR && year == -1) {
822 couldBe[j] &= ~unset;
826 // Now fallback to a standardized order to fill in the rest with
827 for (int i = 0; i < unknownCount; ++i) {
828 if (couldBe[i] & AMONTH && month == -1) month = unknown[i];
829 else if (couldBe[i] & ADAY && day == -1) day = unknown[i];
830 else if (couldBe[i] & AYEAR && year == -1) year = unknown[i];
832 #ifdef PARSEDATESTRINGDEBUG
833 qDebug() << "Final set" << year << month << day;
836 if (year == -1 || month == -1 || day == -1) {
837 #ifdef PARSEDATESTRINGDEBUG
838 qDebug() << "Parser failure" << year << month << day;
850 QDate date(year + y2k, month, day);
852 // When we were given a bad cookie that when parsed
853 // set the day to 29 and the year to one that doesn't
854 // have the 29th of Feb rather then adding the extra
855 // complicated checking earlier just swap here.
856 // Example: 29 23 Feb
858 date = QDate(day + y2k, month, year);
860 QDateTime dateTime(date, time, Qt::UTC);
862 if (zoneOffset != -1) {
863 dateTime = dateTime.addSecs(zoneOffset);
865 if (!dateTime.isValid())
871 Parses the cookie string \a cookieString as received from a server
872 response in the "Set-Cookie:" header. If there's a parsing error,
873 this function returns an empty list.
875 Since the HTTP header can set more than one cookie at the same
876 time, this function returns a QList<QNetworkCookie>, one for each
877 cookie that is parsed.
881 QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
883 // cookieString can be a number of set-cookie header strings joined together
884 // by \n, parse each line separately.
885 QList<QNetworkCookie> cookies;
886 QList<QByteArray> list = cookieString.split('\n');
887 for (int a = 0; a < list.size(); a++)
888 cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
892 QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
894 // According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
895 // the Set-Cookie response header is of the format:
897 // Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
899 // where only the NAME=VALUE part is mandatory
901 // We do not support RFC 2965 Set-Cookie2-style cookies
903 QList<QNetworkCookie> result;
904 QDateTime now = QDateTime::currentDateTime().toUTC();
907 const int length = cookieString.length();
908 while (position < length) {
909 QNetworkCookie cookie;
911 // The first part is always the "NAME=VALUE" part
912 QPair<QByteArray,QByteArray> field = nextField(cookieString, position, true);
913 if (field.first.isEmpty())
916 cookie.setName(field.first);
917 cookie.setValue(field.second);
919 position = nextNonWhitespace(cookieString, position);
920 while (position < length) {
921 switch (cookieString.at(position++)) {
923 // new field in the cookie
924 field = nextField(cookieString, position, false);
925 field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
927 if (field.first == "expires") {
928 position -= field.second.length();
930 for (end = position; end < length; ++end)
931 if (isValueSeparator(cookieString.at(end)))
934 QByteArray dateString = cookieString.mid(position, end - position).trimmed();
936 QDateTime dt = parseDateString(dateString.toLower());
938 cookie.setExpirationDate(dt);
939 //if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.1)
940 } else if (field.first == "domain") {
941 QByteArray rawDomain = field.second;
942 //empty domain should be ignored (RFC6265 section 5.2.3)
943 if (!rawDomain.isEmpty()) {
944 QString maybeLeadingDot;
945 if (rawDomain.startsWith('.')) {
946 maybeLeadingDot = QLatin1Char('.');
947 rawDomain = rawDomain.mid(1);
950 //IDN domains are required by RFC6265, accepting utf8 as well doesn't break any test cases.
951 QString normalizedDomain = QUrl::fromAce(QUrl::toAce(QString::fromUtf8(rawDomain)));
952 if (!normalizedDomain.isEmpty()) {
953 cookie.setDomain(maybeLeadingDot + normalizedDomain);
955 //Normalization fails for malformed domains, e.g. "..example.org", reject the cookie now
956 //rather than accepting it but never sending it due to domain match failure, as the
957 //strict reading of RFC6265 would indicate.
961 } else if (field.first == "max-age") {
963 int secs = field.second.toInt(&ok);
966 //earliest representable time (RFC6265 section 5.2.2)
967 cookie.setExpirationDate(QDateTime::fromTime_t(0));
969 cookie.setExpirationDate(now.addSecs(secs));
972 //if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.2)
973 } else if (field.first == "path") {
974 if (field.second.startsWith('/')) {
975 // ### we should treat cookie paths as an octet sequence internally
976 // However RFC6265 says we should assume UTF-8 for presentation as a string
977 cookie.setPath(QString::fromUtf8(field.second));
979 // if the path doesn't start with '/' then set the default path (RFC6265 section 5.2.4)
980 // and also IETF test case path0030 which has valid and empty path in the same cookie
981 cookie.setPath(QString());
983 } else if (field.first == "secure") {
984 cookie.setSecure(true);
985 } else if (field.first == "httponly") {
986 cookie.setHttpOnly(true);
988 // ignore unknown fields in the cookie (RFC6265 section 5.2, rule 6)
991 position = nextNonWhitespace(cookieString, position);
995 if (!cookie.name().isEmpty())
1004 This functions normalizes the path and domain of the cookie if they were previously empty.
1005 The \a url parameter is used to determine the correct domain and path.
1007 void QNetworkCookie::normalize(const QUrl &url)
1009 // don't do path checking. See http://bugreports.qt-project.org/browse/QTBUG-5815
1010 if (d->path.isEmpty()) {
1011 QString pathAndFileName = url.path();
1012 QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
1013 if (defaultPath.isEmpty())
1014 defaultPath = QLatin1Char('/');
1015 d->path = defaultPath;
1018 if (d->domain.isEmpty())
1019 d->domain = url.host();
1020 else if (!d->domain.startsWith(QLatin1Char('.')))
1021 // Ensure the domain starts with a dot if its field was not empty
1022 // in the HTTP header. There are some servers that forget the
1023 // leading dot and this is actually forbidden according to RFC 2109,
1024 // but all browsers accept it anyway so we do that as well.
1025 d->domain.prepend(QLatin1Char('.'));
1028 #ifndef QT_NO_DEBUG_STREAM
1029 QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
1031 s.nospace() << "QNetworkCookie(" << cookie.toRawForm(QNetworkCookie::Full) << ')';