1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
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"
63 \brief The QNetworkCookie class holds one network cookie.
65 Cookies are small bits of information that stateless protocols
66 like HTTP use to maintain some persistent information across
69 A cookie is set by a remote server when it replies to a request
70 and it expects the same cookie to be sent back when further
73 QNetworkCookie holds one such cookie as received from the
74 network. A cookie has a name and a value, but those are opaque to
75 the application (that is, the information stored in them has no
76 meaning to the application). A cookie has an associated path name
77 and domain, which indicate when the cookie should be sent again to
80 A cookie can also have an expiration date, indicating its
81 validity. If the expiration date is not present, the cookie is
82 considered a "session cookie" and should be discarded when the
83 application exits (or when its concept of session is over).
85 QNetworkCookie provides a way of parsing a cookie from the HTTP
86 header format using the QNetworkCookie::parseCookies()
87 function. However, when received in a QNetworkReply, the cookie is
90 This class implements cookies as described by the
91 \l{Netscape Cookie Specification}{initial cookie specification by
92 Netscape}, which is somewhat similar to the \l{RFC 2109} specification,
93 plus the \l{Mitigating Cross-site Scripting With HTTP-only Cookies}
94 {"HttpOnly" extension}. The more recent \l{RFC 2965} specification
95 (which uses the Set-Cookie2 header) is not supported.
97 \sa QNetworkCookieJar, QNetworkRequest, QNetworkReply
101 Create a new QNetworkCookie object, initializing the cookie name
102 to \a name and its value to \a value.
104 A cookie is only valid if it has a name. However, the value is
105 opaque to the application and being empty may have significance to
108 QNetworkCookie::QNetworkCookie(const QByteArray &name, const QByteArray &value)
109 : d(new QNetworkCookiePrivate)
111 qRegisterMetaType<QNetworkCookie>();
112 qRegisterMetaType<QList<QNetworkCookie> >();
119 Creates a new QNetworkCookie object by copying the contents of \a
122 QNetworkCookie::QNetworkCookie(const QNetworkCookie &other)
128 Destroys this QNetworkCookie object.
130 QNetworkCookie::~QNetworkCookie()
132 // QSharedDataPointer auto deletes
137 Copies the contents of the QNetworkCookie object \a other to this
140 QNetworkCookie &QNetworkCookie::operator=(const QNetworkCookie &other)
147 \fn bool QNetworkCookie::operator!=(const QNetworkCookie &other) const
149 Returns true if this cookie is not equal to \a other.
156 Returns true if this cookie is equal to \a other. This function
157 only returns true if all fields of the cookie are the same.
159 However, in some contexts, two cookies of the same name could be
162 \sa operator!=(), hasSameIdentifier()
164 bool QNetworkCookie::operator==(const QNetworkCookie &other) const
168 return d->name == other.d->name &&
169 d->value == other.d->value &&
170 d->expirationDate.toUTC() == other.d->expirationDate.toUTC() &&
171 d->domain == other.d->domain &&
172 d->path == other.d->path &&
173 d->secure == other.d->secure &&
174 d->comment == other.d->comment;
178 Returns true if this cookie has the same identifier tuple as \a other.
179 The identifier tuple is composed of the name, domain and path.
183 bool QNetworkCookie::hasSameIdentifier(const QNetworkCookie &other) const
185 return d->name == other.d->name && d->domain == other.d->domain && d->path == other.d->path;
189 Returns true if the "secure" option was specified in the cookie
190 string, false otherwise.
192 Secure cookies may contain private information and should not be
193 resent over unencrypted connections.
197 bool QNetworkCookie::isSecure() const
203 Sets the secure flag of this cookie to \a enable.
205 Secure cookies may contain private information and should not be
206 resent over unencrypted connections.
210 void QNetworkCookie::setSecure(bool enable)
218 Returns true if the "HttpOnly" flag is enabled for this cookie.
220 A cookie that is "HttpOnly" is only set and retrieved by the
221 network requests and replies; i.e., the HTTP protocol. It is not
222 accessible from scripts running on browsers.
226 bool QNetworkCookie::isHttpOnly() const
234 Sets this cookie's "HttpOnly" flag to \a enable.
236 void QNetworkCookie::setHttpOnly(bool enable)
238 d->httpOnly = enable;
242 Returns true if this cookie is a session cookie. A session cookie
243 is a cookie which has no expiration date, which means it should be
244 discarded when the application's concept of session is over
245 (usually, when the application exits).
247 \sa expirationDate(), setExpirationDate()
249 bool QNetworkCookie::isSessionCookie() const
251 return !d->expirationDate.isValid();
255 Returns the expiration date for this cookie. If this cookie is a
256 session cookie, the QDateTime returned will not be valid. If the
257 date is in the past, this cookie has already expired and should
258 not be sent again back to a remote server.
260 The expiration date corresponds to the parameters of the "expires"
261 entry in the cookie string.
263 \sa isSessionCookie(), setExpirationDate()
265 QDateTime QNetworkCookie::expirationDate() const
267 return d->expirationDate;
271 Sets the expiration date of this cookie to \a date. Setting an
272 invalid expiration date to this cookie will mean it's a session
275 \sa isSessionCookie(), expirationDate()
277 void QNetworkCookie::setExpirationDate(const QDateTime &date)
279 d->expirationDate = date;
283 Returns the domain this cookie is associated with. This
284 corresponds to the "domain" field of the cookie string.
286 Note that the domain here may start with a dot, which is not a
287 valid hostname. However, it means this cookie matches all
288 hostnames ending with that domain name.
292 QString QNetworkCookie::domain() const
298 Sets the domain associated with this cookie to be \a domain.
302 void QNetworkCookie::setDomain(const QString &domain)
308 Returns the path associated with this cookie. This corresponds to
309 the "path" field of the cookie string.
313 QString QNetworkCookie::path() const
319 Sets the path associated with this cookie to be \a path.
323 void QNetworkCookie::setPath(const QString &path)
329 Returns the name of this cookie. The only mandatory field of a
330 cookie is its name, without which it is not considered valid.
332 \sa setName(), value()
334 QByteArray QNetworkCookie::name() const
340 Sets the name of this cookie to be \a cookieName. Note that
341 setting a cookie name to an empty QByteArray will make this cookie
346 void QNetworkCookie::setName(const QByteArray &cookieName)
348 d->name = cookieName;
352 Returns this cookies value, as specified in the cookie
353 string. Note that a cookie is still valid if its value is empty.
355 Cookie name-value pairs are considered opaque to the application:
356 that is, their values don't mean anything.
358 \sa setValue(), name()
360 QByteArray QNetworkCookie::value() const
366 Sets the value of this cookie to be \a value.
370 void QNetworkCookie::setValue(const QByteArray &value)
375 // ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
376 static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
381 // (3) token = quoted-string
383 const int length = text.length();
384 position = nextNonWhitespace(text, position);
386 // parse the first part, before the equal sign
387 for (i = position; i < length; ++i) {
388 register char c = text.at(i);
389 if (c == ';' || c == ',' || c == '=')
393 QByteArray first = text.mid(position, i - position).trimmed();
397 return qMakePair(QByteArray(), QByteArray());
398 if (i == length || text.at(i) != '=')
399 // no equal sign, we found format (1)
400 return qMakePair(first, QByteArray());
403 second.reserve(32); // arbitrary but works for most cases
405 i = nextNonWhitespace(text, position + 1);
406 if (i < length && text.at(i) == '"') {
407 // a quote, we found format (3), where:
408 // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
409 // qdtext = <any TEXT except <">>
410 // quoted-pair = "\" CHAR
412 // If it is NAME=VALUE, retain the value as is
413 // refer to http://bugreports.qt-project.org/browse/QTBUG-17746
418 register char c = text.at(i);
420 // end of quoted text
424 } else if (c == '\\') {
430 return qMakePair(QByteArray(), QByteArray());
438 for ( ; i < length; ++i) {
439 register char c = text.at(i);
440 if (c == ',' || c == ';')
445 // no quote, we found format (2)
447 for ( ; i < length; ++i) {
448 register char c = text.at(i);
449 // for name value pairs, we want to parse until reaching the next ';'
450 // and not break when reaching a space char
451 if (c == ',' || c == ';' || ((isNameValue && (c == '\n' || c == '\r')) || (!isNameValue && isLWS(c))))
455 second = text.mid(position, i - position).trimmed();
460 second.resize(0); // turns into empty-but-not-null
461 return qMakePair(first, second);
465 \enum QNetworkCookie::RawForm
467 This enum is used with the toRawForm() function to declare which
468 form of a cookie shall be returned.
470 \value NameAndValueOnly makes toRawForm() return only the
471 "NAME=VALUE" part of the cookie, as suitable for sending back
472 to a server in a client request's "Cookie:" header. Multiple
473 cookies are separated by a semi-colon in the "Cookie:" header
476 \value Full makes toRawForm() return the full
477 cookie contents, as suitable for sending to a client in a
478 server's "Set-Cookie:" header. Multiple cookies are separated
479 by commas in a "Set-Cookie:" header.
481 Note that only the Full form of the cookie can be parsed back into
482 its original contents.
484 \sa toRawForm(), parseCookies()
488 Returns the raw form of this QNetworkCookie. The QByteArray
489 returned by this function is suitable for an HTTP header, either
490 in a server response (the Set-Cookie header) or the client request
491 (the Cookie header). You can choose from one of two formats, using
496 QByteArray QNetworkCookie::toRawForm(RawForm form) const
499 if (d->name.isEmpty())
500 return result; // not a valid cookie
504 if ((d->value.contains(';') ||
505 d->value.contains(',') ||
506 d->value.contains('"')) &&
507 (!d->value.startsWith('"') &&
508 !d->value.endsWith('"'))) {
511 QByteArray value = d->value;
512 value.replace('"', "\\\"");
521 // same as above, but encoding everything back
523 result += "; secure";
525 result += "; HttpOnly";
526 if (!isSessionCookie()) {
527 result += "; expires=";
528 result += QLocale::c().toString(d->expirationDate.toUTC(),
529 QLatin1String("ddd, dd-MMM-yyyy hh:mm:ss 'GMT")).toLatin1();
531 if (!d->domain.isEmpty()) {
532 result += "; domain=";
533 QString domainNoDot = d->domain;
534 if (domainNoDot.startsWith(QLatin1Char('.'))) {
536 domainNoDot = domainNoDot.mid(1);
538 result += QUrl::toAce(domainNoDot);
540 if (!d->path.isEmpty()) {
542 result += QUrl::toPercentEncoding(d->path, "/");
548 static const char zones[] =
566 static int zoneOffsets[] = {-8, -8, -7, -7, -6, -6, -5, -5, -4, -3, 0, 0, 0, 1, 2, 9 };
568 static const char months[] =
583 static inline bool isNumber(char s)
584 { return s >= '0' && s <= '9'; }
586 static inline bool isTerminator(char c)
587 { return c == '\n' || c == '\r'; }
589 static inline bool isValueSeparator(char c)
590 { return isTerminator(c) || c == ';'; }
592 static inline bool isWhitespace(char c)
593 { return c == ' ' || c == '\t'; }
595 static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
597 if (dateString[at] < 'a' || dateString[at] > 'z')
599 if (val == -1 && dateString.length() >= at + 3) {
603 const char *str = array + i;
604 if (str[0] == dateString[at]
605 && str[1] == dateString[at + 1]
606 && str[2] == dateString[at + 2]) {
610 i += int(strlen(str)) + 1;
617 //#define PARSEDATESTRINGDEBUG
624 Parse all the date formats that Firefox can.
626 The official format is:
627 expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT
629 But browsers have been supporting a very wide range of date
630 strings. To work on many sites we need to support more then
631 just the official date format.
633 For reference see Firefox's PR_ParseTimeStringToExplodedTime in
634 prtime.c. The Firefox date parser is coded in a very complex way
635 and is slightly over ~700 lines long. While this implementation
636 will be slightly slower for the non standard dates it is smaller,
637 more readable, and maintainable.
639 Or in their own words:
640 "} // else what the hell is this."
642 static QDateTime parseDateString(const QByteArray &dateString)
645 // placeholders for values when we are not sure it is a year, month or day
646 int unknown[3] = {-1, -1, -1};
652 // hour:minute:second.ms pm
653 QRegExp timeRx(QLatin1String("(\\d{1,2}):(\\d{1,2})(:(\\d{1,2})|)(\\.(\\d{1,3})|)((\\s{0,}(am|pm))|)"));
656 while (at < dateString.length()) {
657 #ifdef PARSEDATESTRINGDEBUG
658 qDebug() << dateString.mid(at);
660 bool isNum = isNumber(dateString[at]);
664 && checkStaticArray(month, dateString, at, months, sizeof(months)- 1)) {
666 #ifdef PARSEDATESTRINGDEBUG
667 qDebug() << "Month:" << month;
675 && checkStaticArray(zoneOffset, dateString, at, zones, sizeof(zones)- 1)) {
676 int sign = (at >= 0 && dateString[at - 1] == '-') ? -1 : 1;
677 zoneOffset = sign * zoneOffsets[zoneOffset] * 60 * 60;
678 #ifdef PARSEDATESTRINGDEBUG
679 qDebug() << "Zone:" << month;
686 && (zoneOffset == -1 || zoneOffset == 0) // Can only go after gmt
687 && (dateString[at] == '+' || dateString[at] == '-')
689 || isWhitespace(dateString[at - 1])
690 || dateString[at - 1] == ','
692 && (dateString[at - 3] == 'g')
693 && (dateString[at - 2] == 'm')
694 && (dateString[at - 1] == 't')))) {
697 while (end < 5 && dateString.length() > at+end
698 && dateString[at + end] >= '0' && dateString[at + end] <= '9')
704 minutes = atoi(dateString.mid(at + 3, 2).constData());
707 hours = atoi(dateString.mid(at + 1, 2).constData());
710 hours = atoi(dateString.mid(at + 1, 1).constData());
717 int sign = dateString[at] == '-' ? -1 : 1;
718 zoneOffset = sign * ((minutes * 60) + (hours * 60 * 60));
719 #ifdef PARSEDATESTRINGDEBUG
720 qDebug() << "Zone offset:" << zoneOffset << hours << minutes;
728 if (isNum && time.isNull()
729 && dateString.length() >= at + 3
730 && (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
731 // While the date can be found all over the string the format
732 // for the time is set and a nice regexp can be used.
733 int pos = timeRx.indexIn(QLatin1String(dateString), at);
735 QStringList list = timeRx.capturedTexts();
736 int h = atoi(list.at(1).toLatin1().constData());
737 int m = atoi(list.at(2).toLatin1().constData());
738 int s = atoi(list.at(4).toLatin1().constData());
739 int ms = atoi(list.at(6).toLatin1().constData());
740 if (h < 12 && !list.at(9).isEmpty())
741 if (list.at(9) == QLatin1String("pm"))
743 time = QTime(h, m, s, ms);
744 #ifdef PARSEDATESTRINGDEBUG
745 qDebug() << "Time:" << list << timeRx.matchedLength();
747 at += timeRx.matchedLength();
755 && dateString.length() > at + 3) {
756 if (isNumber(dateString[at + 1])
757 && isNumber(dateString[at + 2])
758 && isNumber(dateString[at + 3])) {
759 year = atoi(dateString.mid(at, 4).constData());
761 #ifdef PARSEDATESTRINGDEBUG
762 qDebug() << "Year:" << year;
768 // a one or two digit number
769 // Could be month, day or year
772 if (dateString.length() > at + 1
773 && isNumber(dateString[at + 1]))
775 int x = atoi(dateString.mid(at, length).constData());
776 if (year == -1 && (x > 31 || x == 0)) {
779 if (unknown[0] == -1) unknown[0] = x;
780 else if (unknown[1] == -1) unknown[1] = x;
781 else if (unknown[2] == -1) unknown[2] = x;
784 #ifdef PARSEDATESTRINGDEBUG
785 qDebug() << "Saving" << x;
790 // Unknown character, typically a weekday such as 'Mon'
794 // Once we are done parsing the string take the digits in unknown
795 // and determine which is the unknown year/month/day
797 int couldBe[3] = { 0, 0, 0 };
798 int unknownCount = 3;
799 for (int i = 0; i < unknownCount; ++i) {
800 if (unknown[i] == -1) {
801 couldBe[i] = ADAY | AYEAR | AMONTH;
809 if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
810 couldBe[i] |= AMONTH;
816 // For any possible day make sure one of the values that could be a month
817 // can contain that day.
818 // For any possible month make sure one of the values that can be a
819 // day that month can have.
821 // 31 can't be a day because 11 and 6 don't have 31 days
822 for (int i = 0; i < unknownCount; ++i) {
823 int currentValue = unknown[i];
824 bool findMatchingMonth = couldBe[i] & ADAY && currentValue >= 29;
825 bool findMatchingDay = couldBe[i] & AMONTH;
826 if (!findMatchingMonth || !findMatchingDay)
828 for (int j = 0; j < 3; ++j) {
831 for (int k = 0; k < 2; ++k) {
832 if (k == 0 && !(findMatchingMonth && (couldBe[j] & AMONTH)))
834 else if (k == 1 && !(findMatchingDay && (couldBe[j] & ADAY)))
836 int m = currentValue;
840 if (m == -1) m = month;
844 // When we get 29 and the year ends up having only 28
845 // See date.isValid below
846 // Example: 29 23 Feb
850 case 4: case 6: case 9: case 11:
855 if (d > 0 && d <= 31)
858 if (k == 0) findMatchingMonth = found;
859 else if (k == 1) findMatchingDay = found;
862 if (findMatchingMonth)
865 couldBe[i] &= ~AMONTH;
868 // First set the year/month/day that have been deduced
869 // and reduce the set as we go along to deduce more
870 for (int i = 0; i < unknownCount; ++i) {
872 for (int j = 0; j < 3; ++j) {
873 if (couldBe[j] == ADAY && day == -1) {
876 } else if (couldBe[j] == AMONTH && month == -1) {
879 } else if (couldBe[j] == AYEAR && year == -1) {
886 couldBe[j] &= ~unset;
890 // Now fallback to a standardized order to fill in the rest with
891 for (int i = 0; i < unknownCount; ++i) {
892 if (couldBe[i] & AMONTH && month == -1) month = unknown[i];
893 else if (couldBe[i] & ADAY && day == -1) day = unknown[i];
894 else if (couldBe[i] & AYEAR && year == -1) year = unknown[i];
896 #ifdef PARSEDATESTRINGDEBUG
897 qDebug() << "Final set" << year << month << day;
900 if (year == -1 || month == -1 || day == -1) {
901 #ifdef PARSEDATESTRINGDEBUG
902 qDebug() << "Parser failure" << year << month << day;
914 QDate date(year + y2k, month, day);
916 // When we were given a bad cookie that when parsed
917 // set the day to 29 and the year to one that doesn't
918 // have the 29th of Feb rather then adding the extra
919 // complicated checking earlier just swap here.
920 // Example: 29 23 Feb
922 date = QDate(day + y2k, month, year);
924 QDateTime dateTime(date, time, Qt::UTC);
926 if (zoneOffset != -1) {
927 dateTime = dateTime.addSecs(zoneOffset);
929 if (!dateTime.isValid())
935 Parses the cookie string \a cookieString as received from a server
936 response in the "Set-Cookie:" header. If there's a parsing error,
937 this function returns an empty list.
939 Since the HTTP header can set more than one cookie at the same
940 time, this function returns a QList<QNetworkCookie>, one for each
941 cookie that is parsed.
945 QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
947 // cookieString can be a number of set-cookie header strings joined together
948 // by \n, parse each line separately.
949 QList<QNetworkCookie> cookies;
950 QList<QByteArray> list = cookieString.split('\n');
951 for (int a = 0; a < list.size(); a++)
952 cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
956 QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
958 // According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
959 // the Set-Cookie response header is of the format:
961 // Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
963 // where only the NAME=VALUE part is mandatory
965 // We do not support RFC 2965 Set-Cookie2-style cookies
967 QList<QNetworkCookie> result;
968 QDateTime now = QDateTime::currentDateTime().toUTC();
971 const int length = cookieString.length();
972 while (position < length) {
973 QNetworkCookie cookie;
975 // The first part is always the "NAME=VALUE" part
976 QPair<QByteArray,QByteArray> field = nextField(cookieString, position, true);
977 if (field.first.isEmpty() || field.second.isNull())
980 cookie.setName(field.first);
981 cookie.setValue(field.second);
983 position = nextNonWhitespace(cookieString, position);
984 bool endOfCookie = false;
985 while (!endOfCookie && position < length) {
986 switch (cookieString.at(position++)) {
993 // new field in the cookie
994 field = nextField(cookieString, position, false);
995 field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
997 if (field.first == "expires") {
998 position -= field.second.length();
1000 for (end = position; end < length; ++end)
1001 if (isValueSeparator(cookieString.at(end)))
1004 QByteArray dateString = cookieString.mid(position, end - position).trimmed();
1006 QDateTime dt = parseDateString(dateString.toLower());
1007 if (!dt.isValid()) {
1010 cookie.setExpirationDate(dt);
1011 } else if (field.first == "domain") {
1012 QByteArray rawDomain = field.second;
1013 QString maybeLeadingDot;
1014 if (rawDomain.startsWith('.')) {
1015 maybeLeadingDot = QLatin1Char('.');
1016 rawDomain = rawDomain.mid(1);
1019 QString normalizedDomain = QUrl::fromAce(QUrl::toAce(QString::fromUtf8(rawDomain)));
1020 if (normalizedDomain.isEmpty() && !rawDomain.isEmpty())
1022 cookie.setDomain(maybeLeadingDot + normalizedDomain);
1023 } else if (field.first == "max-age") {
1025 int secs = field.second.toInt(&ok);
1028 cookie.setExpirationDate(now.addSecs(secs));
1029 } else if (field.first == "path") {
1030 QString path = QUrl::fromPercentEncoding(field.second);
1031 cookie.setPath(path);
1032 } else if (field.first == "secure") {
1033 cookie.setSecure(true);
1034 } else if (field.first == "httponly") {
1035 cookie.setHttpOnly(true);
1036 } else if (field.first == "comment") {
1037 //cookie.setComment(QString::fromUtf8(field.second));
1038 } else if (field.first == "version") {
1039 if (field.second != "1") {
1040 // oops, we don't know how to handle this cookie
1044 // got an unknown field in the cookie
1048 position = nextNonWhitespace(cookieString, position);
1052 if (!cookie.name().isEmpty())
1061 This functions normalizes the path and domain of the cookie if they were previously empty.
1062 The \a url parameter is used to determine the correct domain and path.
1064 void QNetworkCookie::normalize(const QUrl &url)
1066 // don't do path checking. See http://bugreports.qt-project.org/browse/QTBUG-5815
1067 if (d->path.isEmpty()) {
1068 QString pathAndFileName = url.path();
1069 QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
1070 if (defaultPath.isEmpty())
1071 defaultPath = QLatin1Char('/');
1072 d->path = defaultPath;
1075 if (d->domain.isEmpty())
1076 d->domain = url.host();
1077 else if (!d->domain.startsWith(QLatin1Char('.')))
1078 // Ensure the domain starts with a dot if its field was not empty
1079 // in the HTTP header. There are some servers that forget the
1080 // leading dot and this is actually forbidden according to RFC 2109,
1081 // but all browsers accept it anyway so we do that as well.
1082 d->domain.prepend(QLatin1Char('.'));
1085 #ifndef QT_NO_DEBUG_STREAM
1086 QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
1088 s.nospace() << "QNetworkCookie(" << cookie.toRawForm(QNetworkCookie::Full) << ')';