Update spec to build Qt 5.0
[profile/ivi/qtbase.git] / src / network / access / qnetworkcookie.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
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.
16 **
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.
24 **
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.
28 **
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.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qnetworkcookie.h"
43 #include "qnetworkcookie_p.h"
44
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"
55
56 QT_BEGIN_NAMESPACE
57
58 /*!
59     \class QNetworkCookie
60     \since 4.4
61     \ingroup shared
62     \inmodule QtNetwork
63
64     \brief The QNetworkCookie class holds one network cookie.
65
66     Cookies are small bits of information that stateless protocols
67     like HTTP use to maintain some persistent information across
68     requests.
69
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
72     requests are sent.
73
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
79     the server.
80
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).
85
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
89     already parsed.
90
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.
97
98     \sa QNetworkCookieJar, QNetworkRequest, QNetworkReply
99 */
100
101 /*!
102     Create a new QNetworkCookie object, initializing the cookie name
103     to \a name and its value to \a value.
104
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
107     the remote server.
108 */
109 QNetworkCookie::QNetworkCookie(const QByteArray &name, const QByteArray &value)
110     : d(new QNetworkCookiePrivate)
111 {
112     qRegisterMetaType<QNetworkCookie>();
113     qRegisterMetaType<QList<QNetworkCookie> >();
114
115     d->name = name;
116     d->value = value;
117 }
118
119 /*!
120     Creates a new QNetworkCookie object by copying the contents of \a
121     other.
122 */
123 QNetworkCookie::QNetworkCookie(const QNetworkCookie &other)
124     : d(other.d)
125 {
126 }
127
128 /*!
129     Destroys this QNetworkCookie object.
130 */
131 QNetworkCookie::~QNetworkCookie()
132 {
133     // QSharedDataPointer auto deletes
134     d = 0;
135 }
136
137 /*!
138     Copies the contents of the QNetworkCookie object \a other to this
139     object.
140 */
141 QNetworkCookie &QNetworkCookie::operator=(const QNetworkCookie &other)
142 {
143     d = other.d;
144     return *this;
145 }
146
147 /*!
148     \fn void QNetworkCookie::swap(QNetworkCookie &other)
149     \since 5.0
150
151     Swaps this cookie with \a other. This function is very fast and
152     never fails.
153 */
154
155 /*!
156     \fn bool QNetworkCookie::operator!=(const QNetworkCookie &other) const
157
158     Returns true if this cookie is not equal to \a other.
159
160     \sa operator==()
161 */
162
163 /*!
164     \since 5.0
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.
167
168     However, in some contexts, two cookies of the same name could be
169     considered equal.
170
171     \sa operator!=(), hasSameIdentifier()
172 */
173 bool QNetworkCookie::operator==(const QNetworkCookie &other) const
174 {
175     if (d == other.d)
176         return true;
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;
184 }
185
186 /*!
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.
189
190     \sa operator==()
191 */
192 bool QNetworkCookie::hasSameIdentifier(const QNetworkCookie &other) const
193 {
194     return d->name == other.d->name && d->domain == other.d->domain && d->path == other.d->path;
195 }
196
197 /*!
198     Returns true if the "secure" option was specified in the cookie
199     string, false otherwise.
200
201     Secure cookies may contain private information and should not be
202     resent over unencrypted connections.
203
204     \sa setSecure()
205 */
206 bool QNetworkCookie::isSecure() const
207 {
208     return d->secure;
209 }
210
211 /*!
212     Sets the secure flag of this cookie to \a enable.
213
214     Secure cookies may contain private information and should not be
215     resent over unencrypted connections.
216
217     \sa isSecure()
218 */
219 void QNetworkCookie::setSecure(bool enable)
220 {
221     d->secure = enable;
222 }
223
224 /*!
225     \since 4.5
226
227     Returns true if the "HttpOnly" flag is enabled for this cookie.
228
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.
232
233     \sa isSecure()
234 */
235 bool QNetworkCookie::isHttpOnly() const
236 {
237     return d->httpOnly;
238 }
239
240 /*!
241     \since 4.5
242
243     Sets this cookie's "HttpOnly" flag to \a enable.
244 */
245 void QNetworkCookie::setHttpOnly(bool enable)
246 {
247     d->httpOnly = enable;
248 }
249
250 /*!
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).
255
256     \sa expirationDate(), setExpirationDate()
257 */
258 bool QNetworkCookie::isSessionCookie() const
259 {
260     return !d->expirationDate.isValid();
261 }
262
263 /*!
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.
268
269     The expiration date corresponds to the parameters of the "expires"
270     entry in the cookie string.
271
272     \sa isSessionCookie(), setExpirationDate()
273 */
274 QDateTime QNetworkCookie::expirationDate() const
275 {
276     return d->expirationDate;
277 }
278
279 /*!
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
282     cookie.
283
284     \sa isSessionCookie(), expirationDate()
285 */
286 void QNetworkCookie::setExpirationDate(const QDateTime &date)
287 {
288     d->expirationDate = date;
289 }
290
291 /*!
292     Returns the domain this cookie is associated with. This
293     corresponds to the "domain" field of the cookie string.
294
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.
298
299     \sa setDomain()
300 */
301 QString QNetworkCookie::domain() const
302 {
303     return d->domain;
304 }
305
306 /*!
307     Sets the domain associated with this cookie to be \a domain.
308
309     \sa domain()
310 */
311 void QNetworkCookie::setDomain(const QString &domain)
312 {
313     d->domain = domain;
314 }
315
316 /*!
317     Returns the path associated with this cookie. This corresponds to
318     the "path" field of the cookie string.
319
320     \sa setPath()
321 */
322 QString QNetworkCookie::path() const
323 {
324     return d->path;
325 }
326
327 /*!
328     Sets the path associated with this cookie to be \a path.
329
330     \sa path()
331 */
332 void QNetworkCookie::setPath(const QString &path)
333 {
334     d->path = path;
335 }
336
337 /*!
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.
340
341     \sa setName(), value()
342 */
343 QByteArray QNetworkCookie::name() const
344 {
345     return d->name;
346 }
347
348 /*!
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
351     invalid.
352
353     \sa name(), value()
354 */
355 void QNetworkCookie::setName(const QByteArray &cookieName)
356 {
357     d->name = cookieName;
358 }
359
360 /*!
361     Returns this cookies value, as specified in the cookie
362     string. Note that a cookie is still valid if its value is empty.
363
364     Cookie name-value pairs are considered opaque to the application:
365     that is, their values don't mean anything.
366
367     \sa setValue(), name()
368 */
369 QByteArray QNetworkCookie::value() const
370 {
371     return d->value;
372 }
373
374 /*!
375     Sets the value of this cookie to be \a value.
376
377     \sa value(), name()
378 */
379 void QNetworkCookie::setValue(const QByteArray &value)
380 {
381     d->value = value;
382 }
383
384 // ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
385 static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
386 {
387     // format is one of:
388     //    (1)  token
389     //    (2)  token = token
390     //    (3)  token = quoted-string
391     const int length = text.length();
392     position = nextNonWhitespace(text, position);
393
394     int semiColonPosition = text.indexOf(';', position);
395     if (semiColonPosition < 0)
396         semiColonPosition = length; //no ';' means take everything to end of string
397
398     int equalsPosition = text.indexOf('=', position);
399     if (equalsPosition < 0 || equalsPosition > semiColonPosition) {
400         if (isNameValue)
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
403     }
404
405     QByteArray first = text.mid(position, equalsPosition - position).trimmed();
406     QByteArray second;
407     int secondLength = semiColonPosition - equalsPosition - 1;
408     if (secondLength > 0)
409         second = text.mid(equalsPosition + 1, secondLength).trimmed();
410
411     position = semiColonPosition;
412     return qMakePair(first, second);
413 }
414
415 /*!
416     \enum QNetworkCookie::RawForm
417
418     This enum is used with the toRawForm() function to declare which
419     form of a cookie shall be returned.
420
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
425         field.
426
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.
430
431     Note that only the Full form of the cookie can be parsed back into
432     its original contents.
433
434     \sa toRawForm(), parseCookies()
435 */
436
437 /*!
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
442     \a form.
443
444     \sa parseCookies()
445 */
446 QByteArray QNetworkCookie::toRawForm(RawForm form) const
447 {
448     QByteArray result;
449     if (d->name.isEmpty())
450         return result;          // not a valid cookie
451
452     result = d->name;
453     result += '=';
454     result += d->value;
455
456     if (form == Full) {
457         // same as above, but encoding everything back
458         if (isSecure())
459             result += "; secure";
460         if (isHttpOnly())
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();
466         }
467         if (!d->domain.isEmpty()) {
468             result += "; domain=";
469             QString domainNoDot = d->domain;
470             if (domainNoDot.startsWith(QLatin1Char('.'))) {
471                 result += '.';
472                 domainNoDot = domainNoDot.mid(1);
473             }
474             result += QUrl::toAce(domainNoDot);
475         }
476         if (!d->path.isEmpty()) {
477             result += "; path=";
478             result += d->path.toUtf8();
479         }
480     }
481     return result;
482 }
483
484 static const char zones[] =
485     "pst\0" // -8
486     "pdt\0"
487     "mst\0" // -7
488     "mdt\0"
489     "cst\0" // -6
490     "cdt\0"
491     "est\0" // -5
492     "edt\0"
493     "ast\0" // -4
494     "nst\0" // -3
495     "gmt\0" // 0
496     "utc\0"
497     "bst\0"
498     "met\0" // 1
499     "eet\0" // 2
500     "jst\0" // 9
501     "\0";
502 static int zoneOffsets[] = {-8, -8, -7, -7, -6, -6, -5, -5, -4, -3, 0, 0, 0, 1, 2, 9 };
503
504 static const char months[] =
505     "jan\0"
506     "feb\0"
507     "mar\0"
508     "apr\0"
509     "may\0"
510     "jun\0"
511     "jul\0"
512     "aug\0"
513     "sep\0"
514     "oct\0"
515     "nov\0"
516     "dec\0"
517     "\0";
518
519 static inline bool isNumber(char s)
520 { return s >= '0' && s <= '9'; }
521
522 static inline bool isTerminator(char c)
523 { return c == '\n' || c == '\r'; }
524
525 static inline bool isValueSeparator(char c)
526 { return isTerminator(c) || c == ';'; }
527
528 static inline bool isWhitespace(char c)
529 { return c == ' '  || c == '\t'; }
530
531 static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
532 {
533     if (dateString[at] < 'a' || dateString[at] > 'z')
534         return false;
535     if (val == -1 && dateString.length() >= at + 3) {
536         int j = 0;
537         int i = 0;
538         while (i <= size) {
539             const char *str = array + i;
540             if (str[0] == dateString[at]
541                 && str[1] == dateString[at + 1]
542                 && str[2] == dateString[at + 2]) {
543                 val = j;
544                 return true;
545             }
546             i += int(strlen(str)) + 1;
547             ++j;
548         }
549     }
550     return false;
551 }
552
553 //#define PARSEDATESTRINGDEBUG
554
555 #define ADAY   1
556 #define AMONTH 2
557 #define AYEAR  4
558
559 /*
560     Parse all the date formats that Firefox can.
561
562     The official format is:
563     expires=ddd(d)?, dd-MMM-yyyy hh:mm:ss GMT
564
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.
568
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.
574
575     Or in their own words:
576         "} // else what the hell is this."
577 */
578 static QDateTime parseDateString(const QByteArray &dateString)
579 {
580     QTime time;
581     // placeholders for values when we are not sure it is a year, month or day
582     int unknown[3] = {-1, -1, -1};
583     int month = -1;
584     int day = -1;
585     int year = -1;
586     int zoneOffset = -1;
587
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))|)"));
590
591     int at = 0;
592     while (at < dateString.length()) {
593 #ifdef PARSEDATESTRINGDEBUG
594         qDebug() << dateString.mid(at);
595 #endif
596         bool isNum = isNumber(dateString[at]);
597
598         // Month
599         if (!isNum
600             && checkStaticArray(month, dateString, at, months, sizeof(months)- 1)) {
601             ++month;
602 #ifdef PARSEDATESTRINGDEBUG
603             qDebug() << "Month:" << month;
604 #endif
605             at += 3;
606             continue;
607         }
608         // Zone
609         if (!isNum
610             && zoneOffset == -1
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;
616 #endif
617             at += 3;
618             continue;
619         }
620         // Zone offset
621         if (!isNum
622             && (zoneOffset == -1 || zoneOffset == 0) // Can only go after gmt
623             && (dateString[at] == '+' || dateString[at] == '-')
624             && (at == 0
625                 || isWhitespace(dateString[at - 1])
626                 || dateString[at - 1] == ','
627                 || (at >= 3
628                     && (dateString[at - 3] == 'g')
629                     && (dateString[at - 2] == 'm')
630                     && (dateString[at - 1] == 't')))) {
631
632             int end = 1;
633             while (end < 5 && dateString.length() > at+end
634                    && dateString[at + end] >= '0' && dateString[at + end] <= '9')
635                 ++end;
636             int minutes = 0;
637             int hours = 0;
638             switch (end - 1) {
639             case 4:
640                 minutes = atoi(dateString.mid(at + 3, 2).constData());
641                 // fall through
642             case 2:
643                 hours = atoi(dateString.mid(at + 1, 2).constData());
644                 break;
645             case 1:
646                 hours = atoi(dateString.mid(at + 1, 1).constData());
647                 break;
648             default:
649                 at += end;
650                 continue;
651             }
652             if (end != 1) {
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;
657 #endif
658                 at += end;
659                 continue;
660             }
661         }
662
663         // Time
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);
670             if (pos != -1) {
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"))
678                         h += 12;
679                 time = QTime(h, m, s, ms);
680 #ifdef PARSEDATESTRINGDEBUG
681                 qDebug() << "Time:" << list << timeRx.matchedLength();
682 #endif
683                 at += timeRx.matchedLength();
684                 continue;
685             }
686         }
687
688         // 4 digit Year
689         if (isNum
690             && year == -1
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());
696                 at += 4;
697 #ifdef PARSEDATESTRINGDEBUG
698                 qDebug() << "Year:" << year;
699 #endif
700                 continue;
701             }
702         }
703
704         // a one or two digit number
705         // Could be month, day or year
706         if (isNum) {
707             int length = 1;
708             if (dateString.length() > at + 1
709                 && isNumber(dateString[at + 1]))
710                 ++length;
711             int x = atoi(dateString.mid(at, length).constData());
712             if (year == -1 && (x > 31 || x == 0)) {
713                 year = x;
714             } else {
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;
718             }
719             at += length;
720 #ifdef PARSEDATESTRINGDEBUG
721             qDebug() << "Saving" << x;
722 #endif
723             continue;
724         }
725
726         // Unknown character, typically a weekday such as 'Mon'
727         ++at;
728     }
729
730     // Once we are done parsing the string take the digits in unknown
731     // and determine which is the unknown year/month/day
732
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;
738             unknownCount = i;
739             continue;
740         }
741
742         if (unknown[i] >= 1)
743             couldBe[i] = ADAY;
744
745         if (month == -1 && unknown[i] >= 1 && unknown[i] <= 12)
746             couldBe[i] |= AMONTH;
747
748         if (year == -1)
749             couldBe[i] |= AYEAR;
750     }
751
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.
756     // Example: 31 11 06
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)
763             continue;
764         for (int j = 0; j < 3; ++j) {
765             if (j == i)
766                 continue;
767             for (int k = 0; k < 2; ++k) {
768                 if (k == 0 && !(findMatchingMonth && (couldBe[j] & AMONTH)))
769                     continue;
770                 else if (k == 1 && !(findMatchingDay && (couldBe[j] & ADAY)))
771                     continue;
772                 int m = currentValue;
773                 int d = unknown[j];
774                 if (k == 0)
775                     qSwap(m, d);
776                 if (m == -1) m = month;
777                 bool found = true;
778                 switch(m) {
779                     case 2:
780                         // When we get 29 and the year ends up having only 28
781                         // See date.isValid below
782                         // Example: 29 23 Feb
783                         if (d <= 29)
784                             found = false;
785                         break;
786                     case 4: case 6: case 9: case 11:
787                         if (d <= 30)
788                             found = false;
789                         break;
790                     default:
791                         if (d > 0 && d <= 31)
792                             found = false;
793                 }
794                 if (k == 0) findMatchingMonth = found;
795                 else if (k == 1) findMatchingDay = found;
796             }
797         }
798         if (findMatchingMonth)
799             couldBe[i] &= ~ADAY;
800         if (findMatchingDay)
801             couldBe[i] &= ~AMONTH;
802     }
803
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) {
807         int unset = 0;
808         for (int j = 0; j < 3; ++j) {
809             if (couldBe[j] == ADAY && day == -1) {
810                 day = unknown[j];
811                 unset |= ADAY;
812             } else if (couldBe[j] == AMONTH && month == -1) {
813                 month = unknown[j];
814                 unset |= AMONTH;
815             } else if (couldBe[j] == AYEAR && year == -1) {
816                 year = unknown[j];
817                 unset |= AYEAR;
818             } else {
819                 // common case
820                 break;
821             }
822             couldBe[j] &= ~unset;
823         }
824     }
825
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];
831     }
832 #ifdef PARSEDATESTRINGDEBUG
833         qDebug() << "Final set" << year << month << day;
834 #endif
835
836     if (year == -1 || month == -1 || day == -1) {
837 #ifdef PARSEDATESTRINGDEBUG
838         qDebug() << "Parser failure" << year << month << day;
839 #endif
840         return QDateTime();
841     }
842
843     // Y2k behavior
844     int y2k = 0;
845     if (year < 70)
846         y2k = 2000;
847     else if (year < 100)
848         y2k = 1900;
849
850     QDate date(year + y2k, month, day);
851
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
857     if (!date.isValid())
858         date = QDate(day + y2k, month, year);
859
860     QDateTime dateTime(date, time, Qt::UTC);
861
862     if (zoneOffset != -1) {
863         dateTime = dateTime.addSecs(zoneOffset);
864     }
865     if (!dateTime.isValid())
866         return QDateTime();
867     return dateTime;
868 }
869
870 /*!
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.
874
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.
878
879     \sa toRawForm()
880 */
881 QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
882 {
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));
889     return cookies;
890 }
891
892 QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
893 {
894     // According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
895     // the Set-Cookie response header is of the format:
896     //
897     //   Set-Cookie: NAME=VALUE; expires=DATE; path=PATH; domain=DOMAIN_NAME; secure
898     //
899     // where only the NAME=VALUE part is mandatory
900     //
901     // We do not support RFC 2965 Set-Cookie2-style cookies
902
903     QList<QNetworkCookie> result;
904     QDateTime now = QDateTime::currentDateTime().toUTC();
905
906     int position = 0;
907     const int length = cookieString.length();
908     while (position < length) {
909         QNetworkCookie cookie;
910
911         // The first part is always the "NAME=VALUE" part
912         QPair<QByteArray,QByteArray> field = nextField(cookieString, position, true);
913         if (field.first.isEmpty())
914             // parsing error
915             break;
916         cookie.setName(field.first);
917         cookie.setValue(field.second);
918
919         position = nextNonWhitespace(cookieString, position);
920         while (position < length) {
921             switch (cookieString.at(position++)) {
922             case ';':
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
926
927                 if (field.first == "expires") {
928                     position -= field.second.length();
929                     int end;
930                     for (end = position; end < length; ++end)
931                         if (isValueSeparator(cookieString.at(end)))
932                             break;
933
934                     QByteArray dateString = cookieString.mid(position, end - position).trimmed();
935                     position = end;
936                     QDateTime dt = parseDateString(dateString.toLower());
937                     if (dt.isValid())
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);
948                         }
949
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);
954                         } else {
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.
958                             return result;
959                         }
960                     }
961                 } else if (field.first == "max-age") {
962                     bool ok = false;
963                     int secs = field.second.toInt(&ok);
964                     if (ok) {
965                         if (secs <= 0) {
966                             //earliest representable time (RFC6265 section 5.2.2)
967                             cookie.setExpirationDate(QDateTime::fromTime_t(0));
968                         } else {
969                             cookie.setExpirationDate(now.addSecs(secs));
970                         }
971                     }
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));
978                     } else {
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());
982                     }
983                 } else if (field.first == "secure") {
984                     cookie.setSecure(true);
985                 } else if (field.first == "httponly") {
986                     cookie.setHttpOnly(true);
987                 } else {
988                     // ignore unknown fields in the cookie (RFC6265 section 5.2, rule 6)
989                 }
990
991                 position = nextNonWhitespace(cookieString, position);
992             }
993         }
994
995         if (!cookie.name().isEmpty())
996             result += cookie;
997     }
998
999     return result;
1000 }
1001
1002 /*!
1003     \since 5.0
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.
1006 */
1007 void QNetworkCookie::normalize(const QUrl &url)
1008 {
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;
1016     }
1017
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('.'));
1026 }
1027
1028 #ifndef QT_NO_DEBUG_STREAM
1029 QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
1030 {
1031     s.nospace() << "QNetworkCookie(" << cookie.toRawForm(QNetworkCookie::Full) << ')';
1032     return s.space();
1033 }
1034 #endif
1035
1036 QT_END_NAMESPACE