From 8ba781b01e900148fec2e9d26485369b3295487f Mon Sep 17 00:00:00 2001
From: Jocelyn Turcotte <jocelyn.turcotte@nokia.com>
Date: Wed, 14 Sep 2011 16:12:10 +0200
Subject: Remove support for multiple cookies in one Set-Cookie header to
 follow RFC6265.

This also allows cookie values to contain commas to increase compatibility like
most popular browsers do even though the RFC still reserves them for future uses.

Reviewed-by: Peter Hartmann <peter.hartmann@nokia.com>
Task-number: QTBUG-21456
---
 dist/changes-4.8.0                               |  1 +
 src/network/access/qnetworkcookie.cpp            | 18 +++-----
 tests/auto/qnetworkcookie/tst_qnetworkcookie.cpp | 54 +++++++-----------------
 3 files changed, 21 insertions(+), 52 deletions(-)

diff --git a/dist/changes-4.8.0 b/dist/changes-4.8.0
index b8e3842..e208fa1 100644
--- a/dist/changes-4.8.0
+++ b/dist/changes-4.8.0
@@ -105,6 +105,7 @@ QtNetwork
  - HTTP cache: do not load resources from cache that must be revalidated [QTBUG-18983]
  - HTTP cache: change file organization (MR-2505)
  - SOCKS5: write errors are propagated to the outer socket [QTBUG-18713]
+ - Cookies: Commas are no longer used to support multiple cookies in a single Set-Cookie header [QTBUG-21456]
 
  QtOpenGL
  --------
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 0670738..a9bb318 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -372,7 +372,7 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
     // parse the first part, before the equal sign
     for (i = position; i < length; ++i) {
         register char c = text.at(i);
-        if (c == ';' || c == ',' || c == '=')
+        if (c == ';' || c == '=')
             break;
     }
 
@@ -423,7 +423,7 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
 
         for ( ; i < length; ++i) {
             register char c = text.at(i);
-            if (c == ',' || c == ';')
+            if (c == ';')
                 break;
         }
         position = i;
@@ -434,7 +434,7 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
             register char c = text.at(i);
             // for name value pairs, we want to parse until reaching the next ';'
             // and not break when reaching a space char
-            if (c == ',' || c == ';' || ((isNameValue && (c == '\n' || c == '\r')) || (!isNameValue && isLWS(c))))
+            if (c == ';' || ((isNameValue && (c == '\n' || c == '\r')) || (!isNameValue && isLWS(c))))
                 break;
         }
 
@@ -461,8 +461,7 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
 
     \value Full                 makes toRawForm() return the full
         cookie contents, as suitable for sending to a client in a
-        server's "Set-Cookie:" header. Multiple cookies are separated
-        by commas in a "Set-Cookie:" header.
+        server's "Set-Cookie:" header.
 
     Note that only the Full form of the cookie can be parsed back into
     its original contents.
@@ -488,7 +487,6 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
     result = d->name;
     result += '=';
     if ((d->value.contains(';') ||
-        d->value.contains(',') ||
         d->value.contains('"')) &&
         (!d->value.startsWith('"') &&
         !d->value.endsWith('"'))) {
@@ -967,14 +965,8 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
         cookie.setValue(field.second);
 
         position = nextNonWhitespace(cookieString, position);
-        bool endOfCookie = false;
-        while (!endOfCookie && position < length) {
+        while (position < length) {
             switch (cookieString.at(position++)) {
-            case ',':
-                // end of the cookie
-                endOfCookie = true;
-                break;
-
             case ';':
                 // new field in the cookie
                 field = nextField(cookieString, position, false);
diff --git a/tests/auto/qnetworkcookie/tst_qnetworkcookie.cpp b/tests/auto/qnetworkcookie/tst_qnetworkcookie.cpp
index a83f6dd..e4c88cd 100644
--- a/tests/auto/qnetworkcookie/tst_qnetworkcookie.cpp
+++ b/tests/auto/qnetworkcookie/tst_qnetworkcookie.cpp
@@ -181,6 +181,14 @@ void tst_QNetworkCookie::parseSingleCookie_data()
     QTest::newRow("with-value-with-special4") << "a = \"\\\"\" " << cookie;
     cookie.setValue("\"\\\"a, b; c\\\"\"");
     QTest::newRow("with-value-with-special5") << "a = \"\\\"a, b; c\\\"\"" << cookie;
+    // RFC6265 states that cookie values shouldn't contain commas, but we still allow them
+    // since they are only reserved for future compatibility and other browsers do the same.
+    cookie.setValue(",");
+    QTest::newRow("with-value-with-special6") << "a = ," << cookie;
+    cookie.setValue(",b");
+    QTest::newRow("with-value-with-special7") << "a = ,b" << cookie;
+    cookie.setValue("b,");
+    QTest::newRow("with-value-with-special8") << "a = b," << cookie;
 
     cookie.setValue("b c");
     QTest::newRow("with-value-with-whitespace") << "a = b c" << cookie;
@@ -599,7 +607,6 @@ void tst_QNetworkCookie::parseSingleCookie()
 
     //QEXPECT_FAIL("network2", "QDateTime parsing problem: the date is beyond year 8000", Abort);
     QCOMPARE(result.count(), 1);
-    QEXPECT_FAIL("network3", "Cookie value contains commas, violating the HTTP spec", Abort);
     QCOMPARE(result.at(0), expectedCookie);
 
     result = QNetworkCookie::parseCookies(result.at(0).toRawForm());
@@ -634,48 +641,17 @@ void tst_QNetworkCookie::parseMultipleCookies_data()
     // reason: malformed NAME=VALUE pair
     QTest::newRow("invalid-05") << "foo" << list;
     QTest::newRow("invalid-06") << "=b" << list;
-    QTest::newRow("invalid-09") << "foo,a=b" << list;
-    QTest::newRow("invalid-11") << ";path=/" << list;
+    QTest::newRow("invalid-07") << ";path=/" << list;
 
     // reason: malformed expiration date string
-    QTest::newRow("invalid-12") << "a=b;expires=" << list;
-    QTest::newRow("invalid-13") << "a=b;expires=foobar" << list;
-    QTest::newRow("invalid-14") << "a=b;expires=foobar, abc" << list;
-    QTest::newRow("invalid-15") << "a=b;expires=foobar, dd-mmm-yyyy hh:mm:ss GMT; path=/" << list;
-    QTest::newRow("invalid-16") << "a=b;expires=foobar, 32-Caz-1999 24:01:60 GMT; path=/" << list;
-
-    QNetworkCookie cookie;
-    cookie.setName("a");
-    list += cookie;
-    cookie.setName("c");
-    cookie.setValue("d");
-    list += cookie;
-    QTest::newRow("two-1") << "a=,c=d" << list;
-    QTest::newRow("two-2") << "a=, c=d" << list;
-    QTest::newRow("two-3") << "a= ,c=d" << list;
-    QTest::newRow("two-4") << "a= , c=d" << list;
-
-    list.clear();
-    list += cookie;
-    cookie.setName("a");
-    cookie.setValue(QByteArray());
-    list += cookie;
-    QTest::newRow("two-5") << "c=d,a=" << list;
-    QTest::newRow("two-6") << "c=d, a=" << list;
-    QTest::newRow("two-7") << "c=d , a=" << list;
-
-    cookie.setName("foo");
-    cookie.setValue("bar");
-    cookie.setPath("/");
-    list += cookie;
-    QTest::newRow("complex-1") << "c=d, a=, foo=bar; path=/" << list;
-
-    cookie.setName("baz");
-    cookie.setDomain(".qt.nokia.com");
-    list.prepend(cookie);
-    QTest::newRow("complex-2") << "baz=bar; path=/; domain=.qt.nokia.com, c=d,a=,foo=bar; path=/" << list;
+    QTest::newRow("invalid-08") << "a=b;expires=" << list;
+    QTest::newRow("invalid-09") << "a=b;expires=foobar" << list;
+    QTest::newRow("invalid-10") << "a=b;expires=foobar, abc" << list;
+    QTest::newRow("invalid-11") << "a=b;expires=foobar, dd-mmm-yyyy hh:mm:ss GMT; path=/" << list;
+    QTest::newRow("invalid-12") << "a=b;expires=foobar, 32-Caz-1999 24:01:60 GMT; path=/" << list;
 
     // cookies obtained from the network:
+    QNetworkCookie cookie;
     cookie = QNetworkCookie("id", "51706646077999719");
     cookie.setDomain(".bluestreak.com");
     cookie.setPath("/");
-- 
cgit v0.12