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.
155 Returns true if this cookie is equal to \a other. This function
156 only returns true if all fields of the cookie are the same.
158 However, in some contexts, two cookies of the same name could be
161 \sa operator!=(), hasSameIdentifier()
163 bool QNetworkCookie::operator==(const QNetworkCookie &other) const
167 return d->name == other.d->name &&
168 d->value == other.d->value &&
169 d->expirationDate.toUTC() == other.d->expirationDate.toUTC() &&
170 d->domain == other.d->domain &&
171 d->path == other.d->path &&
172 d->secure == other.d->secure &&
173 d->comment == other.d->comment;
177 Returns true if this cookie has the same identifier tuple as \a other.
178 The identifier tuple is composed of the name, domain and path.
182 bool QNetworkCookie::hasSameIdentifier(const QNetworkCookie &other) const
184 return d->name == other.d->name && d->domain == other.d->domain && d->path == other.d->path;
188 Returns true if the "secure" option was specified in the cookie
189 string, false otherwise.
191 Secure cookies may contain private information and should not be
192 resent over unencrypted connections.
196 bool QNetworkCookie::isSecure() const
202 Sets the secure flag of this cookie to \a enable.
204 Secure cookies may contain private information and should not be
205 resent over unencrypted connections.
209 void QNetworkCookie::setSecure(bool enable)
217 Returns true if the "HttpOnly" flag is enabled for this cookie.
219 A cookie that is "HttpOnly" is only set and retrieved by the
220 network requests and replies; i.e., the HTTP protocol. It is not
221 accessible from scripts running on browsers.
225 bool QNetworkCookie::isHttpOnly() const
233 Sets this cookie's "HttpOnly" flag to \a enable.
235 void QNetworkCookie::setHttpOnly(bool enable)
237 d->httpOnly = enable;
241 Returns true if this cookie is a session cookie. A session cookie
242 is a cookie which has no expiration date, which means it should be
243 discarded when the application's concept of session is over
244 (usually, when the application exits).
246 \sa expirationDate(), setExpirationDate()
248 bool QNetworkCookie::isSessionCookie() const
250 return !d->expirationDate.isValid();
254 Returns the expiration date for this cookie. If this cookie is a
255 session cookie, the QDateTime returned will not be valid. If the
256 date is in the past, this cookie has already expired and should
257 not be sent again back to a remote server.
259 The expiration date corresponds to the parameters of the "expires"
260 entry in the cookie string.
262 \sa isSessionCookie(), setExpirationDate()
264 QDateTime QNetworkCookie::expirationDate() const
266 return d->expirationDate;
270 Sets the expiration date of this cookie to \a date. Setting an
271 invalid expiration date to this cookie will mean it's a session
274 \sa isSessionCookie(), expirationDate()
276 void QNetworkCookie::setExpirationDate(const QDateTime &date)
278 d->expirationDate = date;
282 Returns the domain this cookie is associated with. This
283 corresponds to the "domain" field of the cookie string.
285 Note that the domain here may start with a dot, which is not a
286 valid hostname. However, it means this cookie matches all
287 hostnames ending with that domain name.
291 QString QNetworkCookie::domain() const
297 Sets the domain associated with this cookie to be \a domain.
301 void QNetworkCookie::setDomain(const QString &domain)
307 Returns the path associated with this cookie. This corresponds to
308 the "path" field of the cookie string.
312 QString QNetworkCookie::path() const
318 Sets the path associated with this cookie to be \a path.
322 void QNetworkCookie::setPath(const QString &path)
328 Returns the name of this cookie. The only mandatory field of a
329 cookie is its name, without which it is not considered valid.
331 \sa setName(), value()
333 QByteArray QNetworkCookie::name() const
339 Sets the name of this cookie to be \a cookieName. Note that
340 setting a cookie name to an empty QByteArray will make this cookie
345 void QNetworkCookie::setName(const QByteArray &cookieName)
347 d->name = cookieName;
351 Returns this cookies value, as specified in the cookie
352 string. Note that a cookie is still valid if its value is empty.
354 Cookie name-value pairs are considered opaque to the application:
355 that is, their values don't mean anything.
357 \sa setValue(), name()
359 QByteArray QNetworkCookie::value() const
365 Sets the value of this cookie to be \a value.
369 void QNetworkCookie::setValue(const QByteArray &value)
374 // ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
375 static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
380 // (3) token = quoted-string
382 const int length = text.length();
383 position = nextNonWhitespace(text, position);
385 // parse the first part, before the equal sign
386 for (i = position; i < length; ++i) {
387 register char c = text.at(i);
388 if (c == ';' || c == ',' || c == '=')
392 QByteArray first = text.mid(position, i - position).trimmed();
396 return qMakePair(QByteArray(), QByteArray());
397 if (i == length || text.at(i) != '=')
398 // no equal sign, we found format (1)
399 return qMakePair(first, QByteArray());
402 second.reserve(32); // arbitrary but works for most cases
404 i = nextNonWhitespace(text, position + 1);
405 if (i < length && text.at(i) == '"') {
406 // a quote, we found format (3), where:
407 // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
408 // qdtext = <any TEXT except <">>
409 // quoted-pair = "\" CHAR
411 // If it is NAME=VALUE, retain the value as is
412 // refer to http://bugreports.qt-project.org/browse/QTBUG-17746
417 register char c = text.at(i);
419 // end of quoted text
423 } else if (c == '\\') {
429 return qMakePair(QByteArray(), QByteArray());
437 for ( ; i < length; ++i) {
438 register char c = text.at(i);
439 if (c == ',' || c == ';')
444 // no quote, we found format (2)
446 for ( ; i < length; ++i) {
447 register char c = text.at(i);
448 // for name value pairs, we want to parse until reaching the next ';'
449 // and not break when reaching a space char
450 if (c == ',' || c == ';' || ((isNameValue && (c == '\n' || c == '\r')) || (!isNameValue && isLWS(c))))
454 second = text.mid(position, i - position).trimmed();
459 second.resize(0); // turns into empty-but-not-null
460 return qMakePair(first, second);
464 \enum QNetworkCookie::RawForm
466 This enum is used with the toRawForm() function to declare which
467 form of a cookie shall be returned.
469 \value NameAndValueOnly makes toRawForm() return only the
470 "NAME=VALUE" part of the cookie, as suitable for sending back
471 to a server in a client request's "Cookie:" header. Multiple
472 cookies are separated by a semi-colon in the "Cookie:" header
475 \value Full makes toRawForm() return the full
476 cookie contents, as suitable for sending to a client in a
477 server's "Set-Cookie:" header. Multiple cookies are separated
478 by commas in a "Set-Cookie:" header.
480 Note that only the Full form of the cookie can be parsed back into
481 its original contents.
483 \sa toRawForm(), parseCookies()
487 Returns the raw form of this QNetworkCookie. The QByteArray
488 returned by this function is suitable for an HTTP header, either
489 in a server response (the Set-Cookie header) or the client request
490 (the Cookie header). You can choose from one of two formats, using
495 QByteArray QNetworkCookie::toRawForm(RawForm form) const
498 if (d->name.isEmpty())
499 return result; // not a valid cookie
503 if ((d->value.contains(';') ||
504 d->value.contains(',') ||
505 d->value.contains('"')) &&
506 (!d->value.startsWith('"') &&
507 !d->value.endsWith('"'))) {
510 QByteArray value = d->value;
511 value.replace('"', "\\\"");
520 // same as above, but encoding everything back
522 result += "; secure";
524 result += "; HttpOnly";
525 if (!isSessionCookie()) {
526 result += "; expires=";
527 result += QLocale::c().toString(d->expirationDate.toUTC(),
528 QLatin1String("ddd, dd-MMM-yyyy hh:mm:ss 'GMT")).toLatin1();
530 if (!d->domain.isEmpty()) {
531 result += "; domain=";
532 QString domainNoDot = d->domain;
533 if (domainNoDot.startsWith(QLatin1Char('.'))) {
535 domainNoDot = domainNoDot.mid(1);
537 result += QUrl::toAce(domainNoDot);
539 if (!d->path.isEmpty()) {
541 result += QUrl::toPercentEncoding(d->path, "/");
547 static const char zones[] =
565 static int zoneOffsets[] = {-8, -8, -7, -7, -6, -6, -5, -5, -4, -3, 0, 0, 0, 1, 2, 9 };
567 static const char months[] =
582 static inline bool isNumber(char s)
583 { return s >= '0' && s <= '9'; }
585 static inline bool isTerminator(char c)
586 { return c == '\n' || c == '\r'; }
588 static inline bool isValueSeparator(char c)
589 { return isTerminator(c) || c == ';'; }
591 static inline bool isWhitespace(char c)
592 { return c == ' ' || c == '\t'; }
594 static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
596 if (dateString[at] < 'a' || dateString[at] > 'z')
598 if (val == -1 && dateString.length() >= at + 3) {
602 const char *str = array + i;
603 if (str[0] == dateString[at]
604 && str[1] == dateString[at + 1]
605 && str[2] == dateString[at + 2]) {
609 i += strlen(str) + 1;
616 //#define PARSEDATESTRINGDEBUG
623 Parse all the date formats that Firefox can.
625 The official format is:
626 expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT
628 But browsers have been supporting a very wide range of date
629 strings. To work on many sites we need to support more then
630 just the official date format.
632 For reference see Firefox's PR_ParseTimeStringToExplodedTime in
633 prtime.c. The Firefox date parser is coded in a very complex way
634 and is slightly over ~700 lines long. While this implementation
635 will be slightly slower for the non standard dates it is smaller,
636 more readable, and maintainable.
638 Or in their own words:
639 "} // else what the hell is this."
641 static QDateTime parseDateString(const QByteArray &dateString)
644 // placeholders for values when we are not sure it is a year, month or day
645 int unknown[3] = {-1, -1, -1};
651 // hour:minute:second.ms pm
652 QRegExp timeRx(QLatin1String("(\\d{1,2}):(\\d{1,2})(:(\\d{1,2})|)(\\.(\\d{1,3})|)((\\s{0,}(am|pm))|)"));
655 while (at < dateString.length()) {
656 #ifdef PARSEDATESTRINGDEBUG
657 qDebug() << dateString.mid(at);
659 bool isNum = isNumber(dateString[at]);
663 && checkStaticArray(month, dateString, at, months, sizeof(months)- 1)) {
665 #ifdef PARSEDATESTRINGDEBUG
666 qDebug() << "Month:" << month;
674 && checkStaticArray(zoneOffset, dateString, at, zones, sizeof(zones)- 1)) {
675 int sign = (at >= 0 && dateString[at - 1] == '-') ? -1 : 1;
676 zoneOffset = sign * zoneOffsets[zoneOffset] * 60 * 60;
677 #ifdef PARSEDATESTRINGDEBUG
678 qDebug() << "Zone:" << month;
685 && (zoneOffset == -1 || zoneOffset == 0) // Can only go after gmt
686 && (dateString[at] == '+' || dateString[at] == '-')
688 || isWhitespace(dateString[at - 1])
689 || dateString[at - 1] == ','
691 && (dateString[at - 3] == 'g')
692 && (dateString[at - 2] == 'm')
693 && (dateString[at - 1] == 't')))) {
696 while (end < 5 && dateString.length() > at+end
697 && dateString[at + end] >= '0' && dateString[at + end] <= '9')
703 minutes = atoi(dateString.mid(at + 3, 2).constData());
706 hours = atoi(dateString.mid(at + 1, 2).constData());
709 hours = atoi(dateString.mid(at + 1, 1).constData());
716 int sign = dateString[at] == '-' ? -1 : 1;
717 zoneOffset = sign * ((minutes * 60) + (hours * 60 * 60));
718 #ifdef PARSEDATESTRINGDEBUG
719 qDebug() << "Zone offset:" << zoneOffset << hours << minutes;
727 if (isNum && time.isNull()
728 && dateString.length() >= at + 3
729 && (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
730 // While the date can be found all over the string the format
731 // for the time is set and a nice regexp can be used.
732 int pos = timeRx.indexIn(QLatin1String(dateString), at);
734 QStringList list = timeRx.capturedTexts();
735 int h = atoi(list.at(1).toLatin1().constData());
736 int m = atoi(list.at(2).toLatin1().constData());
737 int s = atoi(list.at(4).toLatin1().constData());
738 int ms = atoi(list.at(6).toLatin1().constData());
739 if (h < 12 && !list.at(9).isEmpty())
740 if (list.at(9) == QLatin1String("pm"))
742 time = QTime(h, m, s, ms);
743 #ifdef PARSEDATESTRINGDEBUG
744 qDebug() << "Time:" << list << timeRx.matchedLength();
746 at += timeRx.matchedLength();
754 && dateString.length() > at + 3) {
755 if (isNumber(dateString[at + 1])
756 && isNumber(dateString[at + 2])
757 && isNumber(dateString[at + 3])) {
758 year = atoi(dateString.mid(at, 4).constData());
760 #ifdef PARSEDATESTRINGDEBUG
761 qDebug() << "Year:" << year;
767 // a one or two digit number
768 // Could be month, day or year
771 if (dateString.length() > at + 1
772 && isNumber(dateString[at + 1]))
774 int x = atoi(dateString.mid(at, length).constData());
775 if (year == -1 && (x > 31 || x == 0)) {
778 if (unknown[0] == -1) unknown[0] = x;
779 else if (unknown[1] == -1) unknown[1] = x;
780 else if (unknown[2] == -1) unknown[2] = x;
783 #ifdef PARSEDATESTRINGDEBUG
784 qDebug() << "Saving" << x;
789 // Unknown character, typically a weekday such as 'Mon'
793 // Once we are done parsing the string take the digits in unknown
794 // and determine which is the unknown year/month/day
796 int couldBe[3] = { 0, 0, 0 };
797 int unknownCount = 3;
798 for (int i = 0; i < unknownCount; ++i) {
799 if (unknown[i] == -1) {
800 couldBe[i] = ADAY | AYEAR | AMONTH;
808 if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
809 couldBe[i] |= AMONTH;
815 // For any possible day make sure one of the values that could be a month
816 // can contain that day.
817 // For any possible month make sure one of the values that can be a
818 // day that month can have.
820 // 31 can't be a day because 11 and 6 don't have 31 days
821 for (int i = 0; i < unknownCount; ++i) {
822 int currentValue = unknown[i];
823 bool findMatchingMonth = couldBe[i] & ADAY && currentValue >= 29;
824 bool findMatchingDay = couldBe[i] & AMONTH;
825 if (!findMatchingMonth || !findMatchingDay)
827 for (int j = 0; j < 3; ++j) {
830 for (int k = 0; k < 2; ++k) {
831 if (k == 0 && !(findMatchingMonth && (couldBe[j] & AMONTH)))
833 else if (k == 1 && !(findMatchingDay && (couldBe[j] & ADAY)))
835 int m = currentValue;
839 if (m == -1) m = month;
843 // When we get 29 and the year ends up having only 28
844 // See date.isValid below
845 // Example: 29 23 Feb
849 case 4: case 6: case 9: case 11:
854 if (d > 0 && d <= 31)
857 if (k == 0) findMatchingMonth = found;
858 else if (k == 1) findMatchingDay = found;
861 if (findMatchingMonth)
864 couldBe[i] &= ~AMONTH;
867 // First set the year/month/day that have been deduced
868 // and reduce the set as we go along to deduce more
869 for (int i = 0; i < unknownCount; ++i) {
871 for (int j = 0; j < 3; ++j) {
872 if (couldBe[j] == ADAY && day == -1) {
875 } else if (couldBe[j] == AMONTH && month == -1) {
878 } else if (couldBe[j] == AYEAR && year == -1) {
885 couldBe[j] &= ~unset;
889 // Now fallback to a standardized order to fill in the rest with
890 for (int i = 0; i < unknownCount; ++i) {
891 if (couldBe[i] & AMONTH && month == -1) month = unknown[i];
892 else if (couldBe[i] & ADAY && day == -1) day = unknown[i];
893 else if (couldBe[i] & AYEAR && year == -1) year = unknown[i];
895 #ifdef PARSEDATESTRINGDEBUG
896 qDebug() << "Final set" << year << month << day;
899 if (year == -1 || month == -1 || day == -1) {
900 #ifdef PARSEDATESTRINGDEBUG
901 qDebug() << "Parser failure" << year << month << day;
913 QDate date(year + y2k, month, day);
915 // When we were given a bad cookie that when parsed
916 // set the day to 29 and the year to one that doesn't
917 // have the 29th of Feb rather then adding the extra
918 // complicated checking earlier just swap here.
919 // Example: 29 23 Feb
921 date = QDate(day + y2k, month, year);
923 QDateTime dateTime(date, time, Qt::UTC);
925 if (zoneOffset != -1) {
926 dateTime = dateTime.addSecs(zoneOffset);
928 if (!dateTime.isValid())
934 Parses the cookie string \a cookieString as received from a server
935 response in the "Set-Cookie:" header. If there's a parsing error,
936 this function returns an empty list.
938 Since the HTTP header can set more than one cookie at the same
939 time, this function returns a QList<QNetworkCookie>, one for each
940 cookie that is parsed.
944 QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
946 // cookieString can be a number of set-cookie header strings joined together
947 // by \n, parse each line separately.
948 QList<QNetworkCookie> cookies;
949 QList<QByteArray> list = cookieString.split('\n');
950 for (int a = 0; a < list.size(); a++)
951 cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
955 QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
957 // According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
958 // the Set-Cookie response header is of the format:
960 // Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
962 // where only the NAME=VALUE part is mandatory
964 // We do not support RFC 2965 Set-Cookie2-style cookies
966 QList<QNetworkCookie> result;
967 QDateTime now = QDateTime::currentDateTime().toUTC();
970 const int length = cookieString.length();
971 while (position < length) {
972 QNetworkCookie cookie;
974 // The first part is always the "NAME=VALUE" part
975 QPair<QByteArray,QByteArray> field = nextField(cookieString, position, true);
976 if (field.first.isEmpty() || field.second.isNull())
979 cookie.setName(field.first);
980 cookie.setValue(field.second);
982 position = nextNonWhitespace(cookieString, position);
983 bool endOfCookie = false;
984 while (!endOfCookie && position < length) {
985 switch (cookieString.at(position++)) {
992 // new field in the cookie
993 field = nextField(cookieString, position, false);
994 field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
996 if (field.first == "expires") {
997 position -= field.second.length();
999 for (end = position; end < length; ++end)
1000 if (isValueSeparator(cookieString.at(end)))
1003 QByteArray dateString = cookieString.mid(position, end - position).trimmed();
1005 QDateTime dt = parseDateString(dateString.toLower());
1006 if (!dt.isValid()) {
1009 cookie.setExpirationDate(dt);
1010 } else if (field.first == "domain") {
1011 QByteArray rawDomain = field.second;
1012 QString maybeLeadingDot;
1013 if (rawDomain.startsWith('.')) {
1014 maybeLeadingDot = QLatin1Char('.');
1015 rawDomain = rawDomain.mid(1);
1018 QString normalizedDomain = QUrl::fromAce(QUrl::toAce(QString::fromUtf8(rawDomain)));
1019 if (normalizedDomain.isEmpty() && !rawDomain.isEmpty())
1021 cookie.setDomain(maybeLeadingDot + normalizedDomain);
1022 } else if (field.first == "max-age") {
1024 int secs = field.second.toInt(&ok);
1027 cookie.setExpirationDate(now.addSecs(secs));
1028 } else if (field.first == "path") {
1029 QString path = QUrl::fromPercentEncoding(field.second);
1030 cookie.setPath(path);
1031 } else if (field.first == "secure") {
1032 cookie.setSecure(true);
1033 } else if (field.first == "httponly") {
1034 cookie.setHttpOnly(true);
1035 } else if (field.first == "comment") {
1036 //cookie.setComment(QString::fromUtf8(field.second));
1037 } else if (field.first == "version") {
1038 if (field.second != "1") {
1039 // oops, we don't know how to handle this cookie
1043 // got an unknown field in the cookie
1047 position = nextNonWhitespace(cookieString, position);
1051 if (!cookie.name().isEmpty())
1059 This functions normalizes the path and domain of the cookie if they were previously empty.
1061 void QNetworkCookie::normalize(const QUrl &url)
1063 // don't do path checking. See http://bugreports.qt-project.org/browse/QTBUG-5815
1064 if (d->path.isEmpty()) {
1065 QString pathAndFileName = url.path();
1066 QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
1067 if (defaultPath.isEmpty())
1068 defaultPath = QLatin1Char('/');
1069 d->path = defaultPath;
1072 if (d->domain.isEmpty())
1073 d->domain = url.host();
1074 else if (!d->domain.startsWith(QLatin1Char('.')))
1075 // Ensure the domain starts with a dot if its field was not empty
1076 // in the HTTP header. There are some servers that forget the
1077 // leading dot and this is actually forbidden according to RFC 2109,
1078 // but all browsers accept it anyway so we do that as well.
1079 d->domain.prepend(QLatin1Char('.'));
1082 #ifndef QT_NO_DEBUG_STREAM
1083 QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
1085 s.nospace() << "QNetworkCookie(" << cookie.toRawForm(QNetworkCookie::Full) << ')';