summaryrefslogtreecommitdiffstats
path: root/kioslave/http/kcookiejar
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitce4a32fe52ef09d8f5ff1dd22c001110902b60a2 (patch)
tree5ac38a06f3dde268dc7927dc155896926aaf7012 /kioslave/http/kcookiejar
downloadtdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.tar.gz
tdelibs-ce4a32fe52ef09d8f5ff1dd22c001110902b60a2.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kioslave/http/kcookiejar')
-rw-r--r--kioslave/http/kcookiejar/Makefile.am31
-rw-r--r--kioslave/http/kcookiejar/domain_info1
-rw-r--r--kioslave/http/kcookiejar/kcookiejar.cpp1558
-rw-r--r--kioslave/http/kcookiejar/kcookiejar.desktop157
-rw-r--r--kioslave/http/kcookiejar/kcookiejar.h365
-rw-r--r--kioslave/http/kcookiejar/kcookiescfg.upd16
-rw-r--r--kioslave/http/kcookiejar/kcookieserver.cpp606
-rw-r--r--kioslave/http/kcookiejar/kcookieserver.h98
-rw-r--r--kioslave/http/kcookiejar/kcookiewin.cpp382
-rw-r--r--kioslave/http/kcookiejar/kcookiewin.h84
-rw-r--r--kioslave/http/kcookiejar/main.cpp92
-rw-r--r--kioslave/http/kcookiejar/netscape_cookie_spec.html331
-rw-r--r--kioslave/http/kcookiejar/rfc21091179
-rw-r--r--kioslave/http/kcookiejar/rfc29651459
-rw-r--r--kioslave/http/kcookiejar/tests/Makefile.am18
-rw-r--r--kioslave/http/kcookiejar/tests/cookie.test162
-rw-r--r--kioslave/http/kcookiejar/tests/cookie_rfc.test148
-rw-r--r--kioslave/http/kcookiejar/tests/cookie_saving.test430
-rw-r--r--kioslave/http/kcookiejar/tests/cookie_settings.test116
-rw-r--r--kioslave/http/kcookiejar/tests/kcookiejartest.cpp270
20 files changed, 7503 insertions, 0 deletions
diff --git a/kioslave/http/kcookiejar/Makefile.am b/kioslave/http/kcookiejar/Makefile.am
new file mode 100644
index 000000000..933de5e13
--- /dev/null
+++ b/kioslave/http/kcookiejar/Makefile.am
@@ -0,0 +1,31 @@
+# Makefile.am of kdebase/kioslave/http
+
+SUBDIRS=tests
+INCLUDES= $(all_includes)
+
+####### Files
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+kdeinit_LTLIBRARIES = kcookiejar.la
+kde_module_LTLIBRARIES = kded_kcookiejar.la
+
+kcookiejar_la_SOURCES = main.cpp
+METASOURCES = AUTO
+kcookiejar_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kcookiejar_la_LIBADD = $(LIB_KDECORE)
+
+kded_kcookiejar_la_SOURCES = kcookiejar.cpp kcookieserver.cpp \
+ kcookieserver.skel kcookiewin.cpp
+kded_kcookiejar_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kded_kcookiejar_la_LIBADD = $(LIB_KIO) $(LIB_KDED)
+
+kded_DATA = kcookiejar.desktop
+kdeddir = $(kde_servicesdir)/kded
+
+update_DATA = kcookiescfg.upd
+updatedir = $(kde_datadir)/kconf_update
+
+cookie_DATA = domain_info
+cookiedir = $(kde_datadir)/khtml
+
diff --git a/kioslave/http/kcookiejar/domain_info b/kioslave/http/kcookiejar/domain_info
new file mode 100644
index 000000000..94baf8dae
--- /dev/null
+++ b/kioslave/http/kcookiejar/domain_info
@@ -0,0 +1 @@
+twoLevelTLD=name,ai,au,bd,bh,ck,eg,et,fk,il,in,kh,kr,mk,mt,na,np,nz,pg,pk,qa,sa,sb,sg,sv,ua,ug,uk,uy,vn,za,zw
diff --git a/kioslave/http/kcookiejar/kcookiejar.cpp b/kioslave/http/kcookiejar/kcookiejar.cpp
new file mode 100644
index 000000000..5b5f78f6b
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookiejar.cpp
@@ -0,0 +1,1558 @@
+/* This file is part of the KDE File Manager
+
+ Copyright (C) 1998-2000 Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2000,2001 Dawit Alemayehu (adawit@kde.org)
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, and/or sell copies of the
+ Software, and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+//----------------------------------------------------------------------------
+//
+// KDE File Manager -- HTTP Cookies
+// $Id$
+
+//
+// The cookie protocol is a mess. RFC2109 is a joke since nobody seems to
+// use it. Apart from that it is badly written.
+// We try to implement Netscape Cookies and try to behave us according to
+// RFC2109 as much as we can.
+//
+// We assume cookies do not contain any spaces (Netscape spec.)
+// According to RFC2109 this is allowed though.
+//
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#ifdef USE_SOLARIS
+#include <strings.h>
+#endif
+
+#include <stdlib.h>
+
+//#include <netinet/in.h>
+//#include <arpa/inet.h>
+
+#include <qstring.h>
+#include <qstrlist.h>
+#include <qptrlist.h>
+#include <qptrdict.h>
+#include <qfile.h>
+#include <qdir.h>
+#include <qregexp.h>
+
+#include <kurl.h>
+#include <krfcdate.h>
+#include <kconfig.h>
+#include <ksavefile.h>
+#include <kdebug.h>
+
+#include "kcookiejar.h"
+
+
+// BR87227
+// Waba: Should the number of cookies be limited?
+// I am not convinced of the need of such limit
+// Mozilla seems to limit to 20 cookies / domain
+// but it is unclear which policy it uses to expire
+// cookies when it exceeds that amount
+#undef MAX_COOKIE_LIMIT
+
+#define MAX_COOKIES_PER_HOST 25
+#define READ_BUFFER_SIZE 8192
+#define IP_ADDRESS_EXPRESSION "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
+
+// Note with respect to QString::fromLatin1( )
+// Cookies are stored as 8 bit data and passed to kio_http as
+// latin1 regardless of their actual encoding.
+
+// L1 is used to indicate latin1 constants
+#define L1(x) QString::fromLatin1(x)
+
+template class QPtrList<KHttpCookie>;
+template class QPtrDict<KHttpCookieList>;
+
+QString KCookieJar::adviceToStr(KCookieAdvice _advice)
+{
+ switch( _advice )
+ {
+ case KCookieAccept: return L1("Accept");
+ case KCookieReject: return L1("Reject");
+ case KCookieAsk: return L1("Ask");
+ default: return L1("Dunno");
+ }
+}
+
+KCookieAdvice KCookieJar::strToAdvice(const QString &_str)
+{
+ if (_str.isEmpty())
+ return KCookieDunno;
+
+ QCString advice = _str.lower().latin1();
+
+ if (advice == "accept")
+ return KCookieAccept;
+ else if (advice == "reject")
+ return KCookieReject;
+ else if (advice == "ask")
+ return KCookieAsk;
+
+ return KCookieDunno;
+}
+
+// KHttpCookie
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Cookie constructor
+//
+KHttpCookie::KHttpCookie(const QString &_host,
+ const QString &_domain,
+ const QString &_path,
+ const QString &_name,
+ const QString &_value,
+ time_t _expireDate,
+ int _protocolVersion,
+ bool _secure,
+ bool _httpOnly,
+ bool _explicitPath) :
+ mHost(_host),
+ mDomain(_domain),
+ mPath(_path.isEmpty() ? QString::null : _path),
+ mName(_name),
+ mValue(_value),
+ mExpireDate(_expireDate),
+ mProtocolVersion(_protocolVersion),
+ mSecure(_secure),
+ mHttpOnly(_httpOnly),
+ mExplicitPath(_explicitPath)
+{
+}
+
+//
+// Checks if a cookie has been expired
+//
+bool KHttpCookie::isExpired(time_t currentDate)
+{
+ return (mExpireDate != 0) && (mExpireDate < currentDate);
+}
+
+//
+// Returns a string for a HTTP-header
+//
+QString KHttpCookie::cookieStr(bool useDOMFormat)
+{
+ QString result;
+
+ if (useDOMFormat || (mProtocolVersion == 0))
+ {
+ if ( !mName.isEmpty() )
+ result = mName + '=';
+ result += mValue;
+ }
+ else
+ {
+ result = mName + '=' + mValue;
+ if (mExplicitPath)
+ result += L1("; $Path=\"") + mPath + L1("\"");
+ if (!mDomain.isEmpty())
+ result += L1("; $Domain=\"") + mDomain + L1("\"");
+ }
+ return result;
+}
+
+//
+// Returns whether this cookie should be send to this location.
+bool KHttpCookie::match(const QString &fqdn, const QStringList &domains,
+ const QString &path)
+{
+ // Cookie domain match check
+ if (mDomain.isEmpty())
+ {
+ if (fqdn != mHost)
+ return false;
+ }
+ else if (!domains.contains(mDomain))
+ {
+ if (mDomain[0] == '.')
+ return false;
+
+ // Maybe the domain needs an extra dot.
+ QString domain = '.' + mDomain;
+ if ( !domains.contains( domain ) )
+ if ( fqdn != mDomain )
+ return false;
+ }
+
+ // Cookie path match check
+ if (mPath.isEmpty())
+ return true;
+
+ // According to the netscape spec both http://www.acme.com/foobar,
+ // http://www.acme.com/foo.bar and http://www.acme.com/foo/bar
+ // match http://www.acme.com/foo.
+ // We only match http://www.acme.com/foo/bar
+
+ if( path.startsWith(mPath) &&
+ (
+ (path.length() == mPath.length() ) || // Paths are exact match
+ (path[mPath.length()-1] == '/') || // mPath ended with a slash
+ (path[mPath.length()] == '/') // A slash follows.
+ ))
+ return true; // Path of URL starts with cookie-path
+
+ return false;
+}
+
+// KHttpCookieList
+///////////////////////////////////////////////////////////////////////////
+
+int KHttpCookieList::compareItems( void * item1, void * item2)
+{
+ int pathLen1 = ((KHttpCookie *)item1)->path().length();
+ int pathLen2 = ((KHttpCookie *)item2)->path().length();
+ if (pathLen1 > pathLen2)
+ return -1;
+ if (pathLen1 < pathLen2)
+ return 1;
+ return 0;
+}
+
+
+// KCookieJar
+///////////////////////////////////////////////////////////////////////////
+
+//
+// Constructs a new cookie jar
+//
+// One jar should be enough for all cookies.
+//
+KCookieJar::KCookieJar()
+{
+ m_cookieDomains.setAutoDelete( true );
+ m_globalAdvice = KCookieDunno;
+ m_configChanged = false;
+ m_cookiesChanged = false;
+
+ KConfig cfg("khtml/domain_info", true, false, "data");
+ QStringList countries = cfg.readListEntry("twoLevelTLD");
+ for(QStringList::ConstIterator it = countries.begin();
+ it != countries.end(); ++it)
+ {
+ m_twoLevelTLD.replace(*it, (int *) 1);
+ }
+}
+
+//
+// Destructs the cookie jar
+//
+// Poor little cookies, they will all be eaten by the cookie monster!
+//
+KCookieJar::~KCookieJar()
+{
+ // Not much to do here
+}
+
+static void removeDuplicateFromList(KHttpCookieList *list, KHttpCookie *cookiePtr, bool nameMatchOnly=false, bool updateWindowId=false)
+{
+ QString domain1 = cookiePtr->domain();
+ if (domain1.isEmpty())
+ domain1 = cookiePtr->host();
+
+ for ( KHttpCookiePtr cookie=list->first(); cookie != 0; )
+ {
+ QString domain2 = cookie->domain();
+ if (domain2.isEmpty())
+ domain2 = cookie->host();
+
+ if (
+ (cookiePtr->name() == cookie->name()) &&
+ (
+ nameMatchOnly ||
+ ( (domain1 == domain2) && (cookiePtr->path() == cookie->path()) )
+ )
+ )
+ {
+ if (updateWindowId)
+ {
+ for(QValueList<long>::ConstIterator it = cookie->windowIds().begin();
+ it != cookie->windowIds().end(); ++it)
+ {
+ long windowId = *it;
+ if (windowId && (cookiePtr->windowIds().find(windowId) == cookiePtr->windowIds().end()))
+ {
+ cookiePtr->windowIds().append(windowId);
+ }
+ }
+ }
+ KHttpCookiePtr old_cookie = cookie;
+ cookie = list->next();
+ list->removeRef( old_cookie );
+ break;
+ }
+ else
+ {
+ cookie = list->next();
+ }
+ }
+}
+
+
+//
+// Looks for cookies in the cookie jar which are appropriate for _url.
+// Returned is a string containing all appropriate cookies in a format
+// which can be added to a HTTP-header without any additional processing.
+//
+QString KCookieJar::findCookies(const QString &_url, bool useDOMFormat, long windowId, KHttpCookieList *pendingCookies)
+{
+ QString cookieStr;
+ QStringList domains;
+ QString fqdn;
+ QString path;
+ KHttpCookiePtr cookie;
+ KCookieAdvice advice = m_globalAdvice;
+
+ if (!parseURL(_url, fqdn, path))
+ return cookieStr;
+
+ bool secureRequest = (_url.find( L1("https://"), 0, false) == 0 ||
+ _url.find( L1("webdavs://"), 0, false) == 0);
+
+ // kdDebug(7104) << "findCookies: URL= " << _url << ", secure = " << secureRequest << endl;
+
+ extractDomains(fqdn, domains);
+
+ KHttpCookieList allCookies;
+
+ for(QStringList::ConstIterator it = domains.begin();
+ true;
+ ++it)
+ {
+ KHttpCookieList *cookieList;
+ if (it == domains.end())
+ {
+ cookieList = pendingCookies; // Add pending cookies
+ pendingCookies = 0;
+ if (!cookieList)
+ break;
+ }
+ else
+ {
+ QString key = (*it).isNull() ? L1("") : (*it);
+ cookieList = m_cookieDomains[key];
+ if (!cookieList)
+ continue; // No cookies for this domain
+ }
+
+ if (cookieList->getAdvice() != KCookieDunno)
+ advice = cookieList->getAdvice();
+
+ for ( cookie=cookieList->first(); cookie != 0; cookie=cookieList->next() )
+ {
+ // If the we are setup to automatically accept all session cookies and to
+ // treat all cookies as session cookies or the current cookie is a session
+ // cookie, then send the cookie back regardless of either policy.
+ if (advice == KCookieReject &&
+ !(m_autoAcceptSessionCookies &&
+ (m_ignoreCookieExpirationDate || cookie->expireDate() == 0)))
+ continue;
+
+ if (!cookie->match(fqdn, domains, path))
+ continue;
+
+ if( cookie->isSecure() && !secureRequest )
+ continue;
+
+ if( cookie->isHttpOnly() && useDOMFormat )
+ continue;
+
+ // Do not send expired cookies.
+ if ( cookie->isExpired (time(0)) )
+ {
+ // Note there is no need to actually delete the cookie here
+ // since the cookieserver will invoke ::saveCookieJar because
+ // of the state change below. This will then do the job of
+ // deleting the cookie for us.
+ m_cookiesChanged = true;
+ continue;
+ }
+
+ if (windowId && (cookie->windowIds().find(windowId) == cookie->windowIds().end()))
+ {
+ cookie->windowIds().append(windowId);
+ }
+
+ if (it == domains.end()) // Only needed when processing pending cookies
+ removeDuplicateFromList(&allCookies, cookie);
+
+ allCookies.append(cookie);
+ }
+ if (it == domains.end())
+ break; // Finished.
+ }
+
+ int cookieCount = 0;
+
+ int protVersion=0;
+ for ( cookie=allCookies.first(); cookie != 0; cookie=allCookies.next() )
+ {
+ if (cookie->protocolVersion() > protVersion)
+ protVersion = cookie->protocolVersion();
+ }
+
+ for ( cookie=allCookies.first(); cookie != 0; cookie=allCookies.next() )
+ {
+ if (useDOMFormat)
+ {
+ if (cookieCount > 0)
+ cookieStr += L1("; ");
+ cookieStr += cookie->cookieStr(true);
+ }
+ else
+ {
+ if (cookieCount == 0)
+ {
+ cookieStr += L1("Cookie: ");
+ if (protVersion > 0)
+ {
+ QString version;
+ version.sprintf("$Version=%d; ", protVersion); // Without quotes
+ cookieStr += version;
+ }
+ }
+ else
+ {
+ cookieStr += L1("; ");
+ }
+ cookieStr += cookie->cookieStr(false);
+ }
+ cookieCount++;
+ }
+
+ return cookieStr;
+}
+
+//
+// This function parses a string like 'my_name="my_value";' and returns
+// 'my_name' in Name and 'my_value' in Value.
+//
+// A pointer to the end of the parsed part is returned.
+// This pointer points either to:
+// '\0' - The end of the string has reached.
+// ';' - Another my_name="my_value" pair follows
+// ',' - Another cookie follows
+// '\n' - Another header follows
+static const char * parseNameValue(const char *header,
+ QString &Name,
+ QString &Value,
+ bool keepQuotes=false,
+ bool rfcQuotes=false)
+{
+ const char *s = header;
+ // Parse 'my_name' part
+ for(; (*s != '='); s++)
+ {
+ if ((*s=='\0') || (*s==';') || (*s=='\n'))
+ {
+ // No '=' sign -> use string as the value, name is empty
+ // (behavior found in Mozilla and IE)
+ Name = "";
+ Value = QString::fromLatin1(header);
+ Value.truncate( s - header );
+ Value = Value.stripWhiteSpace();
+ return (s);
+ }
+ }
+
+ Name = header;
+ Name.truncate( s - header );
+ Name = Name.stripWhiteSpace();
+
+ // *s == '='
+ s++;
+
+ // Skip any whitespace
+ for(; (*s == ' ') || (*s == '\t'); s++)
+ {
+ if ((*s=='\0') || (*s==';') || (*s=='\n'))
+ {
+ // End of Name
+ Value = "";
+ return (s);
+ }
+ }
+
+ if ((rfcQuotes || !keepQuotes) && (*s == '\"'))
+ {
+ // Parse '"my_value"' part (quoted value)
+ if (keepQuotes)
+ header = s++;
+ else
+ header = ++s; // skip "
+ for(;(*s != '\"');s++)
+ {
+ if ((*s=='\0') || (*s=='\n'))
+ {
+ // End of Name
+ Value = QString::fromLatin1(header);
+ Value.truncate(s - header);
+ return (s);
+ }
+ }
+ Value = QString::fromLatin1(header);
+ // *s == '\"';
+ if (keepQuotes)
+ Value.truncate( ++s - header );
+ else
+ Value.truncate( s++ - header );
+
+ // Skip any remaining garbage
+ for(;; s++)
+ {
+ if ((*s=='\0') || (*s==';') || (*s=='\n'))
+ break;
+ }
+ }
+ else
+ {
+ // Parse 'my_value' part (unquoted value)
+ header = s;
+ while ((*s != '\0') && (*s != ';') && (*s != '\n'))
+ s++;
+ // End of Name
+ Value = QString::fromLatin1(header);
+ Value.truncate( s - header );
+ Value = Value.stripWhiteSpace();
+ }
+ return (s);
+
+}
+
+void KCookieJar::stripDomain(const QString &_fqdn, QString &_domain)
+{
+ QStringList domains;
+ extractDomains(_fqdn, domains);
+ if (domains.count() > 3)
+ _domain = domains[3];
+ else
+ _domain = domains[0];
+}
+
+QString KCookieJar::stripDomain( KHttpCookiePtr cookiePtr)
+{
+ QString domain; // We file the cookie under this domain.
+ if (cookiePtr->domain().isEmpty())
+ stripDomain( cookiePtr->host(), domain);
+ else
+ stripDomain (cookiePtr->domain(), domain);
+ return domain;
+}
+
+bool KCookieJar::parseURL(const QString &_url,
+ QString &_fqdn,
+ QString &_path)
+{
+ KURL kurl(_url);
+ if (!kurl.isValid())
+ return false;
+
+ _fqdn = kurl.host().lower();
+ if (kurl.port())
+ {
+ if (((kurl.protocol() == L1("http")) && (kurl.port() != 80)) ||
+ ((kurl.protocol() == L1("https")) && (kurl.port() != 443)))
+ {
+ _fqdn = L1("%1:%2").arg(kurl.port()).arg(_fqdn);
+ }
+ }
+
+ // Cookie spoofing protection. Since there is no way a path separator
+ // or escape encoded character is allowed in the hostname according
+ // to RFC 2396, reject attempts to include such things there!
+ if(_fqdn.find('/') > -1 || _fqdn.find('%') > -1)
+ {
+ return false; // deny everything!!
+ }
+
+ _path = kurl.path();
+ if (_path.isEmpty())
+ _path = L1("/");
+
+ QRegExp exp(L1("[\\\\/]\\.\\.[\\\\/]"));
+ // Weird path, cookie stealing attempt?
+ if (exp.search(_path) != -1)
+ return false; // Deny everything!!
+
+ return true;
+}
+
+void KCookieJar::extractDomains(const QString &_fqdn,
+ QStringList &_domains)
+{
+ // Return numeric IPv6 addresses as is...
+ if (_fqdn[0] == '[')
+ {
+ _domains.append( _fqdn );
+ return;
+ }
+ // Return numeric IPv4 addresses as is...
+ if ((_fqdn[0] >= '0') && (_fqdn[0] <= '9'))
+ {
+ if (_fqdn.find(QRegExp(IP_ADDRESS_EXPRESSION)) > -1)
+ {
+ _domains.append( _fqdn );
+ return;
+ }
+ }
+
+ QStringList partList = QStringList::split('.', _fqdn, false);
+
+ if (partList.count())
+ partList.remove(partList.begin()); // Remove hostname
+
+ while(partList.count())
+ {
+
+ if (partList.count() == 1)
+ break; // We only have a TLD left.
+
+ if ((partList.count() == 2) && (m_twoLevelTLD[partList[1].lower()]))
+ {
+ // This domain uses two-level TLDs in the form xxxx.yy
+ break;
+ }
+
+ if ((partList.count() == 2) && (partList[1].length() == 2))
+ {
+ // If this is a TLD, we should stop. (e.g. co.uk)
+ // We assume this is a TLD if it ends with .xx.yy or .x.yy
+ if (partList[0].length() <= 2)
+ break; // This is a TLD.
+
+ // Catch some TLDs that we miss with the previous check
+ // e.g. com.au, org.uk, mil.co
+ QCString t = partList[0].lower().utf8();
+ if ((t == "com") || (t == "net") || (t == "org") || (t == "gov") || (t == "edu") || (t == "mil") || (t == "int"))
+ break;
+ }
+
+ QString domain = partList.join(L1("."));
+ _domains.append(domain);
+ _domains.append('.' + domain);
+ partList.remove(partList.begin()); // Remove part
+ }
+
+ // Always add the FQDN at the start of the list for
+ // hostname == cookie-domainname checks!
+ _domains.prepend( '.' + _fqdn );
+ _domains.prepend( _fqdn );
+}
+
+
+/*
+ Changes dates in from the following format
+
+ Wed Sep 12 07:00:00 2007 GMT
+ to
+ Wed Sep 12 2007 07:00:00 GMT
+
+ to allow KRFCDate::parseDate to properly parse expiration date formats
+ used in cookies by some servers such as amazon.com. See BR# 145244.
+*/
+static QString fixupDateTime(const QString& dt)
+{
+ const int index = dt.find(QRegExp("[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}"));
+
+ if (index > -1)
+ {
+ QStringList dateStrList = QStringList::split(' ', dt.mid(index));
+ if (dateStrList.count() > 1)
+ {
+ QString date = dateStrList[0];
+ dateStrList[0] = dateStrList[1];
+ dateStrList[1] = date;
+ date = dt;
+ return date.replace(index, date.length(), dateStrList.join(" "));
+ }
+ }
+
+ return dt;
+}
+
+//
+// This function parses cookie_headers and returns a linked list of
+// KHttpCookie objects for all cookies found in cookie_headers.
+// If no cookies could be found 0 is returned.
+//
+// cookie_headers should be a concatenation of all lines of a HTTP-header
+// which start with "Set-Cookie". The lines should be separated by '\n's.
+//
+KHttpCookieList KCookieJar::makeCookies(const QString &_url,
+ const QCString &cookie_headers,
+ long windowId)
+{
+ KHttpCookieList cookieList;
+ KHttpCookieList cookieList2;
+ KHttpCookiePtr lastCookie = 0;
+ const char *cookieStr = cookie_headers.data();
+ QString Name;
+ QString Value;
+ QString fqdn;
+ QString path;
+ bool crossDomain = false;
+
+ if (!parseURL(_url, fqdn, path))
+ {
+ // Error parsing _url
+ return KHttpCookieList();
+ }
+ QString defaultPath;
+ int i = path.findRev('/');
+ if (i > 0)
+ defaultPath = path.left(i);
+
+ // The hard stuff :)
+ for(;;)
+ {
+ // check for "Set-Cookie"
+ if (strncmp(cookieStr, "Cross-Domain\n", 13) == 0)
+ {
+ cookieStr += 13;
+ crossDomain = true;
+ }
+ else if (strncasecmp(cookieStr, "Set-Cookie:", 11) == 0)
+ {
+ cookieStr = parseNameValue(cookieStr+11, Name, Value, true);
+
+ // Host = FQDN
+ // Default domain = ""
+ // Default path according to rfc2109
+
+ KHttpCookie *cookie = new KHttpCookie(fqdn, L1(""), defaultPath, Name, Value);
+ if (windowId)
+ cookie->mWindowIds.append(windowId);
+ cookie->mCrossDomain = crossDomain;
+
+ // Insert cookie in chain
+ cookieList.append(cookie);
+ lastCookie = cookie;
+ }
+ else if (strncasecmp(cookieStr, "Set-Cookie2:", 12) == 0)
+ {
+ // Attempt to follow rfc2965
+ cookieStr = parseNameValue(cookieStr+12, Name, Value, true, true);
+
+ // Host = FQDN
+ // Default domain = ""
+ // Default path according to rfc2965
+
+ KHttpCookie *cookie = new KHttpCookie(fqdn, L1(""), defaultPath, Name, Value);
+ if (windowId)
+ cookie->mWindowIds.append(windowId);
+ cookie->mCrossDomain = crossDomain;
+
+ // Insert cookie in chain
+ cookieList2.append(cookie);
+ lastCookie = cookie;
+ }
+ else
+ {
+ // This is not the start of a cookie header, skip till next line.
+ while (*cookieStr && *cookieStr != '\n')
+ cookieStr++;
+
+ if (*cookieStr == '\n')
+ cookieStr++;
+
+ if (!*cookieStr)
+ break; // End of cookie_headers
+ else
+ continue; // end of this header, continue with next.
+ }
+
+ while ((*cookieStr == ';') || (*cookieStr == ' '))
+ {
+ cookieStr++;
+
+ // Name-Value pair follows
+ cookieStr = parseNameValue(cookieStr, Name, Value);
+
+ QCString cName = Name.lower().latin1();
+ if (cName == "domain")
+ {
+ QString dom = Value.lower();
+ // RFC2965 3.2.2: If an explicitly specified value does not
+ // start with a dot, the user agent supplies a leading dot
+ if(dom.length() && dom[0] != '.')
+ dom.prepend(".");
+ // remove a trailing dot
+ if(dom.length() > 2 && dom[dom.length()-1] == '.')
+ dom = dom.left(dom.length()-1);
+
+ if(dom.contains('.') > 1 || dom == ".local")
+ lastCookie->mDomain = dom;
+ }
+ else if (cName == "max-age")
+ {
+ int max_age = Value.toInt();
+ if (max_age == 0)
+ lastCookie->mExpireDate = 1;
+ else
+ lastCookie->mExpireDate = time(0)+max_age;
+ }
+ else if (cName == "expires")
+ {
+ // Parse brain-dead netscape cookie-format
+ lastCookie->mExpireDate = KRFCDate::parseDate(Value);
+
+ // Workaround for servers that send the expiration date in
+ // 'Wed Sep 12 07:00:00 2007 GMT' format. See BR# 145244.
+ if (lastCookie->mExpireDate == 0)
+ lastCookie->mExpireDate = KRFCDate::parseDate(fixupDateTime(Value));
+ }
+ else if (cName == "path")
+ {
+ if (Value.isEmpty())
+ lastCookie->mPath = QString::null; // Catch "" <> QString::null
+ else
+ lastCookie->mPath = KURL::decode_string(Value);
+ lastCookie->mExplicitPath = true;
+ }
+ else if (cName == "version")
+ {
+ lastCookie->mProtocolVersion = Value.toInt();
+ }
+ else if ((cName == "secure") ||
+ (cName.isEmpty() && Value.lower() == L1("secure")))
+ {
+ lastCookie->mSecure = true;
+ }
+ else if ((cName == "httponly") ||
+ (cName.isEmpty() && Value.lower() == L1("httponly")))
+ {
+ lastCookie->mHttpOnly = true;
+ }
+ }
+
+ if (*cookieStr == '\0')
+ break; // End of header
+
+ // Skip ';' or '\n'
+ cookieStr++;
+ }
+
+ // RFC2965 cookies come last so that they override netscape cookies.
+ while( !cookieList2.isEmpty() && (lastCookie = cookieList2.take(0)) )
+ {
+ removeDuplicateFromList(&cookieList, lastCookie, true);
+ cookieList.append(lastCookie);
+ }
+
+ return cookieList;
+}
+
+/**
+* Parses cookie_domstr and returns a linked list of KHttpCookie objects.
+* cookie_domstr should be a semicolon-delimited list of "name=value"
+* pairs. Any whitespace before "name" or around '=' is discarded.
+* If no cookies are found, 0 is returned.
+*/
+KHttpCookieList KCookieJar::makeDOMCookies(const QString &_url,
+ const QCString &cookie_domstring,
+ long windowId)
+{
+ // A lot copied from above
+ KHttpCookieList cookieList;
+ KHttpCookiePtr lastCookie = 0;
+
+ const char *cookieStr = cookie_domstring.data();
+ QString Name;
+ QString Value;
+ QString fqdn;
+ QString path;
+
+ if (!parseURL(_url, fqdn, path))
+ {
+ // Error parsing _url
+ return KHttpCookieList();
+ }
+
+ // This time it's easy
+ while(*cookieStr)
+ {
+ cookieStr = parseNameValue(cookieStr, Name, Value);
+
+ // Host = FQDN
+ // Default domain = ""
+ // Default path = ""
+ KHttpCookie *cookie = new KHttpCookie(fqdn, QString::null, QString::null,
+ Name, Value );
+ if (windowId)
+ cookie->mWindowIds.append(windowId);
+
+ cookieList.append(cookie);
+ lastCookie = cookie;
+
+ if (*cookieStr != '\0')
+ cookieStr++; // Skip ';' or '\n'
+ }
+
+ return cookieList;
+}
+
+#ifdef MAX_COOKIE_LIMIT
+static void makeRoom(KHttpCookieList *cookieList, KHttpCookiePtr &cookiePtr)
+{
+ // Too much cookies: throw one away, try to be somewhat clever
+ KHttpCookiePtr lastCookie = 0;
+ for(KHttpCookiePtr cookie = cookieList->first(); cookie; cookie = cookieList->next())
+ {
+ if (cookieList->compareItems(cookie, cookiePtr) < 0)
+ break;
+ lastCookie = cookie;
+ }
+ if (!lastCookie)
+ lastCookie = cookieList->first();
+ cookieList->removeRef(lastCookie);
+}
+#endif
+
+//
+// This function hands a KHttpCookie object over to the cookie jar.
+//
+// On return cookiePtr is set to 0.
+//
+void KCookieJar::addCookie(KHttpCookiePtr &cookiePtr)
+{
+ QStringList domains;
+ KHttpCookieList *cookieList = 0L;
+
+ // We always need to do this to make sure that the
+ // that cookies of type hostname == cookie-domainname
+ // are properly removed and/or updated as necessary!
+ extractDomains( cookiePtr->host(), domains );
+ for ( QStringList::ConstIterator it = domains.begin();
+ (it != domains.end() && !cookieList);
+ ++it )
+ {
+ QString key = (*it).isNull() ? L1("") : (*it);
+ KHttpCookieList *list= m_cookieDomains[key];
+ if ( !list ) continue;
+
+ removeDuplicateFromList(list, cookiePtr, false, true);
+ }
+
+ QString domain = stripDomain( cookiePtr );
+ QString key = domain.isNull() ? L1("") : domain;
+ cookieList = m_cookieDomains[ key ];
+ if (!cookieList)
+ {
+ // Make a new cookie list
+ cookieList = new KHttpCookieList();
+ cookieList->setAutoDelete(true);
+
+ // All cookies whose domain is not already
+ // known to us should be added with KCookieDunno.
+ // KCookieDunno means that we use the global policy.
+ cookieList->setAdvice( KCookieDunno );
+
+ m_cookieDomains.insert( domain, cookieList);
+
+ // Update the list of domains
+ m_domainList.append(domain);
+ }
+
+ // Add the cookie to the cookie list
+ // The cookie list is sorted 'longest path first'
+ if (!cookiePtr->isExpired(time(0)))
+ {
+#ifdef MAX_COOKIE_LIMIT
+ if (cookieList->count() >= MAX_COOKIES_PER_HOST)
+ makeRoom(cookieList, cookiePtr); // Delete a cookie
+#endif
+ cookieList->inSort( cookiePtr );
+ m_cookiesChanged = true;
+ }
+ else
+ {
+ delete cookiePtr;
+ }
+ cookiePtr = 0;
+}
+
+//
+// This function advices whether a single KHttpCookie object should
+// be added to the cookie jar.
+//
+KCookieAdvice KCookieJar::cookieAdvice(KHttpCookiePtr cookiePtr)
+{
+ if (m_rejectCrossDomainCookies && cookiePtr->isCrossDomain())
+ return KCookieReject;
+
+ QStringList domains;
+
+ extractDomains(cookiePtr->host(), domains);
+
+ // If the cookie specifies a domain, check whether it is valid. Otherwise,
+ // accept the cookie anyways but remove the domain="" value to prevent
+ // cross-site cookie injection.
+ if (!cookiePtr->domain().isEmpty())
+ {
+ if (!domains.contains(cookiePtr->domain()) &&
+ !cookiePtr->domain().endsWith("."+cookiePtr->host()))
+ cookiePtr->fixDomain(QString::null);
+ }
+
+ if (m_autoAcceptSessionCookies && (cookiePtr->expireDate() == 0 ||
+ m_ignoreCookieExpirationDate))
+ return KCookieAccept;
+
+ KCookieAdvice advice = KCookieDunno;
+ bool isFQDN = true; // First is FQDN
+ QStringList::Iterator it = domains.begin(); // Start with FQDN which first in the list.
+ while( (advice == KCookieDunno) && (it != domains.end()))
+ {
+ QString domain = *it;
+ // Check if a policy for the FQDN/domain is set.
+ if ( domain[0] == '.' || isFQDN )
+ {
+ isFQDN = false;
+ KHttpCookieList *cookieList = m_cookieDomains[domain];
+ if (cookieList)
+ advice = cookieList->getAdvice();
+ }
+ domains.remove(it);
+ it = domains.begin(); // Continue from begin of remaining list
+ }
+
+ if (advice == KCookieDunno)
+ advice = m_globalAdvice;
+
+ return advice;
+}
+
+//
+// This function gets the advice for all cookies originating from
+// _domain.
+//
+KCookieAdvice KCookieJar::getDomainAdvice(const QString &_domain)
+{
+ KHttpCookieList *cookieList = m_cookieDomains[_domain];
+ KCookieAdvice advice;
+
+ if (cookieList)
+ {
+ advice = cookieList->getAdvice();
+ }
+ else
+ {
+ advice = KCookieDunno;
+ }
+
+ return advice;
+}
+
+//
+// This function sets the advice for all cookies originating from
+// _domain.
+//
+void KCookieJar::setDomainAdvice(const QString &_domain, KCookieAdvice _advice)
+{
+ QString domain(_domain);
+ KHttpCookieList *cookieList = m_cookieDomains[domain];
+
+ if (cookieList)
+ {
+ if (cookieList->getAdvice() != _advice)
+ {
+ m_configChanged = true;
+ // domain is already known
+ cookieList->setAdvice( _advice);
+ }
+
+ if ((cookieList->isEmpty()) &&
+ (_advice == KCookieDunno))
+ {
+ // This deletes cookieList!
+ m_cookieDomains.remove(domain);
+ m_domainList.remove(domain);
+ }
+ }
+ else
+ {
+ // domain is not yet known
+ if (_advice != KCookieDunno)
+ {
+ // We should create a domain entry
+ m_configChanged = true;
+ // Make a new cookie list
+ cookieList = new KHttpCookieList();
+ cookieList->setAutoDelete(true);
+ cookieList->setAdvice( _advice);
+ m_cookieDomains.insert( domain, cookieList);
+ // Update the list of domains
+ m_domainList.append( domain);
+ }
+ }
+}
+
+//
+// This function sets the advice for all cookies originating from
+// the same domain as _cookie
+//
+void KCookieJar::setDomainAdvice(KHttpCookiePtr cookiePtr, KCookieAdvice _advice)
+{
+ QString domain;
+ stripDomain(cookiePtr->host(), domain); // We file the cookie under this domain.
+
+ setDomainAdvice(domain, _advice);
+}
+
+//
+// This function sets the global advice for cookies
+//
+void KCookieJar::setGlobalAdvice(KCookieAdvice _advice)
+{
+ if (m_globalAdvice != _advice)
+ m_configChanged = true;
+ m_globalAdvice = _advice;
+}
+
+//
+// Get a list of all domains known to the cookie jar.
+//
+const QStringList& KCookieJar::getDomainList()
+{
+ return m_domainList;
+}
+
+//
+// Get a list of all cookies in the cookie jar originating from _domain.
+//
+const KHttpCookieList *KCookieJar::getCookieList(const QString & _domain,
+ const QString & _fqdn )
+{
+ QString domain;
+
+ if (_domain.isEmpty())
+ stripDomain( _fqdn, domain );
+ else
+ domain = _domain;
+
+ return m_cookieDomains[domain];
+}
+
+//
+// Eat a cookie out of the jar.
+// cookiePtr should be one of the cookies returned by getCookieList()
+//
+void KCookieJar::eatCookie(KHttpCookiePtr cookiePtr)
+{
+ QString domain = stripDomain(cookiePtr); // We file the cookie under this domain.
+ KHttpCookieList *cookieList = m_cookieDomains[domain];
+
+ if (cookieList)
+ {
+ // This deletes cookiePtr!
+ if (cookieList->removeRef( cookiePtr ))
+ m_cookiesChanged = true;
+
+ if ((cookieList->isEmpty()) &&
+ (cookieList->getAdvice() == KCookieDunno))
+ {
+ // This deletes cookieList!
+ m_cookieDomains.remove(domain);
+
+ m_domainList.remove(domain);
+ }
+ }
+}
+
+void KCookieJar::eatCookiesForDomain(const QString &domain)
+{
+ KHttpCookieList *cookieList = m_cookieDomains[domain];
+ if (!cookieList || cookieList->isEmpty()) return;
+
+ cookieList->clear();
+ if (cookieList->getAdvice() == KCookieDunno)
+ {
+ // This deletes cookieList!
+ m_cookieDomains.remove(domain);
+ m_domainList.remove(domain);
+ }
+ m_cookiesChanged = true;
+}
+
+void KCookieJar::eatSessionCookies( long windowId )
+{
+ if (!windowId)
+ return;
+
+ QStringList::Iterator it=m_domainList.begin();
+ for ( ; it != m_domainList.end(); ++it )
+ eatSessionCookies( *it, windowId, false );
+}
+
+void KCookieJar::eatAllCookies()
+{
+ for ( QStringList::Iterator it=m_domainList.begin();
+ it != m_domainList.end();)
+ {
+ QString domain = *it++;
+ // This might remove domain from domainList!
+ eatCookiesForDomain(domain);
+ }
+}
+
+void KCookieJar::eatSessionCookies( const QString& fqdn, long windowId,
+ bool isFQDN )
+{
+ KHttpCookieList* cookieList;
+ if ( !isFQDN )
+ cookieList = m_cookieDomains[fqdn];
+ else
+ {
+ QString domain;
+ stripDomain( fqdn, domain );
+ cookieList = m_cookieDomains[domain];
+ }
+
+ if ( cookieList )
+ {
+ KHttpCookiePtr cookie=cookieList->first();
+ for (; cookie != 0;)
+ {
+ if ((cookie->expireDate() != 0) && !m_ignoreCookieExpirationDate)
+ {
+ cookie = cookieList->next();
+ continue;
+ }
+
+ QValueList<long> &ids = cookie->windowIds();
+ if (!ids.remove(windowId) || !ids.isEmpty())
+ {
+ cookie = cookieList->next();
+ continue;
+ }
+ KHttpCookiePtr old_cookie = cookie;
+ cookie = cookieList->next();
+ cookieList->removeRef( old_cookie );
+ }
+ }
+}
+
+//
+// Saves all cookies to the file '_filename'.
+// On succes 'true' is returned.
+// On failure 'false' is returned.
+bool KCookieJar::saveCookies(const QString &_filename)
+{
+ KSaveFile saveFile(_filename, 0600);
+
+ if (saveFile.status() != 0)
+ return false;
+
+ FILE *fStream = saveFile.fstream();
+
+ time_t curTime = time(0);
+
+ fprintf(fStream, "# KDE Cookie File v2\n#\n");
+
+ fprintf(fStream, "%-20s %-20s %-12s %-10s %-4s %-20s %-4s %s\n",
+ "# Host", "Domain", "Path", "Exp.date", "Prot",
+ "Name", "Sec", "Value");
+
+ for ( QStringList::Iterator it=m_domainList.begin(); it != m_domainList.end();
+ it++ )
+ {
+ const QString &domain = *it;
+ bool domainPrinted = false;
+
+ KHttpCookieList *cookieList = m_cookieDomains[domain];
+ KHttpCookiePtr cookie=cookieList->last();
+
+ for (; cookie != 0;)
+ {
+ if (cookie->isExpired(curTime))
+ {
+ // Delete expired cookies
+ KHttpCookiePtr old_cookie = cookie;
+ cookie = cookieList->prev();
+ cookieList->removeRef( old_cookie );
+ }
+ else if (cookie->expireDate() != 0 && !m_ignoreCookieExpirationDate)
+ {
+ if (!domainPrinted)
+ {
+ domainPrinted = true;
+ fprintf(fStream, "[%s]\n", domain.local8Bit().data());
+ }
+ // Store persistent cookies
+ QString path = L1("\"");
+ path += cookie->path();
+ path += '"';
+ QString domain = L1("\"");
+ domain += cookie->domain();
+ domain += '"';
+ fprintf(fStream, "%-20s %-20s %-12s %10lu %3d %-20s %-4i %s\n",
+ cookie->host().latin1(), domain.latin1(),
+ path.latin1(), (unsigned long) cookie->expireDate(),
+ cookie->protocolVersion(),
+ cookie->name().isEmpty() ? cookie->value().latin1() : cookie->name().latin1(),
+ (cookie->isSecure() ? 1 : 0) + (cookie->isHttpOnly() ? 2 : 0) +
+ (cookie->hasExplicitPath() ? 4 : 0) + (cookie->name().isEmpty() ? 8 : 0),
+ cookie->value().latin1());
+ cookie = cookieList->prev();
+ }
+ else
+ {
+ // Skip session-only cookies
+ cookie = cookieList->prev();
+ }
+ }
+ }
+
+ return saveFile.close();
+}
+
+typedef char *charPtr;
+
+static const char *parseField(charPtr &buffer, bool keepQuotes=false)
+{
+ char *result;
+ if (!keepQuotes && (*buffer == '\"'))
+ {
+ // Find terminating "
+ buffer++;
+ result = buffer;
+ while((*buffer != '\"') && (*buffer))
+ buffer++;
+ }
+ else
+ {
+ // Find first white space
+ result = buffer;
+ while((*buffer != ' ') && (*buffer != '\t') && (*buffer != '\n') && (*buffer))
+ buffer++;
+ }
+
+ if (!*buffer)
+ return result; //
+ *buffer++ = '\0';
+
+ // Skip white-space
+ while((*buffer == ' ') || (*buffer == '\t') || (*buffer == '\n'))
+ buffer++;
+
+ return result;
+}
+
+
+//
+// Reloads all cookies from the file '_filename'.
+// On succes 'true' is returned.
+// On failure 'false' is returned.
+bool KCookieJar::loadCookies(const QString &_filename)
+{
+ FILE *fStream = fopen( QFile::encodeName(_filename), "r");
+ if (fStream == 0)
+ {
+ return false;
+ }
+
+ time_t curTime = time(0);
+
+ char *buffer = new char[READ_BUFFER_SIZE];
+
+ bool err = false;
+ err = (fgets(buffer, READ_BUFFER_SIZE, fStream) == 0);
+
+ int version = 1;
+ if (!err)
+ {
+ if (strcmp(buffer, "# KDE Cookie File\n") == 0)
+ {
+ // version 1
+ }
+ else if (sscanf(buffer, "# KDE Cookie File v%d\n", &version) != 1)
+ {
+ err = true;
+ }
+ }
+
+ if (!err)
+ {
+ while(fgets(buffer, READ_BUFFER_SIZE, fStream) != 0)
+ {
+ char *line = buffer;
+ // Skip lines which begin with '#' or '['
+ if ((line[0] == '#') || (line[0] == '['))
+ continue;
+
+ const char *host( parseField(line) );
+ const char *domain( parseField(line) );
+ const char *path( parseField(line) );
+ const char *expStr( parseField(line) );
+ if (!expStr) continue;
+ int expDate = (time_t) strtoul(expStr, 0, 10);
+ const char *verStr( parseField(line) );
+ if (!verStr) continue;
+ int protVer = (time_t) strtoul(verStr, 0, 10);
+ const char *name( parseField(line) );
+ bool keepQuotes = false;
+ bool secure = false;
+ bool httpOnly = false;
+ bool explicitPath = false;
+ const char *value = 0;
+ if ((version == 2) || (protVer >= 200))
+ {
+ if (protVer >= 200)
+ protVer -= 200;
+ int i = atoi( parseField(line) );
+ secure = i & 1;
+ httpOnly = i & 2;
+ explicitPath = i & 4;
+ if (i & 8)
+ name = "";
+ line[strlen(line)-1] = '\0'; // Strip LF.
+ value = line;
+ }
+ else
+ {
+ if (protVer >= 100)
+ {
+ protVer -= 100;
+ keepQuotes = true;
+ }
+ value = parseField(line, keepQuotes);
+ secure = atoi( parseField(line) );
+ }
+
+ // Parse error
+ if (!value) continue;
+
+ // Expired or parse error
+ if ((expDate == 0) || (expDate < curTime))
+ continue;
+
+ KHttpCookie *cookie = new KHttpCookie(QString::fromLatin1(host),
+ QString::fromLatin1(domain),
+ QString::fromLatin1(path),
+ QString::fromLatin1(name),
+ QString::fromLatin1(value),
+ expDate, protVer,
+ secure, httpOnly, explicitPath);
+ addCookie(cookie);
+ }
+ }
+ delete [] buffer;
+ m_cookiesChanged = false;
+
+ fclose( fStream);
+ return err;
+}
+
+//
+// Save the cookie configuration
+//
+
+void KCookieJar::saveConfig(KConfig *_config)
+{
+ if (!m_configChanged)
+ return;
+
+ _config->setGroup("Cookie Dialog");
+ _config->writeEntry("PreferredPolicy", m_preferredPolicy);
+ _config->writeEntry("ShowCookieDetails", m_showCookieDetails );
+ _config->setGroup("Cookie Policy");
+ _config->writeEntry("CookieGlobalAdvice", adviceToStr( m_globalAdvice));
+
+ QStringList domainSettings;
+ for ( QStringList::Iterator it=m_domainList.begin();
+ it != m_domainList.end();
+ it++ )
+ {
+ const QString &domain = *it;
+ KCookieAdvice advice = getDomainAdvice( domain);
+ if (advice != KCookieDunno)
+ {
+ QString value(domain);
+ value += ':';
+ value += adviceToStr(advice);
+ domainSettings.append(value);
+ }
+ }
+ _config->writeEntry("CookieDomainAdvice", domainSettings);
+ _config->sync();
+ m_configChanged = false;
+}
+
+
+//
+// Load the cookie configuration
+//
+
+void KCookieJar::loadConfig(KConfig *_config, bool reparse )
+{
+ if ( reparse )
+ _config->reparseConfiguration();
+
+ _config->setGroup("Cookie Dialog");
+ m_showCookieDetails = _config->readBoolEntry( "ShowCookieDetails" );
+ m_preferredPolicy = _config->readNumEntry( "PreferredPolicy", 0 );
+
+ _config->setGroup("Cookie Policy");
+ QStringList domainSettings = _config->readListEntry("CookieDomainAdvice");
+ m_rejectCrossDomainCookies = _config->readBoolEntry( "RejectCrossDomainCookies", true );
+ m_autoAcceptSessionCookies = _config->readBoolEntry( "AcceptSessionCookies", true );
+ m_ignoreCookieExpirationDate = _config->readBoolEntry( "IgnoreExpirationDate", false );
+ QString value = _config->readEntry("CookieGlobalAdvice", L1("Ask"));
+ m_globalAdvice = strToAdvice(value);
+
+ // Reset current domain settings first.
+ for ( QStringList::Iterator it=m_domainList.begin(); it != m_domainList.end(); )
+ {
+ // Make sure to update iterator before calling setDomainAdvice()
+ // setDomainAdvice() might delete the domain from domainList.
+ QString domain = *it++;
+ setDomainAdvice(domain, KCookieDunno);
+ }
+
+ // Now apply the domain settings read from config file...
+ for ( QStringList::Iterator it=domainSettings.begin();
+ it != domainSettings.end(); )
+ {
+ const QString &value = *it++;
+
+ int sepPos = value.findRev(':');
+
+ if (sepPos <= 0)
+ continue;
+
+ QString domain(value.left(sepPos));
+ KCookieAdvice advice = strToAdvice( value.mid(sepPos + 1) );
+ setDomainAdvice(domain, advice);
+ }
+}
diff --git a/kioslave/http/kcookiejar/kcookiejar.desktop b/kioslave/http/kcookiejar/kcookiejar.desktop
new file mode 100644
index 000000000..54421225a
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookiejar.desktop
@@ -0,0 +1,157 @@
+[Desktop Entry]
+Type=Service
+Name=KDED Cookie Jar Module
+Name[af]=Kded Koekie Houer Module
+Name[ar]=وحدة Jar لكعكة KDED
+Name[az]=KDED Kökə Jar Modulu
+Name[be]=Модуль "печыва" KDED
+Name[bg]=Модул KDED Cookie Jar
+Name[bn]=KDED কুকি জার মডিউল
+Name[bs]=KDED modul "Tegla sa keksima"
+Name[ca]=Mòdul Jar de cookies per a KDED
+Name[cs]=KDED modul pro cookies
+Name[csb]=Sprôwianié kùszkama
+Name[cy]=Modiwl Jar Cwci KDED
+Name[da]=KDED-cookie-jar-modul
+Name[de]=Cookie-Verwaltung
+Name[el]=Άρθρωμα Cookie Jar του KDED
+Name[eo]=KDED-kuketotraktila modulo
+Name[es]=Módulo Jar de cookies de KDED
+Name[et]=KDED Cookie Jar moodul
+Name[eu]=KDED Cookie Jar modulua
+Name[fa]=پیمانۀ ظرف کوکی KDED
+Name[fi]=KDED-evästemoduuli
+Name[fr]=Module Jar de cookie KDED
+Name[fy]=KDED-module foar it bewarjen fan Koekjes
+Name[gl]=Módulo Jar de cookies de KDED
+Name[he]=מודול צנצנת העוגיות של KDED
+Name[hi]=KDED कुकी जार मॉड्यूल
+Name[hr]=KDED modul za čuvanje kolačića
+Name[hu]=KDED cookie-modul
+Name[id]=Modul Penyimpanan Cookies KDED
+Name[is]=KDED smákökukrukka
+Name[it]=Modulo Jar dei cookie per KDED
+Name[ja]=KDED クッキー Jar モジュール
+Name[ka]=KDED-ის ბმულების Jar მოდული
+Name[kk]=KDE cookie модулі
+Name[km]=ម៉ូឌុល Jar នៃ​ខូគី KDED
+Name[ko]=KDED 쿠키 JAR 모듈
+Name[lb]=KDED-Modul fir d'Verwaltung vun de Cookien
+Name[lt]=KDED slapukų rinkinio modulis
+Name[lv]=KDED Cepumu Jar modulis
+Name[mk]=KDED модул Тегла со колачиња
+Name[ms]=Modul Balang Cecikut KDED
+Name[mt]=Modulu tal-"cookies" KDED
+Name[nb]=KDEDs modul for informasjonskapsler (Cookie Jar)
+Name[nds]=KDED-Kookjepleeg
+Name[ne]=KDED कुकी जार मोड्युल
+Name[nl]=KDED-module voor het opslaan van cookies
+Name[nn]=KDED-informasjonskapselmodul
+Name[nso]=Seripa sa Jar ya Cookie ya KDED
+Name[pa]=KDED ਕੂਕੀਜ਼ Jar ਮੈਡੀਊਲ
+Name[pl]=Zarządzanie ciasteczkami
+Name[pt]=Módulo de 'Cookies' do KDED
+Name[pt_BR]=Módulo de Cookie Jar do KDE
+Name[ro]=Modul Cookie JAR pentru KDED
+Name[ru]=Служба cookie
+Name[rw]=Igice Jar Inyandikonyakwirema KDED
+Name[se]=KDED gáhkošlihtti-moduvla
+Name[sk]=Modul pre cookies KDED
+Name[sl]=Modul posode za piškotke KDED
+Name[sq]=Modul i KDED-it për Qyp të keksave nga KDED
+Name[sr]=KDED модул тегле за колачиће
+Name[sr@Latn]=KDED modul tegle za kolačiće
+Name[sv]=KDED-kakburksmodul
+Name[ta]=KDED தற்காலிக நினைவக சாடி பகுதி
+Name[te]=కెడిఈడి కుకీ జాడి మాడ్యూల్
+Name[tg]=Модули KDED Cookie Jar
+Name[th]=โมดูลโถคุกกี KDED
+Name[tr]=KDED Cookie Jar Modülü
+Name[tt]=KDED'nıñ Cookie Modulı
+Name[uk]=Модуль глечика з куками KDED
+Name[uz]=KDED kuki idish moduli
+Name[uz@cyrillic]=KDED куки идиш модули
+Name[ven]=Modulu wa Jar wa Cookie ya KDED
+Name[vi]=Mô-đun Cookie Jar của KDED
+Name[xh]=Isicatshulwa se KDED Cookie Jar
+Name[zh_CN]=KDED Cookie Jar 模块
+Name[zh_HK]=KDED Cookie Jar 模組
+Name[zh_TW]=KDED Cookie Jar 模組
+Name[zu]=Ingxenye Yojeke ye-Cookie ye-KDED
+Comment=Keeps track of all cookies in the system
+Comment[af]=Hou tred van al die koekies in die stelsel
+Comment[ar]=يراقب جميع الكعكات الموجودة على النظام
+Comment[be]=Захоўвае звесткі пра "печыва"
+Comment[bg]=Контрол над всички бисквитки в системата
+Comment[bn]=সিস্টেমে সমস্ত কুকি-র খোঁজখবর রাখে
+Comment[bs]=Prati sve kolačiće (cookije) na sistemu
+Comment[ca]=Segueix totes les galetes en el sistema
+Comment[cs]=Spravuje Cookies v počítači
+Comment[csb]=Trzëmô wszëtczé kùszczi w systemie
+Comment[da]=Holder styr på alle cookier på systemet
+Comment[de]=Verwaltet die Cookies in KDE
+Comment[el]=Διατηρεί αρχείο από όλα τα cookies στο σύστημα
+Comment[eo]=Registras ĉiujn kuketojn en la sistemo
+Comment[es]=Mantiene registro todas las cookies en el sistema
+Comment[et]=Hoiab silma peal kõigil süsteemi küpsistel
+Comment[eu]=Sistemaren cookie guztien jarraipena egiten du
+Comment[fa]=رد همۀ کوکیها را در سیستم نگه می‌دارد
+Comment[fi]=Seuraa järjestelmän evästeitä
+Comment[fr]=Conserve une trace de tous les cookies dans le système
+Comment[fy]=Hâld by wer alle koekjes binne
+Comment[gl]=Manter as pegadas de todas as Cookies no sistema
+Comment[he]=מבצע מעקב אחרי כל העוגיות במערכת
+Comment[hi]=तंत्र की सभी कुकी की जानकारी रखता है
+Comment[hr]=Vođenje evidencije o svim kolačićima na sustavu
+Comment[hu]=Nyomon követi a rendszerben létrejövő cookie-kat
+Comment[id]=Menyimpan semua cookies pada sistem
+Comment[is]=Heldur utanum allar smákökur í kerfinu
+Comment[it]=Tiene traccia di tutti i cookie del sistema
+Comment[ja]=システムのすべてのクッキーを管理します
+Comment[ka]=სისტემის ყველა ბმულის თვალმიდევნება
+Comment[kk]=Жүйедегі бүкіл cookie файлдарды бақылау
+Comment[km]=រក្សា​ការតាមដាន​ខូគី​ទាំងអស់​ក្នុង​ប្រព័ន្ធ
+Comment[lb]=Iwwerwaacht all d'Cookie vum System
+Comment[lt]=Seka visus slapukus sistemoje
+Comment[lv]=Seko visiem sistēmā esošajiem cepumiem
+Comment[mk]=Води сметка за сите колачиња во системот
+Comment[ms]=Memerhati semua cecikut dalam sistem
+Comment[nb]=Holder rede på alle informasjonskapsler i systemet
+Comment[nds]=Passt all Kookjes in't Systeem
+Comment[ne]=प्रणालीमा सबै कुकीहरूको पदचिन्ह राख्दछ
+Comment[nl]=Houdt alle cookies in het systeem bij
+Comment[nn]=Held greie på informasjonskapslane
+Comment[pa]=ਸਿਸਟਮ ਦੇ ਸਾਰੇ ਕੂਕੀਜ਼ ਦਾ ਰਿਕਾਰਡ ਰੱਖੋ
+Comment[pl]=Przechowuje wszystkie ciasteczka w systemie
+Comment[pt]=Mantém um registo de todos os 'cookies' no sistema
+Comment[pt_BR]=Mantém informações sobre todos os cookies do sistema
+Comment[ro]=Administrează toate "cookie"-urile din sistem
+Comment[ru]=Управление закладками-cookie в KDE
+Comment[rw]=Iguma inzira y'inyandikonyakwirema zose muri sisitemu
+Comment[se]=Halddaša buot diehtočoahkuid
+Comment[sk]=Sleduje všetky cookie v systéme
+Comment[sl]=Opazuje vse piškotke v sistemu
+Comment[sr]=Води евиденцију о свим колачићима на систему
+Comment[sr@Latn]=Vodi evidenciju o svim kolačićima na sistemu
+Comment[sv]=Håller ordning på alla kakor i systemet
+Comment[ta]=கணினியின் எல்லா தற்காலிக நினைவகங்களையும் கண்காணிக்கிறது
+Comment[te]=వ్యవస్థలోని అన్ని కుకీల జాడని వుంచుకుంటుంది
+Comment[tg]=Гузаргоҳи ҳамаша Cookies дар система муҳофизат кунед
+Comment[th]=ใช้ติดตามคุกกีทั้งหมดในระบบ
+Comment[tr]=Sistemdeki tüm çerezleri izler
+Comment[tt]=Sistemdäge bar cookie'larnı küz astında tota
+Comment[uk]=Стежить за всіма куками в системі
+Comment[uz]=Tizimdagi hamma kukilarni kuzatadi
+Comment[uz@cyrillic]=Тизимдаги ҳамма кукиларни кузатади
+Comment[vi]=Theo dõi các tập tin cookie trong hệ thống.
+Comment[zh_CN]=将全部 cookies 的记录保存在系统中
+Comment[zh_TW]=追蹤系統所有的 cookies
+ServiceTypes=KDEDModule
+Exec=kcookiejar
+X-DCOP-ServiceType=Unique
+X-KDE-StartupNotify=false
+X-KDE-ModuleType=Library
+X-KDE-Library=kcookiejar
+X-KDE-FactoryName=kcookiejar
+X-KDE-Kded-autoload=false
+X-KDE-Kded-load-on-demand=true
diff --git a/kioslave/http/kcookiejar/kcookiejar.h b/kioslave/http/kcookiejar/kcookiejar.h
new file mode 100644
index 000000000..c73708bea
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookiejar.h
@@ -0,0 +1,365 @@
+/*
+ This file is part of the KDE File Manager
+
+ Copyright (C) 1998 Waldo Bastian (bastian@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+//----------------------------------------------------------------------------
+//
+// KDE File Manager -- HTTP Cookies
+// $Id$
+
+#ifndef KCOOKIEJAR_H
+#define KCOOKIEJAR_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qdict.h>
+#include <qptrlist.h>
+#include <time.h>
+
+class KConfig;
+class KCookieJar;
+class KHttpCookie;
+class KHttpCookieList;
+
+typedef KHttpCookie *KHttpCookiePtr;
+
+enum KCookieAdvice
+{
+ KCookieDunno=0,
+ KCookieAccept,
+ KCookieReject,
+ KCookieAsk
+};
+
+class KHttpCookie
+{
+ friend class KCookieJar;
+ friend class KHttpCookieList;
+
+protected:
+ QString mHost;
+ QString mDomain;
+ QString mPath;
+ QString mName;
+ QString mValue;
+ time_t mExpireDate;
+ int mProtocolVersion;
+ bool mSecure;
+ bool mCrossDomain;
+ bool mHttpOnly;
+ bool mExplicitPath;
+ QValueList<long> mWindowIds;
+
+ QString cookieStr(bool useDOMFormat);
+
+public:
+ KHttpCookie(const QString &_host=QString::null,
+ const QString &_domain=QString::null,
+ const QString &_path=QString::null,
+ const QString &_name=QString::null,
+ const QString &_value=QString::null,
+ time_t _expireDate=0,
+ int _protocolVersion=0,
+ bool _secure = false,
+ bool _httpOnly = false,
+ bool _explicitPath = false);
+
+ QString domain(void) { return mDomain; }
+ QString host(void) { return mHost; }
+ QString path(void) { return mPath; }
+ QString name(void) { return mName; }
+ QString value(void) { return mValue; }
+ QValueList<long> &windowIds(void) { return mWindowIds; }
+ void fixDomain(const QString &domain) { mDomain = domain; }
+ time_t expireDate(void) { return mExpireDate; }
+ int protocolVersion(void) { return mProtocolVersion; }
+ bool isSecure(void) { return mSecure; }
+ bool isExpired(time_t currentDate);
+ bool isCrossDomain(void) { return mCrossDomain; }
+ bool isHttpOnly(void) { return mHttpOnly; }
+ bool hasExplicitPath(void) { return mExplicitPath; }
+ bool match(const QString &fqdn, const QStringList &domainList, const QString &path);
+};
+
+class KHttpCookieList : public QPtrList<KHttpCookie>
+{
+public:
+ KHttpCookieList() : QPtrList<KHttpCookie>(), advice( KCookieDunno )
+ { }
+ virtual ~KHttpCookieList() { }
+
+ virtual int compareItems( void * item1, void * item2);
+ KCookieAdvice getAdvice(void) { return advice; }
+ void setAdvice(KCookieAdvice _advice) { advice = _advice; }
+
+private:
+ KCookieAdvice advice;
+};
+
+class KCookieJar
+{
+public:
+ /**
+ * Constructs a new cookie jar
+ *
+ * One jar should be enough for all cookies.
+ */
+ KCookieJar();
+
+ /**
+ * Destructs the cookie jar
+ *
+ * Poor little cookies, they will all be eaten by the cookie monster!
+ */
+ ~KCookieJar();
+
+ /**
+ * Returns whether the cookiejar has been changed
+ */
+ bool changed() const { return m_cookiesChanged || m_configChanged; }
+
+ /**
+ * Store all the cookies in a safe(?) place
+ */
+ bool saveCookies(const QString &_filename);
+
+ /**
+ * Load all the cookies from file and add them to the cookie jar.
+ */
+ bool loadCookies(const QString &_filename);
+
+ /**
+ * Save the cookie configuration
+ */
+ void saveConfig(KConfig *_config);
+
+ /**
+ * Load the cookie configuration
+ */
+ void loadConfig(KConfig *_config, bool reparse = false);
+
+ /**
+ * Looks for cookies in the cookie jar which are appropriate for _url.
+ * Returned is a string containing all appropriate cookies in a format
+ * which can be added to a HTTP-header without any additional processing.
+ *
+ * If @p useDOMFormat is true, the string is formatted in a format
+ * in compliance with the DOM standard.
+ * @p pendingCookies contains a list of cookies that have not been
+ * approved yet by the user but that will be included in the result
+ * none the less.
+ */
+ QString findCookies(const QString &_url, bool useDOMFormat, long windowId, KHttpCookieList *pendingCookies=0);
+
+ /**
+ * This function parses cookie_headers and returns a linked list of
+ * valid KHttpCookie objects for all cookies found in cookie_headers.
+ * If no cookies could be found 0 is returned.
+ *
+ * cookie_headers should be a concatenation of all lines of a HTTP-header
+ * which start with "Set-Cookie". The lines should be separated by '\n's.
+ */
+ KHttpCookieList makeCookies(const QString &_url, const QCString &cookie_headers, long windowId);
+
+ /**
+ * This function parses cookie_headers and returns a linked list of
+ * valid KHttpCookie objects for all cookies found in cookie_headers.
+ * If no cookies could be found 0 is returned.
+ *
+ * cookie_domstr should be a concatenation of "name=value" pairs, separated
+ * by a semicolon ';'.
+ */
+ KHttpCookieList makeDOMCookies(const QString &_url, const QCString &cookie_domstr, long windowId);
+
+ /**
+ * This function hands a KHttpCookie object over to the cookie jar.
+ *
+ * On return cookiePtr is set to 0.
+ */
+ void addCookie(KHttpCookiePtr &cookiePtr);
+
+ /**
+ * This function advices whether a single KHttpCookie object should
+ * be added to the cookie jar.
+ *
+ * Possible return values are:
+ * - KCookieAccept, the cookie should be added
+ * - KCookieReject, the cookie should not be added
+ * - KCookieAsk, the user should decide what to do
+ */
+ KCookieAdvice cookieAdvice(KHttpCookiePtr cookiePtr);
+
+ /**
+ * This function gets the advice for all cookies originating from
+ * _domain.
+ *
+ * - KCookieDunno, no specific advice for _domain
+ * - KCookieAccept, accept all cookies for _domain
+ * - KCookieReject, reject all cookies for _domain
+ * - KCookieAsk, the user decides what to do with cookies for _domain
+ */
+ KCookieAdvice getDomainAdvice(const QString &_domain);
+
+ /**
+ * This function sets the advice for all cookies originating from
+ * _domain.
+ *
+ * _advice can have the following values:
+ * - KCookieDunno, no specific advice for _domain
+ * - KCookieAccept, accept all cookies for _domain
+ * - KCookieReject, reject all cookies for _domain
+ * - KCookieAsk, the user decides what to do with cookies for _domain
+ */
+ void setDomainAdvice(const QString &_domain, KCookieAdvice _advice);
+
+ /**
+ * This function sets the advice for all cookies originating from
+ * the same domain as _cookie
+ *
+ * _advice can have the following values:
+ * - KCookieDunno, no specific advice for _domain
+ * - KCookieAccept, accept all cookies for _domain
+ * - KCookieReject, reject all cookies for _domain
+ * - KCookieAsk, the user decides what to do with cookies for _domain
+ */
+ void setDomainAdvice(KHttpCookiePtr _cookie, KCookieAdvice _advice);
+
+ /**
+ * Get the global advice for cookies
+ *
+ * The returned advice can have the following values:
+ * - KCookieAccept, accept cookies
+ * - KCookieReject, reject cookies
+ * - KCookieAsk, the user decides what to do with cookies
+ *
+ * The global advice is used if the domain has no advice set.
+ */
+ KCookieAdvice getGlobalAdvice() { return m_globalAdvice; }
+
+ /**
+ * This function sets the global advice for cookies
+ *
+ * _advice can have the following values:
+ * - KCookieAccept, accept cookies
+ * - KCookieReject, reject cookies
+ * - KCookieAsk, the user decides what to do with cookies
+ *
+ * The global advice is used if the domain has no advice set.
+ */
+ void setGlobalAdvice(KCookieAdvice _advice);
+
+ /**
+ * Get a list of all domains known to the cookie jar.
+ * A domain is known to the cookie jar if:
+ * - It has a cookie originating from the domain
+ * - It has a specific advice set for the domain
+ */
+ const QStringList& getDomainList();
+
+ /**
+ * Get a list of all cookies in the cookie jar originating from _domain.
+ */
+ const KHttpCookieList *getCookieList(const QString & _domain,
+ const QString& _fqdn );
+
+ /**
+ * Remove & delete a cookie from the jar.
+ *
+ * cookiePtr should be one of the entries in a KHttpCookieList.
+ * Update your KHttpCookieList by calling getCookieList after
+ * calling this function.
+ */
+ void eatCookie(KHttpCookiePtr cookiePtr);
+
+ /**
+ * Remove & delete all cookies for @p domain.
+ */
+ void eatCookiesForDomain(const QString &domain);
+
+ /**
+ * Remove & delete all cookies
+ */
+ void eatAllCookies();
+
+ /**
+ * Removes all end of session cookies set by the
+ * session @p windId.
+ */
+ void eatSessionCookies( long windowId );
+
+ /**
+ * Removes all end of session cookies set by the
+ * session @p windId.
+ */
+ void eatSessionCookies( const QString& fqdn, long windowId, bool isFQDN = true );
+
+ /**
+ * Parses _url and returns the FQDN (_fqdn) and path (_path).
+ */
+ static bool parseURL(const QString &_url,
+ QString &_fqdn,
+ QString &_path);
+
+ /**
+ * Returns a list of domains in @p _domainList relevant for this host.
+ * The list is sorted with the FQDN listed first and the top-most
+ * domain listed last
+ */
+ void extractDomains(const QString &_fqdn,
+ QStringList &_domainList);
+
+ static QString adviceToStr(KCookieAdvice _advice);
+ static KCookieAdvice strToAdvice(const QString &_str);
+
+ /** Returns the */
+ int preferredDefaultPolicy() const { return m_preferredPolicy; }
+
+ /** Returns the */
+ bool showCookieDetails () const { return m_showCookieDetails; }
+
+ /**
+ * Sets the user's default preference cookie policy.
+ */
+ void setPreferredDefaultPolicy (int value) { m_preferredPolicy = value; }
+
+ /**
+ * Sets the user's preference of level of detail displayed
+ * by the cookie dialog.
+ */
+ void setShowCookieDetails (bool value) { m_showCookieDetails = value; }
+
+protected:
+ void stripDomain(const QString &_fqdn, QString &_domain);
+ QString stripDomain( KHttpCookiePtr cookiePtr);
+
+protected:
+ QStringList m_domainList;
+ KCookieAdvice m_globalAdvice;
+ QDict<KHttpCookieList> m_cookieDomains;
+ QDict<int> m_twoLevelTLD;
+
+ bool m_configChanged;
+ bool m_cookiesChanged;
+ bool m_showCookieDetails;
+ bool m_rejectCrossDomainCookies;
+ bool m_autoAcceptSessionCookies;
+ bool m_ignoreCookieExpirationDate;
+
+ int m_preferredPolicy;
+};
+#endif
diff --git a/kioslave/http/kcookiejar/kcookiescfg.upd b/kioslave/http/kcookiejar/kcookiescfg.upd
new file mode 100644
index 000000000..3c1cd028d
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookiescfg.upd
@@ -0,0 +1,16 @@
+# Update for old cookie config files, if present
+Id=kde2.2/b1
+File=kcookiejarrc
+Group=Browser Settings/HTTP,Cookie Policy
+
+# Update cookies config file...
+Id=kde3.1/cvs
+File=kcookiejarrc
+Group=<default>,Cookie Dialog
+Key=DefaultRadioButton,PreferredPolicy
+Key=ShowCookieDetails
+Group=Cookie Policy
+Key=AcceptTempCookies,AcceptSessionCookies
+Key=AutoAcceptSessionCookies,AcceptSessionCookies
+Key=RejectCrossDomain,RejectCrossDomainCookies
+Key=IgnoreCookieExpirationDate,IgnoreExpirationDate
diff --git a/kioslave/http/kcookiejar/kcookieserver.cpp b/kioslave/http/kcookiejar/kcookieserver.cpp
new file mode 100644
index 000000000..365f15e79
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookieserver.cpp
@@ -0,0 +1,606 @@
+/*
+This file is part of KDE
+
+ Copyright (C) 1998-2000 Waldo Bastian (bastian@kde.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+//----------------------------------------------------------------------------
+//
+// KDE Cookie Server
+// $Id$
+
+#define SAVE_DELAY 3 // Save after 3 minutes
+
+#include <unistd.h>
+
+#include <qtimer.h>
+#include <qptrlist.h>
+#include <qfile.h>
+
+#include <dcopclient.h>
+
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kstandarddirs.h>
+
+#include "kcookiejar.h"
+#include "kcookiewin.h"
+#include "kcookieserver.h"
+
+extern "C" {
+ KDE_EXPORT KDEDModule *create_kcookiejar(const QCString &name)
+ {
+ return new KCookieServer(name);
+ }
+}
+
+
+// Cookie field indexes
+enum CookieDetails { CF_DOMAIN=0, CF_PATH, CF_NAME, CF_HOST,
+ CF_VALUE, CF_EXPIRE, CF_PROVER, CF_SECURE };
+
+
+class CookieRequest {
+public:
+ DCOPClient *client;
+ DCOPClientTransaction *transaction;
+ QString url;
+ bool DOM;
+ long windowId;
+};
+
+template class QPtrList<CookieRequest>;
+
+class RequestList : public QPtrList<CookieRequest>
+{
+public:
+ RequestList() : QPtrList<CookieRequest>() { }
+};
+
+KCookieServer::KCookieServer(const QCString &name)
+ :KDEDModule(name)
+{
+ mOldCookieServer = new DCOPClient(); // backwards compatibility.
+ mOldCookieServer->registerAs("kcookiejar", false);
+ mOldCookieServer->setDaemonMode( true );
+ mCookieJar = new KCookieJar;
+ mPendingCookies = new KHttpCookieList;
+ mPendingCookies->setAutoDelete(true);
+ mRequestList = new RequestList;
+ mAdvicePending = false;
+ mTimer = new QTimer();
+ connect( mTimer, SIGNAL( timeout()), SLOT( slotSave()));
+ mConfig = new KConfig("kcookiejarrc");
+ mCookieJar->loadConfig( mConfig );
+
+ QString filename = locateLocal("data", "kcookiejar/cookies");
+
+ // Stay backwards compatible!
+ QString filenameOld = locate("data", "kfm/cookies");
+ if (!filenameOld.isEmpty())
+ {
+ mCookieJar->loadCookies( filenameOld );
+ if (mCookieJar->saveCookies( filename))
+ {
+ unlink(QFile::encodeName(filenameOld)); // Remove old kfm cookie file
+ }
+ }
+ else
+ {
+ mCookieJar->loadCookies( filename);
+ }
+ connect(this, SIGNAL(windowUnregistered(long)),
+ this, SLOT(slotDeleteSessionCookies(long)));
+}
+
+KCookieServer::~KCookieServer()
+{
+ if (mCookieJar->changed())
+ slotSave();
+ delete mOldCookieServer;
+ delete mCookieJar;
+ delete mTimer;
+ delete mPendingCookies;
+ delete mConfig;
+}
+
+bool KCookieServer::cookiesPending( const QString &url, KHttpCookieList *cookieList )
+{
+ QString fqdn;
+ QStringList domains;
+ QString path;
+ // Check whether 'url' has cookies on the pending list
+ if (mPendingCookies->isEmpty())
+ return false;
+ if (!KCookieJar::parseURL(url, fqdn, path))
+ return false;
+
+ mCookieJar->extractDomains( fqdn, domains );
+ for( KHttpCookie *cookie = mPendingCookies->first();
+ cookie != 0L;
+ cookie = mPendingCookies->next())
+ {
+ if (cookie->match( fqdn, domains, path))
+ {
+ if (!cookieList)
+ return true;
+ cookieList->append(cookie);
+ }
+ }
+ if (!cookieList)
+ return false;
+ return cookieList->isEmpty();
+}
+
+void KCookieServer::addCookies( const QString &url, const QCString &cookieHeader,
+ long windowId, bool useDOMFormat )
+{
+ KHttpCookieList cookieList;
+ if (useDOMFormat)
+ cookieList = mCookieJar->makeDOMCookies(url, cookieHeader, windowId);
+ else
+ cookieList = mCookieJar->makeCookies(url, cookieHeader, windowId);
+
+ checkCookies(&cookieList);
+
+ for(KHttpCookiePtr cookie = cookieList.first(); cookie; cookie = cookieList.first())
+ mPendingCookies->append(cookieList.take());
+
+ if (!mAdvicePending)
+ {
+ mAdvicePending = true;
+ while (!mPendingCookies->isEmpty())
+ {
+ checkCookies(0);
+ }
+ mAdvicePending = false;
+ }
+}
+
+void KCookieServer::checkCookies( KHttpCookieList *cookieList)
+{
+ KHttpCookieList *list;
+
+ if (cookieList)
+ list = cookieList;
+ else
+ list = mPendingCookies;
+
+ KHttpCookiePtr cookie = list->first();
+ while (cookie)
+ {
+ kdDebug(7104) << "checkCookies: Asking cookie advice for " << cookie->host() << endl;
+ KCookieAdvice advice = mCookieJar->cookieAdvice(cookie);
+ switch(advice)
+ {
+ case KCookieAccept:
+ list->take();
+ mCookieJar->addCookie(cookie);
+ cookie = list->current();
+ break;
+
+ case KCookieReject:
+ list->take();
+ delete cookie;
+ cookie = list->current();
+ break;
+
+ default:
+ cookie = list->next();
+ break;
+ }
+ }
+
+ if (cookieList || list->isEmpty())
+ return;
+
+ KHttpCookiePtr currentCookie = mPendingCookies->first();
+
+ KHttpCookieList currentList;
+ currentList.append(currentCookie);
+ QString currentHost = currentCookie->host();
+
+ cookie = mPendingCookies->next();
+ while (cookie)
+ {
+ if (cookie->host() == currentHost)
+ {
+ currentList.append(cookie);
+ }
+ cookie = mPendingCookies->next();
+ }
+
+ KCookieWin *kw = new KCookieWin( 0L, currentList,
+ mCookieJar->preferredDefaultPolicy(),
+ mCookieJar->showCookieDetails() );
+ KCookieAdvice userAdvice = kw->advice(mCookieJar, currentCookie);
+ delete kw;
+ // Save the cookie config if it has changed
+ mCookieJar->saveConfig( mConfig );
+
+ // Apply the user's choice to all cookies that are currently
+ // queued for this host.
+ cookie = mPendingCookies->first();
+ while (cookie)
+ {
+ if (cookie->host() == currentHost)
+ {
+ switch(userAdvice)
+ {
+ case KCookieAccept:
+ mPendingCookies->take();
+ mCookieJar->addCookie(cookie);
+ cookie = mPendingCookies->current();
+ break;
+
+ case KCookieReject:
+ mPendingCookies->take();
+ delete cookie;
+ cookie = mPendingCookies->current();
+ break;
+
+ default:
+ qWarning(__FILE__":%d Problem!", __LINE__);
+ cookie = mPendingCookies->next();
+ break;
+ }
+ }
+ else
+ {
+ cookie = mPendingCookies->next();
+ }
+ }
+
+
+ // Check if we can handle any request
+ for ( CookieRequest *request = mRequestList->first(); request;)
+ {
+ if (!cookiesPending( request->url ))
+ {
+ QCString replyType;
+ QByteArray replyData;
+ QString res = mCookieJar->findCookies( request->url, request->DOM, request->windowId );
+
+ QDataStream stream2(replyData, IO_WriteOnly);
+ stream2 << res;
+ replyType = "QString";
+ request->client->endTransaction( request->transaction,
+ replyType, replyData);
+ CookieRequest *tmp = request;
+ request = mRequestList->next();
+ mRequestList->removeRef( tmp );
+ delete tmp;
+ }
+ else
+ {
+ request = mRequestList->next();
+ }
+ }
+ if (mCookieJar->changed())
+ saveCookieJar();
+}
+
+void KCookieServer::slotSave()
+{
+ QString filename = locateLocal("data", "kcookiejar/cookies");
+ mCookieJar->saveCookies(filename);
+}
+
+void KCookieServer::saveCookieJar()
+{
+ if( mTimer->isActive() )
+ return;
+
+ mTimer->start( 1000*60*SAVE_DELAY, true );
+}
+
+void KCookieServer::putCookie( QStringList& out, KHttpCookie *cookie,
+ const QValueList<int>& fields )
+{
+ QValueList<int>::ConstIterator i = fields.begin();
+ for ( ; i != fields.end(); ++i )
+ {
+ switch(*i)
+ {
+ case CF_DOMAIN :
+ out << cookie->domain();
+ break;
+ case CF_NAME :
+ out << cookie->name();
+ break;
+ case CF_PATH :
+ out << cookie->path();
+ break;
+ case CF_HOST :
+ out << cookie->host();
+ break;
+ case CF_VALUE :
+ out << cookie->value();
+ break;
+ case CF_EXPIRE :
+ out << QString::number(cookie->expireDate());
+ break;
+ case CF_PROVER :
+ out << QString::number(cookie->protocolVersion());
+ break;
+ case CF_SECURE :
+ out << QString::number( cookie->isSecure() ? 1 : 0 );
+ break;
+ default :
+ out << QString::null;
+ }
+ }
+}
+
+bool KCookieServer::cookieMatches( KHttpCookiePtr c,
+ QString domain, QString fqdn,
+ QString path, QString name )
+{
+ if( c )
+ {
+ bool hasDomain = !domain.isEmpty();
+ return
+ ((hasDomain && c->domain() == domain) ||
+ fqdn == c->host()) &&
+ (c->path() == path) &&
+ (c->name() == name) &&
+ (!c->isExpired(time(0)));
+ }
+ return false;
+}
+
+// DCOP function
+QString
+KCookieServer::findCookies(QString url)
+{
+ return findCookies(url, 0);
+}
+
+// DCOP function
+QString
+KCookieServer::findCookies(QString url, long windowId)
+{
+ if (cookiesPending(url))
+ {
+ CookieRequest *request = new CookieRequest;
+ request->client = callingDcopClient();
+ request->transaction = request->client->beginTransaction();
+ request->url = url;
+ request->DOM = false;
+ request->windowId = windowId;
+ mRequestList->append( request );
+ return QString::null; // Talk to you later :-)
+ }
+
+ QString cookies = mCookieJar->findCookies(url, false, windowId);
+
+ if (mCookieJar->changed())
+ saveCookieJar();
+
+ return cookies;
+}
+
+// DCOP function
+QStringList
+KCookieServer::findDomains()
+{
+ QStringList result;
+ const QStringList domains = mCookieJar->getDomainList();
+ for ( QStringList::ConstIterator domIt = domains.begin();
+ domIt != domains.end(); ++domIt )
+ {
+ // Ignore domains that have policy set for but contain
+ // no cookies whatsoever...
+ const KHttpCookieList* list = mCookieJar->getCookieList(*domIt, "");
+ if ( list && !list->isEmpty() )
+ result << *domIt;
+ }
+ return result;
+}
+
+// DCOP function
+QStringList
+KCookieServer::findCookies(QValueList<int> fields,
+ QString domain,
+ QString fqdn,
+ QString path,
+ QString name)
+{
+ QStringList result;
+ bool allDomCookies = name.isEmpty();
+
+ const KHttpCookieList* list = mCookieJar->getCookieList(domain, fqdn);
+ if ( list && !list->isEmpty() )
+ {
+ QPtrListIterator<KHttpCookie>it( *list );
+ for ( ; it.current(); ++it )
+ {
+ if ( !allDomCookies )
+ {
+ if ( cookieMatches(it.current(), domain, fqdn, path, name) )
+ {
+ putCookie(result, it.current(), fields);
+ break;
+ }
+ }
+ else
+ putCookie(result, it.current(), fields);
+ }
+ }
+ return result;
+}
+
+// DCOP function
+QString
+KCookieServer::findDOMCookies(QString url)
+{
+ return findDOMCookies(url, 0);
+}
+
+// DCOP function
+QString
+KCookieServer::findDOMCookies(QString url, long windowId)
+{
+ // We don't wait for pending cookies because it locks up konqueror
+ // which can cause a deadlock if it happens to have a popup-menu up.
+ // Instead we just return pending cookies as if they had been accepted already.
+ KHttpCookieList pendingCookies;
+ cookiesPending(url, &pendingCookies);
+
+ return mCookieJar->findCookies(url, true, windowId, &pendingCookies);
+}
+
+// DCOP function
+void
+KCookieServer::addCookies(QString arg1, QCString arg2, long arg3)
+{
+ addCookies(arg1, arg2, arg3, false);
+}
+
+// DCOP function
+void
+KCookieServer::deleteCookie(QString domain, QString fqdn,
+ QString path, QString name)
+{
+ const KHttpCookieList* list = mCookieJar->getCookieList( domain, fqdn );
+ if ( list && !list->isEmpty() )
+ {
+ QPtrListIterator<KHttpCookie>it (*list);
+ for ( ; it.current(); ++it )
+ {
+ if( cookieMatches(it.current(), domain, fqdn, path, name) )
+ {
+ mCookieJar->eatCookie( it.current() );
+ saveCookieJar();
+ break;
+ }
+ }
+ }
+}
+
+// DCOP function
+void
+KCookieServer::deleteCookiesFromDomain(QString domain)
+{
+ mCookieJar->eatCookiesForDomain(domain);
+ saveCookieJar();
+}
+
+
+// Qt function
+void
+KCookieServer::slotDeleteSessionCookies( long windowId )
+{
+ deleteSessionCookies(windowId);
+}
+
+// DCOP function
+void
+KCookieServer::deleteSessionCookies( long windowId )
+{
+ mCookieJar->eatSessionCookies( windowId );
+ saveCookieJar();
+}
+
+void
+KCookieServer::deleteSessionCookiesFor(QString fqdn, long windowId)
+{
+ mCookieJar->eatSessionCookies( fqdn, windowId );
+ saveCookieJar();
+}
+
+// DCOP function
+void
+KCookieServer::deleteAllCookies()
+{
+ mCookieJar->eatAllCookies();
+ saveCookieJar();
+}
+
+// DCOP function
+void
+KCookieServer::addDOMCookies(QString arg1, QCString arg2, long arg3)
+{
+ addCookies(arg1, arg2, arg3, true);
+}
+
+// DCOP function
+void
+KCookieServer::setDomainAdvice(QString url, QString advice)
+{
+ QString fqdn;
+ QString dummy;
+ if (KCookieJar::parseURL(url, fqdn, dummy))
+ {
+ QStringList domains;
+ mCookieJar->extractDomains(fqdn, domains);
+
+ mCookieJar->setDomainAdvice(domains[domains.count() > 3 ? 3 : 0],
+ KCookieJar::strToAdvice(advice));
+ // Save the cookie config if it has changed
+ mCookieJar->saveConfig( mConfig );
+ }
+}
+
+// DCOP function
+QString
+KCookieServer::getDomainAdvice(QString url)
+{
+ KCookieAdvice advice = KCookieDunno;
+ QString fqdn;
+ QString dummy;
+ if (KCookieJar::parseURL(url, fqdn, dummy))
+ {
+ QStringList domains;
+ mCookieJar->extractDomains(fqdn, domains);
+
+ QStringList::ConstIterator it = domains.begin();
+ while ( (advice == KCookieDunno) && (it != domains.end()) )
+ {
+ // Always check advice in both ".domain" and "domain". Note
+ // that we only want to check "domain" if it matches the
+ // fqdn of the requested URL.
+ if ( (*it)[0] == '.' || (*it) == fqdn )
+ advice = mCookieJar->getDomainAdvice(*it);
+ ++it;
+ }
+ if (advice == KCookieDunno)
+ advice = mCookieJar->getGlobalAdvice();
+ }
+ return KCookieJar::adviceToStr(advice);
+}
+
+// DCOP function
+void
+KCookieServer::reloadPolicy()
+{
+ mCookieJar->loadConfig( mConfig, true );
+}
+
+// DCOP function
+void
+KCookieServer::shutdown()
+{
+ deleteLater();
+}
+
+#include "kcookieserver.moc"
+
diff --git a/kioslave/http/kcookiejar/kcookieserver.h b/kioslave/http/kcookiejar/kcookieserver.h
new file mode 100644
index 000000000..bcd7fa530
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookieserver.h
@@ -0,0 +1,98 @@
+/*
+ This file is part of the KDE File Manager
+
+ Copyright (C) 1998 Waldo Bastian (bastian@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+//----------------------------------------------------------------------------
+//
+// KDE Cookie Server
+// $Id$
+
+#ifndef KCOOKIESERVER_H
+#define KCOOKIESERVER_H
+
+#include <qstringlist.h>
+#include <kded/kdedmodule.h>
+
+class KHttpCookieList;
+class KCookieJar;
+class KHttpCookie;
+class QTimer;
+class RequestList;
+class DCOPClient;
+class KConfig;
+
+class KCookieServer : public KDEDModule
+{
+ Q_OBJECT
+ K_DCOP
+public:
+ KCookieServer(const QCString &);
+ ~KCookieServer();
+
+k_dcop:
+ QString findCookies(QString);
+ QString findCookies(QString, long);
+ QStringList findDomains();
+ QStringList findCookies(QValueList<int>,QString,QString,QString,QString);
+ QString findDOMCookies(QString);
+ QString findDOMCookies(QString, long);
+ void addCookies(QString, QCString, long);
+ void deleteCookie(QString, QString, QString, QString);
+ void deleteCookiesFromDomain(QString);
+ void deleteSessionCookies(long);
+ void deleteSessionCookiesFor(QString, long);
+ void deleteAllCookies();
+ void addDOMCookies(QString, QCString, long);
+ /**
+ * Sets the cookie policy for the domain associated with the specified URL.
+ */
+ void setDomainAdvice(QString url, QString advice);
+ /**
+ * Returns the cookie policy in effect for the specified URL.
+ */
+ QString getDomainAdvice(QString url);
+ void reloadPolicy();
+ void shutdown();
+
+public:
+ bool cookiesPending(const QString &url, KHttpCookieList *cookieList=0);
+ void addCookies(const QString &url, const QCString &cookieHeader,
+ long windowId, bool useDOMFormat);
+ void checkCookies(KHttpCookieList *cookieList);
+
+public slots:
+ void slotSave();
+ void slotDeleteSessionCookies(long);
+
+protected:
+ KCookieJar *mCookieJar;
+ KHttpCookieList *mPendingCookies;
+ RequestList *mRequestList;
+ QTimer *mTimer;
+ bool mAdvicePending;
+ DCOPClient *mOldCookieServer;
+ KConfig *mConfig;
+
+private:
+ virtual int newInstance(QValueList<QCString>) { return 0; }
+ bool cookieMatches(KHttpCookie*, QString, QString, QString, QString);
+ void putCookie(QStringList&, KHttpCookie*, const QValueList<int>&);
+ void saveCookieJar();
+};
+
+#endif
diff --git a/kioslave/http/kcookiejar/kcookiewin.cpp b/kioslave/http/kcookiejar/kcookiewin.cpp
new file mode 100644
index 000000000..5c68f8c1e
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookiewin.cpp
@@ -0,0 +1,382 @@
+/*
+This file is part of KDE
+
+ Copyright (C) 2000- Waldo Bastian <bastian@kde.org>
+ Copyright (C) 2000- Dawit Alemayehu <adawit@kde.org>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+//----------------------------------------------------------------------------
+//
+// KDE File Manager -- HTTP Cookie Dialogs
+// $Id$
+
+// The purpose of the QT_NO_TOOLTIP and QT_NO_WHATSTHIS ifdefs is because
+// this file is also used in Konqueror/Embedded. One of the aims of
+// Konqueror/Embedded is to be a small as possible to fit on embedded
+// devices. For this it's also useful to strip out unneeded features of
+// Qt, like for example QToolTip or QWhatsThis. The availability (or the
+// lack thereof) can be determined using these preprocessor defines.
+// The same applies to the QT_NO_ACCEL ifdef below. I hope it doesn't make
+// too much trouble... (Simon)
+
+#include <qhbox.h>
+#include <qvbox.h>
+#include <qaccel.h>
+#include <qlabel.h>
+#include <qwidget.h>
+#include <qlayout.h>
+#include <qgroupbox.h>
+#include <qdatetime.h>
+#include <qmessagebox.h>
+#include <qpushbutton.h>
+#include <qradiobutton.h>
+#include <qvbuttongroup.h>
+
+#ifndef QT_NO_TOOLTIP
+#include <qtooltip.h>
+#endif
+
+#ifndef QT_NO_WHATSTHIS
+#include <qwhatsthis.h>
+#endif
+
+#include <kidna.h>
+#include <kwin.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kurllabel.h>
+#include <klineedit.h>
+#include <kiconloader.h>
+#include <kapplication.h>
+
+#ifdef Q_WS_X11
+#include <X11/Xlib.h>
+#endif
+
+#include "kcookiejar.h"
+#include "kcookiewin.h"
+
+KCookieWin::KCookieWin( QWidget *parent, KHttpCookieList cookieList,
+ int defaultButton, bool showDetails )
+ :KDialog( parent, "cookiealert", true )
+{
+#ifndef Q_WS_QWS //FIXME(E): Implement for Qt Embedded
+ setCaption( i18n("Cookie Alert") );
+ setIcon( SmallIcon("cookie") );
+ // all cookies in the list should have the same window at this time, so let's take the first
+# ifdef Q_WS_X11
+ if( cookieList.first()->windowIds().count() > 0 )
+ {
+ XSetTransientForHint( qt_xdisplay(), winId(), cookieList.first()->windowIds().first());
+ }
+ else
+ {
+ // No window associated... make sure the user notices our dialog.
+ KWin::setState( winId(), NET::KeepAbove );
+ kapp->updateUserTimestamp();
+ }
+# endif
+#endif
+ // Main widget's layout manager...
+ QVBoxLayout* vlayout = new QVBoxLayout( this, KDialog::marginHint(), KDialog::spacingHint() );
+ vlayout->setResizeMode( QLayout::Fixed );
+
+ // Cookie image and message to user
+ QHBox* hBox = new QHBox( this );
+ hBox->setSpacing( KDialog::spacingHint() );
+ QLabel* icon = new QLabel( hBox );
+ icon->setPixmap( QMessageBox::standardIcon(QMessageBox::Warning) );
+ icon->setAlignment( Qt::AlignCenter );
+ icon->setFixedSize( 2*icon->sizeHint() );
+
+ int count = cookieList.count();
+
+ QVBox* vBox = new QVBox( hBox );
+ QString txt = i18n("You received a cookie from",
+ "You received %n cookies from", count);
+ QLabel* lbl = new QLabel( txt, vBox );
+ lbl->setAlignment( Qt::AlignCenter );
+ KHttpCookiePtr cookie = cookieList.first();
+
+ QString host (cookie->host());
+ int pos = host.find(':');
+ if ( pos > 0 )
+ {
+ QString portNum = host.left(pos);
+ host.remove(0, pos+1);
+ host += ':';
+ host += portNum;
+ }
+
+ txt = QString("<b>%1</b>").arg( KIDNA::toUnicode(host) );
+ if (cookie->isCrossDomain())
+ txt += i18n(" <b>[Cross Domain!]</b>");
+ lbl = new QLabel( txt, vBox );
+ lbl->setAlignment( Qt::AlignCenter );
+ lbl = new QLabel( i18n("Do you want to accept or reject?"), vBox );
+ lbl->setAlignment( Qt::AlignCenter );
+ vlayout->addWidget( hBox, 0, Qt::AlignLeft );
+
+ // Cookie Details dialog...
+ m_detailView = new KCookieDetail( cookieList, count, this );
+ vlayout->addWidget( m_detailView );
+ m_showDetails = showDetails;
+ m_showDetails ? m_detailView->show():m_detailView->hide();
+
+ // Cookie policy choice...
+ m_btnGrp = new QVButtonGroup( i18n("Apply Choice To"), this );
+ m_btnGrp->setRadioButtonExclusive( true );
+
+ txt = (count == 1)? i18n("&Only this cookie") : i18n("&Only these cookies");
+ QRadioButton* rb = new QRadioButton( txt, m_btnGrp );
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::add( rb, i18n("Select this option to accept/reject only this cookie. "
+ "You will be prompted if another cookie is received. "
+ "<em>(see WebBrowsing/Cookies in the Control Center)</em>." ) );
+#endif
+ m_btnGrp->insert( rb );
+ rb = new QRadioButton( i18n("All cookies from this do&main"), m_btnGrp );
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::add( rb, i18n("Select this option to accept/reject all cookies from "
+ "this site. Choosing this option will add a new policy for "
+ "the site this cookie originated from. This policy will be "
+ "permanent until you manually change it from the Control Center "
+ "<em>(see WebBrowsing/Cookies in the Control Center)</em>.") );
+#endif
+ m_btnGrp->insert( rb );
+ rb = new QRadioButton( i18n("All &cookies"), m_btnGrp );
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::add( rb, i18n("Select this option to accept/reject all cookies from "
+ "anywhere. Choosing this option will change the global "
+ "cookie policy set in the Control Center for all cookies "
+ "<em>(see WebBrowsing/Cookies in the Control Center)</em>.") );
+#endif
+ m_btnGrp->insert( rb );
+ vlayout->addWidget( m_btnGrp );
+
+ if ( defaultButton > -1 && defaultButton < 3 )
+ m_btnGrp->setButton( defaultButton );
+ else
+ m_btnGrp->setButton( 1 );
+
+ // Accept/Reject buttons
+ QWidget* bbox = new QWidget( this );
+ QBoxLayout* bbLay = new QHBoxLayout( bbox );
+ bbLay->setSpacing( KDialog::spacingHint() );
+ QPushButton* btn = new QPushButton( i18n("&Accept"), bbox );
+ btn->setDefault( true );
+ btn->setFocus();
+ connect( btn, SIGNAL(clicked()), SLOT(accept()) );
+ bbLay->addWidget( btn );
+ btn = new QPushButton( i18n("&Reject"), bbox );
+ connect( btn, SIGNAL(clicked()), SLOT(reject()) );
+ bbLay->addWidget( btn );
+ bbLay->addStretch( 1 );
+#ifndef QT_NO_ACCEL
+ QAccel* a = new QAccel( this );
+ a->connectItem( a->insertItem(Qt::Key_Escape), btn, SLOT(animateClick()) );
+#endif
+
+ m_button = new QPushButton( bbox );
+ m_button->setText( m_showDetails ? i18n("&Details <<"):i18n("&Details >>") );
+ connect( m_button, SIGNAL(clicked()), SLOT(slotCookieDetails()) );
+ bbLay->addWidget( m_button );
+#ifndef QT_NO_WHATSTHIS
+ QWhatsThis::add( m_button, i18n("See or modify the cookie information") );
+#endif
+
+
+ vlayout->addWidget( bbox );
+ setFixedSize( sizeHint() );
+}
+
+KCookieWin::~KCookieWin()
+{
+}
+
+void KCookieWin::slotCookieDetails()
+{
+ if ( m_detailView->isVisible() )
+ {
+ m_detailView->setMaximumSize( 0, 0 );
+ m_detailView->adjustSize();
+ m_detailView->hide();
+ m_button->setText( i18n( "&Details >>" ) );
+ m_showDetails = false;
+ }
+ else
+ {
+ m_detailView->setMaximumSize( 1000, 1000 );
+ m_detailView->adjustSize();
+ m_detailView->show();
+ m_button->setText( i18n( "&Details <<" ) );
+ m_showDetails = true;
+ }
+}
+
+KCookieAdvice KCookieWin::advice( KCookieJar *cookiejar, KHttpCookie* cookie )
+{
+ int result = exec();
+
+ cookiejar->setShowCookieDetails ( m_showDetails );
+
+ KCookieAdvice advice = (result==QDialog::Accepted) ? KCookieAccept:KCookieReject;
+
+ int preferredPolicy = m_btnGrp->id( m_btnGrp->selected() );
+ cookiejar->setPreferredDefaultPolicy( preferredPolicy );
+
+ switch ( preferredPolicy )
+ {
+ case 2:
+ cookiejar->setGlobalAdvice( advice );
+ break;
+ case 1:
+ cookiejar->setDomainAdvice( cookie, advice );
+ break;
+ case 0:
+ default:
+ break;
+ }
+ return advice;
+}
+
+KCookieDetail::KCookieDetail( KHttpCookieList cookieList, int cookieCount,
+ QWidget* parent, const char* name )
+ :QGroupBox( parent, name )
+{
+ setTitle( i18n("Cookie Details") );
+ QGridLayout* grid = new QGridLayout( this, 9, 2,
+ KDialog::spacingHint(),
+ KDialog::marginHint() );
+ grid->addRowSpacing( 0, fontMetrics().lineSpacing() );
+ grid->setColStretch( 1, 3 );
+
+ QLabel* label = new QLabel( i18n("Name:"), this );
+ grid->addWidget( label, 1, 0 );
+ m_name = new KLineEdit( this );
+ m_name->setReadOnly( true );
+ m_name->setMaximumWidth( fontMetrics().maxWidth() * 25 );
+ grid->addWidget( m_name, 1 ,1 );
+
+ //Add the value
+ label = new QLabel( i18n("Value:"), this );
+ grid->addWidget( label, 2, 0 );
+ m_value = new KLineEdit( this );
+ m_value->setReadOnly( true );
+ m_value->setMaximumWidth( fontMetrics().maxWidth() * 25 );
+ grid->addWidget( m_value, 2, 1);
+
+ label = new QLabel( i18n("Expires:"), this );
+ grid->addWidget( label, 3, 0 );
+ m_expires = new KLineEdit( this );
+ m_expires->setReadOnly( true );
+ m_expires->setMaximumWidth(fontMetrics().maxWidth() * 25 );
+ grid->addWidget( m_expires, 3, 1);
+
+ label = new QLabel( i18n("Path:"), this );
+ grid->addWidget( label, 4, 0 );
+ m_path = new KLineEdit( this );
+ m_path->setReadOnly( true );
+ m_path->setMaximumWidth( fontMetrics().maxWidth() * 25 );
+ grid->addWidget( m_path, 4, 1);
+
+ label = new QLabel( i18n("Domain:"), this );
+ grid->addWidget( label, 5, 0 );
+ m_domain = new KLineEdit( this );
+ m_domain->setReadOnly( true );
+ m_domain->setMaximumWidth( fontMetrics().maxWidth() * 25 );
+ grid->addWidget( m_domain, 5, 1);
+
+ label = new QLabel( i18n("Exposure:"), this );
+ grid->addWidget( label, 6, 0 );
+ m_secure = new KLineEdit( this );
+ m_secure->setReadOnly( true );
+ m_secure->setMaximumWidth( fontMetrics().maxWidth() * 25 );
+ grid->addWidget( m_secure, 6, 1 );
+
+ if ( cookieCount > 1 )
+ {
+ QPushButton* btnNext = new QPushButton( i18n("Next cookie","&Next >>"), this );
+ btnNext->setFixedSize( btnNext->sizeHint() );
+ grid->addMultiCellWidget( btnNext, 8, 8, 0, 1 );
+ connect( btnNext, SIGNAL(clicked()), SLOT(slotNextCookie()) );
+#ifndef QT_NO_TOOLTIP
+ QToolTip::add( btnNext, i18n("Show details of the next cookie") );
+#endif
+ }
+ m_cookieList = cookieList;
+ m_cookie = 0;
+ slotNextCookie();
+}
+
+KCookieDetail::~KCookieDetail()
+{
+}
+
+void KCookieDetail::slotNextCookie()
+{
+ KHttpCookiePtr cookie = m_cookieList.first();
+ if (m_cookie) while(cookie)
+ {
+ if (cookie == m_cookie)
+ {
+ cookie = m_cookieList.next();
+ break;
+ }
+ cookie = m_cookieList.next();
+ }
+ m_cookie = cookie;
+ if (!m_cookie)
+ m_cookie = m_cookieList.first();
+
+ if ( m_cookie )
+ {
+ m_name->setText( m_cookie->name() );
+ m_value->setText( ( m_cookie->value() ) );
+ if ( m_cookie->domain().isEmpty() )
+ m_domain->setText( i18n("Not specified") );
+ else
+ m_domain->setText( m_cookie->domain() );
+ m_path->setText( m_cookie->path() );
+ QDateTime cookiedate;
+ cookiedate.setTime_t( m_cookie->expireDate() );
+ if ( m_cookie->expireDate() )
+ m_expires->setText( KGlobal::locale()->formatDateTime(cookiedate) );
+ else
+ m_expires->setText( i18n("End of Session") );
+ QString sec;
+ if (m_cookie->isSecure())
+ {
+ if (m_cookie->isHttpOnly())
+ sec = i18n("Secure servers only");
+ else
+ sec = i18n("Secure servers, page scripts");
+ }
+ else
+ {
+ if (m_cookie->isHttpOnly())
+ sec = i18n("Servers");
+ else
+ sec = i18n("Servers, page scripts");
+ }
+ m_secure->setText( sec );
+ }
+}
+
+#include "kcookiewin.moc"
diff --git a/kioslave/http/kcookiejar/kcookiewin.h b/kioslave/http/kcookiejar/kcookiewin.h
new file mode 100644
index 000000000..30e92e7e0
--- /dev/null
+++ b/kioslave/http/kcookiejar/kcookiewin.h
@@ -0,0 +1,84 @@
+/*
+ This file is part of the KDE File Manager
+
+ Copyright (C) 1998- Waldo Bastian (bastian@kde.org)
+ Copyright (C) 2000- Dawit Alemayehu (adawit@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+//----------------------------------------------------------------------------
+//
+// KDE File Manager -- HTTP Cookie Dialogs
+// $Id$
+
+#ifndef _KCOOKIEWIN_H_
+#define _KCOOKIEWIN_H_
+
+#include <qgroupbox.h>
+
+#include <kdialog.h>
+#include "kcookiejar.h"
+
+class KLineEdit;
+class QPushButton;
+class QVButtonGroup;
+class KURLLabel;
+
+class KCookieDetail : public QGroupBox
+{
+ Q_OBJECT
+
+public :
+ KCookieDetail( KHttpCookieList cookieList, int cookieCount, QWidget *parent=0,
+ const char *name=0 );
+ ~KCookieDetail();
+
+private :
+ KLineEdit* m_name;
+ KLineEdit* m_value;
+ KLineEdit* m_expires;
+ KLineEdit* m_domain;
+ KLineEdit* m_path;
+ KLineEdit* m_secure;
+
+ KHttpCookieList m_cookieList;
+ KHttpCookiePtr m_cookie;
+
+private slots:
+ void slotNextCookie();
+};
+
+class KCookieWin : public KDialog
+{
+ Q_OBJECT
+
+public :
+ KCookieWin( QWidget *parent, KHttpCookieList cookieList, int defaultButton=0,
+ bool showDetails=false );
+ ~KCookieWin();
+
+ KCookieAdvice advice( KCookieJar *cookiejar, KHttpCookie* cookie );
+
+private :
+ QPushButton* m_button;
+ QVButtonGroup* m_btnGrp;
+ KCookieDetail* m_detailView;
+ bool m_showDetails;
+
+private slots:
+ void slotCookieDetails();
+};
+#endif
diff --git a/kioslave/http/kcookiejar/main.cpp b/kioslave/http/kcookiejar/main.cpp
new file mode 100644
index 000000000..1e943b939
--- /dev/null
+++ b/kioslave/http/kcookiejar/main.cpp
@@ -0,0 +1,92 @@
+/*
+This file is part of KDE
+
+ Copyright (C) 1998-2000 Waldo Bastian (bastian@kde.org)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include <dcopclient.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+#include <kapplication.h>
+
+static const char description[] =
+ I18N_NOOP("HTTP Cookie Daemon");
+
+static const char version[] = "1.0";
+
+static const KCmdLineOptions options[] =
+{
+ { "shutdown", I18N_NOOP("Shut down cookie jar"), 0 },
+ { "remove <domain>", I18N_NOOP("Remove cookies for domain"), 0 },
+ { "remove-all", I18N_NOOP("Remove all cookies"), 0 },
+ { "reload-config", I18N_NOOP("Reload configuration file"), 0 },
+ KCmdLineLastOption
+};
+
+extern "C" KDE_EXPORT int kdemain(int argc, char *argv[])
+{
+ KLocale::setMainCatalogue("kdelibs");
+ KCmdLineArgs::init(argc, argv, "kcookiejar", I18N_NOOP("HTTP cookie daemon"),
+ description, version);
+
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ KInstance a("kcookiejar");
+
+ kapp->dcopClient()->attach();
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ QCString replyType;
+ QByteArray replyData;
+ if (args->isSet("remove-all"))
+ {
+ kapp->dcopClient()->call( "kded", "kcookiejar", "deleteAllCookies()", QByteArray(), replyType, replyData);
+ }
+ if (args->isSet("remove"))
+ {
+ QString domain = args->getOption("remove");
+ QByteArray params;
+ QDataStream stream(params, IO_WriteOnly);
+ stream << domain;
+ kapp->dcopClient()->call( "kded", "kcookiejar", "deleteCookiesFromDomain(QString)", params, replyType, replyData);
+ }
+ if (args->isSet("shutdown"))
+ {
+ QCString module = "kcookiejar";
+ QByteArray params;
+ QDataStream stream(params, IO_WriteOnly);
+ stream << module;
+ kapp->dcopClient()->call( "kded", "kded", "unloadModule(QCString)", params, replyType, replyData);
+ }
+ else if(args->isSet("reload-config"))
+ {
+ kapp->dcopClient()->call( "kded", "kcookiejar", "reloadPolicy()", QByteArray(), replyType, replyData);
+ }
+ else
+ {
+ QCString module = "kcookiejar";
+ QByteArray params;
+ QDataStream stream(params, IO_WriteOnly);
+ stream << module;
+ kapp->dcopClient()->call( "kded", "kded", "loadModule(QCString)", params, replyType, replyData);
+ }
+
+ return 0;
+}
diff --git a/kioslave/http/kcookiejar/netscape_cookie_spec.html b/kioslave/http/kcookiejar/netscape_cookie_spec.html
new file mode 100644
index 000000000..eb190f2e3
--- /dev/null
+++ b/kioslave/http/kcookiejar/netscape_cookie_spec.html
@@ -0,0 +1,331 @@
+<HTML>
+<HEAD>
+<TITLE>Client Side State - HTTP Cookies</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="#ffffff" LINK="#0000ff" VLINK="#ff0000" ALINK="#ff0000" TEXT="#000000" >
+
+
+<CENTER>
+<!-- BANNER:s3 -->
+<A HREF="/maps/banners/documentation_s3.map"><IMG SRC="/images/banners/documentation_s3.gif" ALT="Documentation" BORDER=0 WIDTH=612 HEIGHT=50 ISMAP USEMAP="#banner_nav"></A>
+<MAP NAME="banner_nav">
+<AREA SHAPE=RECT COORDS="62,11,91,40" HREF="/">
+<AREA SHAPE=RECT COORDS="153,41,221,50" HREF="/">
+<AREA SHAPE=RECT COORDS="298,8,374,34" HREF="/support/index.html">
+<AREA SHAPE=RECT COORDS="381,15,586,43" HREF="http://help.netscape.com/browse/index.html">
+<AREA SHAPE=default NOHREF>
+</MAP>
+
+<!-- BANNER:s3 -->
+
+<H2>
+<FONT SIZE=+3>P</FONT>ERSISTENT
+<FONT SIZE=+3>C</FONT>LIENT
+<FONT SIZE=+3>S</FONT>TATE<BR>
+<FONT SIZE=+3>HTTP C</FONT>OOKIES
+</H2>
+
+<H3>Preliminary Specification - Use with caution</H3>
+</CENTER>
+
+<HR SIZE=4>
+
+<CENTER>
+<H3>
+<FONT SIZE=+2>I</FONT>NTRODUCTION
+</H3>
+</CENTER>
+
+Cookies are a general mechanism which server side connections (such as
+CGI scripts) can use to both store and retrieve information on the
+client side of the connection. The addition of a simple, persistent,
+client-side state significantly extends the capabilities of Web-based
+client/server applications.<P>
+
+<CENTER>
+<H3>
+<FONT SIZE=+2>O</FONT>VERVIEW
+</H3>
+</CENTER>
+
+A server, when returning an HTTP object to a client, may also send a
+piece of state information which the client will store. Included in that
+state object is a description of the range of URLs for which that state is
+valid. Any future HTTP requests made by the client which fall in that
+range will include a transmittal of the current value of the state
+object from the client back to the server. The state object is called
+a <B>cookie</B>, for no compelling reason. <P>
+This simple mechanism provides a powerful new tool which enables a host
+of new types of applications to be written for web-based environments.
+Shopping applications can now store information about the currently
+selected items, for fee services can send back registration information
+and free the client from retyping a user-id on next connection,
+sites can store per-user preferences on the client, and have the client supply
+those preferences every time that site is connected to.
+
+<CENTER>
+<H3>
+<FONT SIZE=+2>S</FONT>PECIFICATION
+</H3>
+</CENTER>
+
+A cookie is introduced to the client by including a <B>Set-Cookie</B>
+header as part of an HTTP response, typically this will be generated
+by a CGI script.
+
+<H3>Syntax of the Set-Cookie HTTP Response Header</H3>
+
+This is the format a CGI script would use to add to the HTTP headers
+a new piece of data which is to be stored by the client for later retrieval.
+
+<PRE>
+Set-Cookie: <I>NAME</I>=<I>VALUE</I>; expires=<I>DATE</I>;
+path=<I>PATH</I>; domain=<I>DOMAIN_NAME</I>; secure
+</PRE>
+<DL>
+<DT> <I>NAME</I>=<I>VALUE</I><DD>
+This string is a sequence of characters excluding semi-colon, comma and white
+space. If there is a need to place such data in the name or value, some
+encoding method such as URL style %XX encoding is recommended, though no
+encoding is defined or required. <P> This is the only required attribute
+on the <B>Set-Cookie</B> header. <P>
+<DT><B>expires</B>=<I>DATE</I>
+<DD>
+The <B>expires</B> attribute specifies a date string that
+defines the valid life time of that cookie. Once the expiration
+date has been reached, the cookie will no longer be stored or
+given out. <P>
+The date string is formatted as:
+<BLOCKQUOTE> <TT>Wdy, DD-Mon-YYYY HH:MM:SS GMT</TT></BLOCKQUOTE>
+This is based on
+<A TARGET="_top" HREF="http://ds.internic.net/rfc/rfc822.txt">RFC 822</A>,
+<A TARGET="_top" HREF="http://ds.internic.net/rfc/rfc850.txt">RFC 850</A>,
+<A TARGET="_top" HREF="http://www.w3.org/hypertext/WWW/Protocols/rfc1036/rfc1036.html#z6">
+RFC 1036</A>, and
+<A TARGET="_top" HREF="http://ds1.internic.net/rfc/rfc1123.txt">
+RFC 1123</A>,
+with the variations that the only legal time zone is <B>GMT</B> and
+the separators between the elements of the date must be dashes.
+<P>
+<B>expires</B> is an optional attribute. If not specified, the cookie will
+expire when the user's session ends. <P>
+<B>Note:</B> There is a bug in Netscape Navigator version 1.1 and earlier.
+Only cookies whose <B>path</B> attribute is set explicitly to "/" will
+be properly saved between sessions if they have an <B>expires</B>
+attribute.<P>
+
+<DT> <B>domain</B>=<I>DOMAIN_NAME</I>
+<DD>
+When searching the cookie list for valid cookies, a comparison of the
+<B>domain</B>
+attributes of the cookie is made with the Internet domain name of the
+host from which the URL will be fetched. If there is a tail match,
+then the cookie will go through <B>path</B> matching to see if it
+should be sent. "Tail matching" means that <B>domain</B> attribute
+is matched against the tail of the fully qualified domain name of
+the host. A <B>domain</B> attribute of "acme.com" would match
+host names "anvil.acme.com" as well as "shipping.crate.acme.com". <P>
+
+Only hosts within the specified domain
+can set a cookie for a domain and domains must have at least two (2)
+or three (3) periods in them to prevent domains of the form:
+".com", ".edu", and "va.us". Any domain that fails within
+one of the seven special top level domains listed below only require
+two periods. Any other domain requires at least three. The
+seven special top level domains are: "COM", "EDU", "NET", "ORG",
+"GOV", "MIL", and "INT".
+
+ <P>
+The default value of <B>domain</B> is the host name of the server
+which generated the cookie response. <P>
+<DT> <B>path</B>=<I>PATH</I>
+<DD>
+The <B>path</B> attribute is used to specify the subset of URLs in a
+domain for
+which the cookie is valid. If a cookie has already passed <B>domain</B>
+matching, then the pathname component
+of the URL is compared with the path attribute, and if there is
+a match, the cookie is considered valid and is sent along with
+the URL request. The path "/foo"
+would match "/foobar" and "/foo/bar.html". The path "/" is the most
+general path. <P>
+If the <B>path</B> is not specified, it as assumed to be the same path
+as the document being described by the header which contains the cookie.
+<P>
+<DT> <B>secure</B>
+<DD>
+If a cookie is marked <B>secure</B>, it will only be transmitted if the
+communications channel with the host is a secure one. Currently
+this means that secure cookies will only be sent to HTTPS (HTTP over SSL)
+servers. <P>
+If <B>secure</B> is not specified, a cookie is considered safe to be sent
+in the clear over unsecured channels.
+</DL>
+
+<H3>Syntax of the Cookie HTTP Request Header</H3>
+
+When requesting a URL from an HTTP server, the browser will match
+the URL against all cookies and if any of them match, a line
+containing the name/value pairs of all matching cookies will
+be included in the HTTP request. Here is the format of that line:
+<PRE>
+Cookie: <I>NAME1=OPAQUE_STRING1</I>; <I>NAME2=OPAQUE_STRING2 ...</I>
+</PRE>
+
+<H3>Additional Notes</H3>
+
+<UL>
+<LI>Multiple <B>Set-Cookie</B> headers can be issued in a single server
+response.
+<p>
+<LI>Instances of the same path and name will overwrite each other, with the
+latest instance taking precedence. Instances of the same path but
+different names will add additional mappings.
+<p>
+<LI>Setting the path to a higher-level value does not override other more
+specific path mappings. If there are multiple matches for a given cookie
+name, but with separate paths, all the matching cookies will be sent.
+(See examples below.)
+<p>
+<LI>The
+expires header lets the client know when it is safe to purge the mapping
+but the client is not required to do so. A client may also delete a
+cookie before it's expiration date arrives if the number of cookies
+exceeds its internal limits.
+<p>
+<LI>When sending cookies to a server, all cookies with a more specific
+path mapping should be sent before cookies with less specific path
+mappings. For example, a cookie "name1=foo" with a path mapping
+of "/" should be sent after a cookie "name1=foo2" with
+a path mapping of "/bar" if they are both to be sent.
+<p>
+<LI>There are limitations on the number of cookies that a client
+can store at any one time. This is a specification of the minimum
+number of cookies that a client should be prepared to receive and
+store.
+
+<UL>
+ <LI>300 total cookies
+ <LI>4 kilobytes per cookie, where the name and the OPAQUE_STRING
+ combine to form the 4 kilobyte limit.
+ <LI>20 cookies per server or domain. (note that completely
+ specified hosts and domains are treated as separate entities
+ and have a 20 cookie limitation for each, not combined)
+</UL>
+Servers should not expect clients to be able to exceed these limits.
+When the 300 cookie limit or the 20 cookie per server limit
+is exceeded, clients should delete the least recently used cookie.
+When a cookie larger than 4 kilobytes is encountered the cookie
+should be trimmed to fit, but the name should remain intact
+as long as it is less than 4 kilobytes.
+ <P>
+<LI>If a CGI script wishes to delete a cookie, it can do so by
+returning a cookie with the same name, and an <B>expires</B> time
+which is in the past. The path and name must match exactly
+in order for the expiring cookie to replace the valid cookie.
+This requirement makes it difficult for anyone but the originator
+of a cookie to delete a cookie.
+<P><LI>When caching HTTP, as a proxy server might do, the <B>Set-cookie</B>
+response header should never be cached.
+<P><LI>If a proxy server receives a response which
+contains a <B>Set-cookie</B> header, it should propagate the <B>Set-cookie</B>
+header to the client, regardless of whether the response was 304
+(Not Modified) or 200 (OK).
+<P>Similarly, if a client request contains a Cookie: header, it
+should be forwarded through a proxy, even if the conditional
+If-modified-since request is being made.
+</UL>
+
+<CENTER>
+<H3>
+<FONT SIZE=+2>E</FONT>XAMPLES
+</H3>
+</CENTER>
+
+Here are some sample exchanges which are designed to illustrate the use
+of cookies.
+<H3>First Example transaction sequence:</H3>
+<DL>
+<dt>Client requests a document, and receives in the response:<dd>
+<PRE>
+Set-Cookie: CUSTOMER=WILE_E_COYOTE; path=/; expires=Wednesday, 09-Nov-99 23:12:40 GMT</PRE>
+<dt>When client requests a URL in path "/" on this server, it sends:<DD>
+<PRE>Cookie: CUSTOMER=WILE_E_COYOTE</PRE>
+<dt>Client requests a document, and receives in the response:<dd>
+<PRE>Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/</PRE>
+<dt>When client requests a URL in path "/" on this server, it sends:<dd>
+<PRE>Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
+<dt>Client receives:<dd>
+<PRE>Set-Cookie: SHIPPING=FEDEX; path=/foo</PRE>
+<dt>When client requests a URL in path "/" on this server, it sends:<dd>
+<PRE>Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
+<dt>When client requests a URL in path "/foo" on this server, it sends:<dd>
+<PRE>Cookie: CUSTOMER=WILE_E_COYOTE; PART_NUMBER=ROCKET_LAUNCHER_0001; SHIPPING=FEDEX</PRE>
+</DL>
+<H3>Second Example transaction sequence:</H3>
+<DL>
+<dt>Assume all mappings from above have been cleared.<p>
+<dt>Client receives:<dd>
+<PRE>Set-Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001; path=/</PRE>
+<dt>When client requests a URL in path "/" on this server, it sends:<dd>
+<PRE>Cookie: PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
+<dt>Client receives:<dd>
+<PRE>Set-Cookie: PART_NUMBER=RIDING_ROCKET_0023; path=/ammo</PRE>
+<dt>When client requests a URL in path "/ammo" on this server, it sends:<dd>
+<PRE>Cookie: PART_NUMBER=RIDING_ROCKET_0023; PART_NUMBER=ROCKET_LAUNCHER_0001</PRE>
+<dd>NOTE: There are two name/value pairs named "PART_NUMBER" due to the
+inheritance
+of the "/" mapping in addition to the "/ammo" mapping.
+</DL>
+
+<HR SIZE=4>
+<P>
+
+<CENTER>
+
+
+<!-- footer -->
+<TABLE WIDTH=600 BORDER=0 CELLPADDING=0 CELLSPACING=0>
+<TR>
+<TD WIDTH=600 HEIGHT=8><HR SIZE=1 NOSHADE></TD></TR>
+<TR><TD ALIGN=LEFT VALIGN=TOP><FONT FACE="sans-serif, Arial, Helvetica" SIZE=-2><A HREF="http://home.netscape.com/misc/nav_redir/help.html" TARGET="_top">Help</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A
+HREF="http://home.netscape.com/misc/nav_redir/site_map.html" TARGET="_top">Site&nbsp;Map</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A
+HREF="http://home.netscape.com/misc/nav_redir/howtoget.html" TARGET="_top">How&nbsp;to&nbsp;Get&nbsp;Netscape&nbsp;Products</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A HREF="http://home.netscape.com/misc/nav_redir/ad.html" TARGET="_top">Advertise&nbsp;With&nbsp;Us</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/addsite.html" TARGET="_top">Add Site</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<A HREF="http://home.netscape.com/misc/nav_redir/custom_browser.html" TARGET="_top">Custom Browser Program</A></FONT></TD></TR>
+<TR>
+<TD WIDTH=600 HEIGHT=8 COLSPAN=0></TD>
+</TR>
+
+<TR>
+<TD ALIGN=LEFT VALIGN=TOP>
+<!-- Channels -->
+<FONT FACE="sans-serif, Arial, Helvetica" SIZE=-2><A HREF="http://home.netscape.com/misc/nav_redir/channels/autos.html" TARGET="_top">Autos</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
+HREF="http://home.netscape.com/misc/nav_redir/channels/business.html" TARGET="_top">Business</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/computers_internet.html" TARGET="_top">Computing&nbsp;&amp;&nbsp;Internet</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/entertainment.html" TARGET="_top">Entertainment</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
+HREF="http://home.netscape.com/misc/nav_redir/channels/kids_family.html" TARGET="_top">Family</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
+HREF="http://home.netscape.com/misc/nav_redir/channels/games.html" TARGET="_top">Games</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/health.html" TARGET="_top">Health</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/lifestyles.html" TARGET="_top">Lifestyles</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
+HREF="http://home.netscape.com/misc/nav_redir/channels/local.html" TARGET="_top">Local</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/netscape.html" TARGET="_top">Netscape</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/open_directory.html">Netscape&nbsp;Open&nbsp;Directory</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
+HREF="http://home.netscape.com/misc/nav_redir/channels/news.html" TARGET="_top">News</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/personalize_finance.html" TARGET="_top">Personal&nbsp;Finance</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
+HREF="http://home.netscape.com/misc/nav_redir/channels/real_estate.html" TARGET="_top">Real Estate</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/education.html" TARGET="_top">Research&nbsp;&amp;&nbsp;Learn</A>&nbsp;&nbsp;&nbsp;|&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/shopping.html" TARGET="_top">Shopping</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/smallbiz.html" TARGET="_top">Small Business</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A
+HREF="http://home.netscape.com/misc/nav_redir/channels/sports.html" TARGET="_top">Sports</A>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <A HREF="http://home.netscape.com/misc/nav_redir/channels/travel.html" TARGET="_top">Travel</A></FONT></TD></TR>
+</TABLE>
+
+<TABLE WIDTH=600 BORDER=0 CELLPADDING=0 CELLSPACING=0>
+<TR><TD WIDTH=600 HEIGHT=8 COLSPAN=0></TD></TR>
+<TR>
+<TD WIDTH=600 COLSPAN=5 VALIGN=TOP ALIGN=LEFT>
+<FONT FACE="sans-serif, Arial, Helvetica" SIZE=-2>
+&copy; 1999 Netscape, All Rights Reserved. <A HREF="http://home.netscape.com/legal_notices/index.html">Legal & Privacy Notices</A><BR>This site powered by <A HREF="http://home.netscape.com/comprod/server_central/index.html" TARGET="_top">Netscape SuiteSpot servers</A>.</FONT></TD>
+</TR>
+</TABLE>
+<!-- end footer -->
+
+
+
+
+</CENTER>
+<P>
+
+
+
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/kioslave/http/kcookiejar/rfc2109 b/kioslave/http/kcookiejar/rfc2109
new file mode 100644
index 000000000..432fdcc6e
--- /dev/null
+++ b/kioslave/http/kcookiejar/rfc2109
@@ -0,0 +1,1179 @@
+
+
+
+
+
+
+Network Working Group D. Kristol
+Request for Comments: 2109 Bell Laboratories, Lucent Technologies
+Category: Standards Track L. Montulli
+ Netscape Communications
+ February 1997
+
+
+ HTTP State Management Mechanism
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+1. ABSTRACT
+
+ This document specifies a way to create a stateful session with HTTP
+ requests and responses. It describes two new headers, Cookie and
+ Set-Cookie, which carry state information between participating
+ origin servers and user agents. The method described here differs
+ from Netscape's Cookie proposal, but it can interoperate with
+ HTTP/1.0 user agents that use Netscape's method. (See the HISTORICAL
+ section.)
+
+2. TERMINOLOGY
+
+ The terms user agent, client, server, proxy, and origin server have
+ the same meaning as in the HTTP/1.0 specification.
+
+ Fully-qualified host name (FQHN) means either the fully-qualified
+ domain name (FQDN) of a host (i.e., a completely specified domain
+ name ending in a top-level domain such as .com or .uk), or the
+ numeric Internet Protocol (IP) address of a host. The fully
+ qualified domain name is preferred; use of numeric IP addresses is
+ strongly discouraged.
+
+ The terms request-host and request-URI refer to the values the client
+ would send to the server as, respectively, the host (but not port)
+ and abs_path portions of the absoluteURI (http_URL) of the HTTP
+ request line. Note that request-host must be a FQHN.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 1]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ Hosts names can be specified either as an IP address or a FQHN
+ string. Sometimes we compare one host name with another. Host A's
+ name domain-matches host B's if
+
+ * both host names are IP addresses and their host name strings match
+ exactly; or
+
+ * both host names are FQDN strings and their host name strings match
+ exactly; or
+
+ * A is a FQDN string and has the form NB, where N is a non-empty name
+ string, B has the form .B', and B' is a FQDN string. (So, x.y.com
+ domain-matches .y.com but not y.com.)
+
+ Note that domain-match is not a commutative operation: a.b.c.com
+ domain-matches .c.com, but not the reverse.
+
+ Because it was used in Netscape's original implementation of state
+ management, we will use the term cookie to refer to the state
+ information that passes between an origin server and user agent, and
+ that gets stored by the user agent.
+
+3. STATE AND SESSIONS
+
+ This document describes a way to create stateful sessions with HTTP
+ requests and responses. Currently, HTTP servers respond to each
+ client request without relating that request to previous or
+ subsequent requests; the technique allows clients and servers that
+ wish to exchange state information to place HTTP requests and
+ responses within a larger context, which we term a "session". This
+ context might be used to create, for example, a "shopping cart", in
+ which user selections can be aggregated before purchase, or a
+ magazine browsing system, in which a user's previous reading affects
+ which offerings are presented.
+
+ There are, of course, many different potential contexts and thus many
+ different potential types of session. The designers' paradigm for
+ sessions created by the exchange of cookies has these key attributes:
+
+ 1. Each session has a beginning and an end.
+
+ 2. Each session is relatively short-lived.
+
+ 3. Either the user agent or the origin server may terminate a
+ session.
+
+ 4. The session is implicit in the exchange of state information.
+
+
+
+
+Kristol & Montulli Standards Track [Page 2]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+4. OUTLINE
+
+ We outline here a way for an origin server to send state information
+ to the user agent, and for the user agent to return the state
+ information to the origin server. The goal is to have a minimal
+ impact on HTTP and user agents. Only origin servers that need to
+ maintain sessions would suffer any significant impact, and that
+ impact can largely be confined to Common Gateway Interface (CGI)
+ programs, unless the server provides more sophisticated state
+ management support. (See Implementation Considerations, below.)
+
+4.1 Syntax: General
+
+ The two state management headers, Set-Cookie and Cookie, have common
+ syntactic properties involving attribute-value pairs. The following
+ grammar uses the notation, and tokens DIGIT (decimal digits) and
+ token (informally, a sequence of non-special, non-white space
+ characters) from the HTTP/1.1 specification [RFC 2068] to describe
+ their syntax.
+
+ av-pairs = av-pair *(";" av-pair)
+ av-pair = attr ["=" value] ; optional value
+ attr = token
+ value = word
+ word = token | quoted-string
+
+ Attributes (names) (attr) are case-insensitive. White space is
+ permitted between tokens. Note that while the above syntax
+ description shows value as optional, most attrs require them.
+
+ NOTE: The syntax above allows whitespace between the attribute and
+ the = sign.
+
+4.2 Origin Server Role
+
+4.2.1 General
+
+ The origin server initiates a session, if it so desires. (Note that
+ "session" here does not refer to a persistent network connection but
+ to a logical session created from HTTP requests and responses. The
+ presence or absence of a persistent connection should have no effect
+ on the use of cookie-derived sessions). To initiate a session, the
+ origin server returns an extra response header to the client, Set-
+ Cookie. (The details follow later.)
+
+ A user agent returns a Cookie request header (see below) to the
+ origin server if it chooses to continue a session. The origin server
+ may ignore it or use it to determine the current state of the
+
+
+
+Kristol & Montulli Standards Track [Page 3]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ session. It may send back to the client a Set-Cookie response header
+ with the same or different information, or it may send no Set-Cookie
+ header at all. The origin server effectively ends a session by
+ sending the client a Set-Cookie header with Max-Age=0.
+
+ Servers may return a Set-Cookie response headers with any response.
+ User agents should send Cookie request headers, subject to other
+ rules detailed below, with every request.
+
+ An origin server may include multiple Set-Cookie headers in a
+ response. Note that an intervening gateway could fold multiple such
+ headers into a single header.
+
+4.2.2 Set-Cookie Syntax
+
+ The syntax for the Set-Cookie response header is
+
+ set-cookie = "Set-Cookie:" cookies
+ cookies = 1#cookie
+ cookie = NAME "=" VALUE *(";" cookie-av)
+ NAME = attr
+ VALUE = value
+ cookie-av = "Comment" "=" value
+ | "Domain" "=" value
+ | "Max-Age" "=" value
+ | "Path" "=" value
+ | "Secure"
+ | "Version" "=" 1*DIGIT
+
+ Informally, the Set-Cookie response header comprises the token Set-
+ Cookie:, followed by a comma-separated list of one or more cookies.
+ Each cookie begins with a NAME=VALUE pair, followed by zero or more
+ semi-colon-separated attribute-value pairs. The syntax for
+ attribute-value pairs was shown earlier. The specific attributes and
+ the semantics of their values follows. The NAME=VALUE attribute-
+ value pair must come first in each cookie. The others, if present,
+ can occur in any order. If an attribute appears more than once in a
+ cookie, the behavior is undefined.
+
+ NAME=VALUE
+ Required. The name of the state information ("cookie") is NAME,
+ and its value is VALUE. NAMEs that begin with $ are reserved for
+ other uses and must not be used by applications.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 4]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ The VALUE is opaque to the user agent and may be anything the
+ origin server chooses to send, possibly in a server-selected
+ printable ASCII encoding. "Opaque" implies that the content is of
+ interest and relevance only to the origin server. The content
+ may, in fact, be readable by anyone that examines the Set-Cookie
+ header.
+
+ Comment=comment
+ Optional. Because cookies can contain private information about a
+ user, the Cookie attribute allows an origin server to document its
+ intended use of a cookie. The user can inspect the information to
+ decide whether to initiate or continue a session with this cookie.
+
+ Domain=domain
+ Optional. The Domain attribute specifies the domain for which the
+ cookie is valid. An explicitly specified domain must always start
+ with a dot.
+
+ Max-Age=delta-seconds
+ Optional. The Max-Age attribute defines the lifetime of the
+ cookie, in seconds. The delta-seconds value is a decimal non-
+ negative integer. After delta-seconds seconds elapse, the client
+ should discard the cookie. A value of zero means the cookie
+ should be discarded immediately.
+
+ Path=path
+ Optional. The Path attribute specifies the subset of URLs to
+ which this cookie applies.
+
+ Secure
+ Optional. The Secure attribute (with no value) directs the user
+ agent to use only (unspecified) secure means to contact the origin
+ server whenever it sends back this cookie.
+
+ The user agent (possibly under the user's control) may determine
+ what level of security it considers appropriate for "secure"
+ cookies. The Secure attribute should be considered security
+ advice from the server to the user agent, indicating that it is in
+ the session's interest to protect the cookie contents.
+
+ Version=version
+ Required. The Version attribute, a decimal integer, identifies to
+ which version of the state management specification the cookie
+ conforms. For this specification, Version=1 applies.
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 5]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+4.2.3 Controlling Caching
+
+ An origin server must be cognizant of the effect of possible caching
+ of both the returned resource and the Set-Cookie header. Caching
+ "public" documents is desirable. For example, if the origin server
+ wants to use a public document such as a "front door" page as a
+ sentinel to indicate the beginning of a session for which a Set-
+ Cookie response header must be generated, the page should be stored
+ in caches "pre-expired" so that the origin server will see further
+ requests. "Private documents", for example those that contain
+ information strictly private to a session, should not be cached in
+ shared caches.
+
+ If the cookie is intended for use by a single user, the Set-cookie
+ header should not be cached. A Set-cookie header that is intended to
+ be shared by multiple users may be cached.
+
+ The origin server should send the following additional HTTP/1.1
+ response headers, depending on circumstances:
+
+ * To suppress caching of the Set-Cookie header: Cache-control: no-
+ cache="set-cookie".
+
+ and one of the following:
+
+ * To suppress caching of a private document in shared caches: Cache-
+ control: private.
+
+ * To allow caching of a document and require that it be validated
+ before returning it to the client: Cache-control: must-revalidate.
+
+ * To allow caching of a document, but to require that proxy caches
+ (not user agent caches) validate it before returning it to the
+ client: Cache-control: proxy-revalidate.
+
+ * To allow caching of a document and request that it be validated
+ before returning it to the client (by "pre-expiring" it):
+ Cache-control: max-age=0. Not all caches will revalidate the
+ document in every case.
+
+ HTTP/1.1 servers must send Expires: old-date (where old-date is a
+ date long in the past) on responses containing Set-Cookie response
+ headers unless they know for certain (by out of band means) that
+ there are no downsteam HTTP/1.0 proxies. HTTP/1.1 servers may send
+ other Cache-Control directives that permit caching by HTTP/1.1
+ proxies in addition to the Expires: old-date directive; the Cache-
+ Control directive will override the Expires: old-date for HTTP/1.1
+ proxies.
+
+
+
+Kristol & Montulli Standards Track [Page 6]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+4.3 User Agent Role
+
+4.3.1 Interpreting Set-Cookie
+
+ The user agent keeps separate track of state information that arrives
+ via Set-Cookie response headers from each origin server (as
+ distinguished by name or IP address and port). The user agent
+ applies these defaults for optional attributes that are missing:
+
+ VersionDefaults to "old cookie" behavior as originally specified by
+ Netscape. See the HISTORICAL section.
+
+ Domain Defaults to the request-host. (Note that there is no dot at
+ the beginning of request-host.)
+
+ Max-AgeThe default behavior is to discard the cookie when the user
+ agent exits.
+
+ Path Defaults to the path of the request URL that generated the
+ Set-Cookie response, up to, but not including, the
+ right-most /.
+
+ Secure If absent, the user agent may send the cookie over an
+ insecure channel.
+
+4.3.2 Rejecting Cookies
+
+ To prevent possible security or privacy violations, a user agent
+ rejects a cookie (shall not store its information) if any of the
+ following is true:
+
+ * The value for the Path attribute is not a prefix of the request-
+ URI.
+
+ * The value for the Domain attribute contains no embedded dots or
+ does not start with a dot.
+
+ * The value for the request-host does not domain-match the Domain
+ attribute.
+
+ * The request-host is a FQDN (not IP address) and has the form HD,
+ where D is the value of the Domain attribute, and H is a string
+ that contains one or more dots.
+
+ Examples:
+
+ * A Set-Cookie from request-host y.x.foo.com for Domain=.foo.com
+ would be rejected, because H is y.x and contains a dot.
+
+
+
+Kristol & Montulli Standards Track [Page 7]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ * A Set-Cookie from request-host x.foo.com for Domain=.foo.com would
+ be accepted.
+
+ * A Set-Cookie with Domain=.com or Domain=.com., will always be
+ rejected, because there is no embedded dot.
+
+ * A Set-Cookie with Domain=ajax.com will be rejected because the
+ value for Domain does not begin with a dot.
+
+4.3.3 Cookie Management
+
+ If a user agent receives a Set-Cookie response header whose NAME is
+ the same as a pre-existing cookie, and whose Domain and Path
+ attribute values exactly (string) match those of a pre-existing
+ cookie, the new cookie supersedes the old. However, if the Set-
+ Cookie has a value for Max-Age of zero, the (old and new) cookie is
+ discarded. Otherwise cookies accumulate until they expire (resources
+ permitting), at which time they are discarded.
+
+ Because user agents have finite space in which to store cookies, they
+ may also discard older cookies to make space for newer ones, using,
+ for example, a least-recently-used algorithm, along with constraints
+ on the maximum number of cookies that each origin server may set.
+
+ If a Set-Cookie response header includes a Comment attribute, the
+ user agent should store that information in a human-readable form
+ with the cookie and should display the comment text as part of a
+ cookie inspection user interface.
+
+ User agents should allow the user to control cookie destruction. An
+ infrequently-used cookie may function as a "preferences file" for
+ network applications, and a user may wish to keep it even if it is
+ the least-recently-used cookie. One possible implementation would be
+ an interface that allows the permanent storage of a cookie through a
+ checkbox (or, conversely, its immediate destruction).
+
+ Privacy considerations dictate that the user have considerable
+ control over cookie management. The PRIVACY section contains more
+ information.
+
+4.3.4 Sending Cookies to the Origin Server
+
+ When it sends a request to an origin server, the user agent sends a
+ Cookie request header to the origin server if it has cookies that are
+ applicable to the request, based on
+
+ * the request-host;
+
+
+
+
+Kristol & Montulli Standards Track [Page 8]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ * the request-URI;
+
+ * the cookie's age.
+
+ The syntax for the header is:
+
+ cookie = "Cookie:" cookie-version
+ 1*((";" | ",") cookie-value)
+ cookie-value = NAME "=" VALUE [";" path] [";" domain]
+ cookie-version = "$Version" "=" value
+ NAME = attr
+ VALUE = value
+ path = "$Path" "=" value
+ domain = "$Domain" "=" value
+
+ The value of the cookie-version attribute must be the value from the
+ Version attribute, if any, of the corresponding Set-Cookie response
+ header. Otherwise the value for cookie-version is 0. The value for
+ the path attribute must be the value from the Path attribute, if any,
+ of the corresponding Set-Cookie response header. Otherwise the
+ attribute should be omitted from the Cookie request header. The
+ value for the domain attribute must be the value from the Domain
+ attribute, if any, of the corresponding Set-Cookie response header.
+ Otherwise the attribute should be omitted from the Cookie request
+ header.
+
+ Note that there is no Comment attribute in the Cookie request header
+ corresponding to the one in the Set-Cookie response header. The user
+ agent does not return the comment information to the origin server.
+
+ The following rules apply to choosing applicable cookie-values from
+ among all the cookies the user agent has.
+
+ Domain Selection
+ The origin server's fully-qualified host name must domain-match
+ the Domain attribute of the cookie.
+
+ Path Selection
+ The Path attribute of the cookie must match a prefix of the
+ request-URI.
+
+ Max-Age Selection
+ Cookies that have expired should have been discarded and thus
+ are not forwarded to an origin server.
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 9]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ If multiple cookies satisfy the criteria above, they are ordered in
+ the Cookie header such that those with more specific Path attributes
+ precede those with less specific. Ordering with respect to other
+ attributes (e.g., Domain) is unspecified.
+
+ Note: For backward compatibility, the separator in the Cookie header
+ is semi-colon (;) everywhere. A server should also accept comma (,)
+ as the separator between cookie-values for future compatibility.
+
+4.3.5 Sending Cookies in Unverifiable Transactions
+
+ Users must have control over sessions in order to ensure privacy.
+ (See PRIVACY section below.) To simplify implementation and to
+ prevent an additional layer of complexity where adequate safeguards
+ exist, however, this document distinguishes between transactions that
+ are verifiable and those that are unverifiable. A transaction is
+ verifiable if the user has the option to review the request-URI prior
+ to its use in the transaction. A transaction is unverifiable if the
+ user does not have that option. Unverifiable transactions typically
+ arise when a user agent automatically requests inlined or embedded
+ entities or when it resolves redirection (3xx) responses from an
+ origin server. Typically the origin transaction, the transaction
+ that the user initiates, is verifiable, and that transaction may
+ directly or indirectly induce the user agent to make unverifiable
+ transactions.
+
+ When it makes an unverifiable transaction, a user agent must enable a
+ session only if a cookie with a domain attribute D was sent or
+ received in its origin transaction, such that the host name in the
+ Request-URI of the unverifiable transaction domain-matches D.
+
+ This restriction prevents a malicious service author from using
+ unverifiable transactions to induce a user agent to start or continue
+ a session with a server in a different domain. The starting or
+ continuation of such sessions could be contrary to the privacy
+ expectations of the user, and could also be a security problem.
+
+ User agents may offer configurable options that allow the user agent,
+ or any autonomous programs that the user agent executes, to ignore
+ the above rule, so long as these override options default to "off".
+
+ Many current user agents already provide a review option that would
+ render many links verifiable. For instance, some user agents display
+ the URL that would be referenced for a particular link when the mouse
+ pointer is placed over that link. The user can therefore determine
+ whether to visit that site before causing the browser to do so.
+ (Though not implemented on current user agents, a similar technique
+ could be used for a button used to submit a form -- the user agent
+
+
+
+Kristol & Montulli Standards Track [Page 10]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ could display the action to be taken if the user were to select that
+ button.) However, even this would not make all links verifiable; for
+ example, links to automatically loaded images would not normally be
+ subject to "mouse pointer" verification.
+
+ Many user agents also provide the option for a user to view the HTML
+ source of a document, or to save the source to an external file where
+ it can be viewed by another application. While such an option does
+ provide a crude review mechanism, some users might not consider it
+ acceptable for this purpose.
+
+4.4 How an Origin Server Interprets the Cookie Header
+
+ A user agent returns much of the information in the Set-Cookie header
+ to the origin server when the Path attribute matches that of a new
+ request. When it receives a Cookie header, the origin server should
+ treat cookies with NAMEs whose prefix is $ specially, as an attribute
+ for the adjacent cookie. The value for such a NAME is to be
+ interpreted as applying to the lexically (left-to-right) most recent
+ cookie whose name does not have the $ prefix. If there is no
+ previous cookie, the value applies to the cookie mechanism as a
+ whole. For example, consider the cookie
+
+ Cookie: $Version="1"; Customer="WILE_E_COYOTE";
+ $Path="/acme"
+
+ $Version applies to the cookie mechanism as a whole (and gives the
+ version number for the cookie mechanism). $Path is an attribute
+ whose value (/acme) defines the Path attribute that was used when the
+ Customer cookie was defined in a Set-Cookie response header.
+
+4.5 Caching Proxy Role
+
+ One reason for separating state information from both a URL and
+ document content is to facilitate the scaling that caching permits.
+ To support cookies, a caching proxy must obey these rules already in
+ the HTTP specification:
+
+ * Honor requests from the cache, if possible, based on cache validity
+ rules.
+
+ * Pass along a Cookie request header in any request that the proxy
+ must make of another server.
+
+ * Return the response to the client. Include any Set-Cookie response
+ header.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 11]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ * Cache the received response subject to the control of the usual
+ headers, such as Expires, Cache-control: no-cache, and Cache-
+ control: private,
+
+ * Cache the Set-Cookie subject to the control of the usual header,
+ Cache-control: no-cache="set-cookie". (The Set-Cookie header
+ should usually not be cached.)
+
+ Proxies must not introduce Set-Cookie (Cookie) headers of their own
+ in proxy responses (requests).
+
+5. EXAMPLES
+
+5.1 Example 1
+
+ Most detail of request and response headers has been omitted. Assume
+ the user agent has no stored cookies.
+
+ 1. User Agent -> Server
+
+ POST /acme/login HTTP/1.1
+ [form data]
+
+ User identifies self via a form.
+
+ 2. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
+
+ Cookie reflects user's identity.
+
+ 3. User Agent -> Server
+
+ POST /acme/pickitem HTTP/1.1
+ Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
+ [form data]
+
+ User selects an item for "shopping basket."
+
+ 4. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ Shopping basket contains an item.
+
+
+
+
+Kristol & Montulli Standards Track [Page 12]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ 5. User Agent -> Server
+
+ POST /acme/shipping HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+ [form data]
+
+ User selects shipping method from form.
+
+ 6. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie: Shipping="FedEx"; Version="1"; Path="/acme"
+
+ New cookie reflects shipping method.
+
+ 7. User Agent -> Server
+
+ POST /acme/process HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme";
+ Shipping="FedEx"; $Path="/acme"
+ [form data]
+
+ User chooses to process order.
+
+ 8. Server -> User Agent
+
+ HTTP/1.1 200 OK
+
+ Transaction is complete.
+
+ The user agent makes a series of requests on the origin server, after
+ each of which it receives a new cookie. All the cookies have the
+ same Path attribute and (default) domain. Because the request URLs
+ all have /acme as a prefix, and that matches the Path attribute, each
+ request contains all the cookies received so far.
+
+5.2 Example 2
+
+ This example illustrates the effect of the Path attribute. All
+ detail of request and response headers has been omitted. Assume the
+ user agent has no stored cookies.
+
+ Imagine the user agent has received, in response to earlier requests,
+ the response headers
+
+
+
+Kristol & Montulli Standards Track [Page 13]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ Set-Cookie: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ and
+
+ Set-Cookie: Part_Number="Riding_Rocket_0023"; Version="1";
+ Path="/acme/ammo"
+
+ A subsequent request by the user agent to the (same) server for URLs
+ of the form /acme/ammo/... would include the following request
+ header:
+
+ Cookie: $Version="1";
+ Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+
+ Note that the NAME=VALUE pair for the cookie with the more specific
+ Path attribute, /acme/ammo, comes before the one with the less
+ specific Path attribute, /acme. Further note that the same cookie
+ name appears more than once.
+
+ A subsequent request by the user agent to the (same) server for a URL
+ of the form /acme/parts/ would include the following request header:
+
+ Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+
+ Here, the second cookie's Path attribute /acme/ammo is not a prefix
+ of the request URL, /acme/parts/, so the cookie does not get
+ forwarded to the server.
+
+6. IMPLEMENTATION CONSIDERATIONS
+
+ Here we speculate on likely or desirable details for an origin server
+ that implements state management.
+
+6.1 Set-Cookie Content
+
+ An origin server's content should probably be divided into disjoint
+ application areas, some of which require the use of state
+ information. The application areas can be distinguished by their
+ request URLs. The Set-Cookie header can incorporate information
+ about the application areas by setting the Path attribute for each
+ one.
+
+ The session information can obviously be clear or encoded text that
+ describes state. However, if it grows too large, it can become
+ unwieldy. Therefore, an implementor might choose for the session
+ information to be a key to a server-side resource. Of course, using
+
+
+
+Kristol & Montulli Standards Track [Page 14]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ a database creates some problems that this state management
+ specification was meant to avoid, namely:
+
+ 1. keeping real state on the server side;
+
+ 2. how and when to garbage-collect the database entry, in case the
+ user agent terminates the session by, for example, exiting.
+
+6.2 Stateless Pages
+
+ Caching benefits the scalability of WWW. Therefore it is important
+ to reduce the number of documents that have state embedded in them
+ inherently. For example, if a shopping-basket-style application
+ always displays a user's current basket contents on each page, those
+ pages cannot be cached, because each user's basket's contents would
+ be different. On the other hand, if each page contains just a link
+ that allows the user to "Look at My Shopping Basket", the page can be
+ cached.
+
+6.3 Implementation Limits
+
+ Practical user agent implementations have limits on the number and
+ size of cookies that they can store. In general, user agents' cookie
+ support should have no fixed limits. They should strive to store as
+ many frequently-used cookies as possible. Furthermore, general-use
+ user agents should provide each of the following minimum capabilities
+ individually, although not necessarily simultaneously:
+
+ * at least 300 cookies
+
+ * at least 4096 bytes per cookie (as measured by the size of the
+ characters that comprise the cookie non-terminal in the syntax
+ description of the Set-Cookie header)
+
+ * at least 20 cookies per unique host or domain name
+
+ User agents created for specific purposes or for limited-capacity
+ devices should provide at least 20 cookies of 4096 bytes, to ensure
+ that the user can interact with a session-based origin server.
+
+ The information in a Set-Cookie response header must be retained in
+ its entirety. If for some reason there is inadequate space to store
+ the cookie, it must be discarded, not truncated.
+
+ Applications should use as few and as small cookies as possible, and
+ they should cope gracefully with the loss of a cookie.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 15]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+6.3.1 Denial of Service Attacks
+
+ User agents may choose to set an upper bound on the number of cookies
+ to be stored from a given host or domain name or on the size of the
+ cookie information. Otherwise a malicious server could attempt to
+ flood a user agent with many cookies, or large cookies, on successive
+ responses, which would force out cookies the user agent had received
+ from other servers. However, the minima specified above should still
+ be supported.
+
+7. PRIVACY
+
+7.1 User Agent Control
+
+ An origin server could create a Set-Cookie header to track the path
+ of a user through the server. Users may object to this behavior as
+ an intrusive accumulation of information, even if their identity is
+ not evident. (Identity might become evident if a user subsequently
+ fills out a form that contains identifying information.) This state
+ management specification therefore requires that a user agent give
+ the user control over such a possible intrusion, although the
+ interface through which the user is given this control is left
+ unspecified. However, the control mechanisms provided shall at least
+ allow the user
+
+ * to completely disable the sending and saving of cookies.
+
+ * to determine whether a stateful session is in progress.
+
+ * to control the saving of a cookie on the basis of the cookie's
+ Domain attribute.
+
+ Such control could be provided by, for example, mechanisms
+
+ * to notify the user when the user agent is about to send a cookie
+ to the origin server, offering the option not to begin a session.
+
+ * to display a visual indication that a stateful session is in
+ progress.
+
+ * to let the user decide which cookies, if any, should be saved
+ when the user concludes a window or user agent session.
+
+ * to let the user examine the contents of a cookie at any time.
+
+ A user agent usually begins execution with no remembered state
+ information. It should be possible to configure a user agent never
+ to send Cookie headers, in which case it can never sustain state with
+
+
+
+Kristol & Montulli Standards Track [Page 16]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ an origin server. (The user agent would then behave like one that is
+ unaware of how to handle Set-Cookie response headers.)
+
+ When the user agent terminates execution, it should let the user
+ discard all state information. Alternatively, the user agent may ask
+ the user whether state information should be retained; the default
+ should be "no". If the user chooses to retain state information, it
+ would be restored the next time the user agent runs.
+
+ NOTE: User agents should probably be cautious about using files to
+ store cookies long-term. If a user runs more than one instance of
+ the user agent, the cookies could be commingled or otherwise messed
+ up.
+
+7.2 Protocol Design
+
+ The restrictions on the value of the Domain attribute, and the rules
+ concerning unverifiable transactions, are meant to reduce the ways
+ that cookies can "leak" to the "wrong" site. The intent is to
+ restrict cookies to one, or a closely related set of hosts.
+ Therefore a request-host is limited as to what values it can set for
+ Domain. We consider it acceptable for hosts host1.foo.com and
+ host2.foo.com to share cookies, but not a.com and b.com.
+
+ Similarly, a server can only set a Path for cookies that are related
+ to the request-URI.
+
+8. SECURITY CONSIDERATIONS
+
+8.1 Clear Text
+
+ The information in the Set-Cookie and Cookie headers is unprotected.
+ Two consequences are:
+
+ 1. Any sensitive information that is conveyed in them is exposed
+ to intruders.
+
+ 2. A malicious intermediary could alter the headers as they travel
+ in either direction, with unpredictable results.
+
+ These facts imply that information of a personal and/or financial
+ nature should only be sent over a secure channel. For less sensitive
+ information, or when the content of the header is a database key, an
+ origin server should be vigilant to prevent a bad Cookie value from
+ causing failures.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 17]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+8.2 Cookie Spoofing
+
+ Proper application design can avoid spoofing attacks from related
+ domains. Consider:
+
+ 1. User agent makes request to victim.cracker.edu, gets back
+ cookie session_id="1234" and sets the default domain
+ victim.cracker.edu.
+
+ 2. User agent makes request to spoof.cracker.edu, gets back
+ cookie session-id="1111", with Domain=".cracker.edu".
+
+ 3. User agent makes request to victim.cracker.edu again, and
+ passes
+
+ Cookie: $Version="1";
+ session_id="1234";
+ session_id="1111"; $Domain=".cracker.edu"
+
+ The server at victim.cracker.edu should detect that the second
+ cookie was not one it originated by noticing that the Domain
+ attribute is not for itself and ignore it.
+
+8.3 Unexpected Cookie Sharing
+
+ A user agent should make every attempt to prevent the sharing of
+ session information between hosts that are in different domains.
+ Embedded or inlined objects may cause particularly severe privacy
+ problems if they can be used to share cookies between disparate
+ hosts. For example, a malicious server could embed cookie
+ information for host a.com in a URI for a CGI on host b.com. User
+ agent implementors are strongly encouraged to prevent this sort of
+ exchange whenever possible.
+
+9. OTHER, SIMILAR, PROPOSALS
+
+ Three other proposals have been made to accomplish similar goals.
+ This specification is an amalgam of Kristol's State-Info proposal and
+ Netscape's Cookie proposal.
+
+ Brian Behlendorf proposed a Session-ID header that would be user-
+ agent-initiated and could be used by an origin server to track
+ "clicktrails". It would not carry any origin-server-defined state,
+ however. Phillip Hallam-Baker has proposed another client-defined
+ session ID mechanism for similar purposes.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 18]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+ While both session IDs and cookies can provide a way to sustain
+ stateful sessions, their intended purpose is different, and,
+ consequently, the privacy requirements for them are different. A
+ user initiates session IDs to allow servers to track progress through
+ them, or to distinguish multiple users on a shared machine. Cookies
+ are server-initiated, so the cookie mechanism described here gives
+ users control over something that would otherwise take place without
+ the users' awareness. Furthermore, cookies convey rich, server-
+ selected information, whereas session IDs comprise user-selected,
+ simple information.
+
+10. HISTORICAL
+
+10.1 Compatibility With Netscape's Implementation
+
+ HTTP/1.0 clients and servers may use Set-Cookie and Cookie headers
+ that reflect Netscape's original cookie proposal. These notes cover
+ inter-operation between "old" and "new" cookies.
+
+10.1.1 Extended Cookie Header
+
+ This proposal adds attribute-value pairs to the Cookie request header
+ in a compatible way. An "old" client that receives a "new" cookie
+ will ignore attributes it does not understand; it returns what it
+ does understand to the origin server. A "new" client always sends
+ cookies in the new form.
+
+ An "old" server that receives a "new" cookie will see what it thinks
+ are many cookies with names that begin with a $, and it will ignore
+ them. (The "old" server expects these cookies to be separated by
+ semi-colon, not comma.) A "new" server can detect cookies that have
+ passed through an "old" client, because they lack a $Version
+ attribute.
+
+10.1.2 Expires and Max-Age
+
+ Netscape's original proposal defined an Expires header that took a
+ date value in a fixed-length variant format in place of Max-Age:
+
+ Wdy, DD-Mon-YY HH:MM:SS GMT
+
+ Note that the Expires date format contains embedded spaces, and that
+ "old" cookies did not have quotes around values. Clients that
+ implement to this specification should be aware of "old" cookies and
+ Expires.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 19]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+10.1.3 Punctuation
+
+ In Netscape's original proposal, the values in attribute-value pairs
+ did not accept "-quoted strings. Origin servers should be cautious
+ about sending values that require quotes unless they know the
+ receiving user agent understands them (i.e., "new" cookies). A
+ ("new") user agent should only use quotes around values in Cookie
+ headers when the cookie's version(s) is (are) all compliant with this
+ specification or later.
+
+ In Netscape's original proposal, no whitespace was permitted around
+ the = that separates attribute-value pairs. Therefore such
+ whitespace should be used with caution in new implementations.
+
+10.2 Caching and HTTP/1.0
+
+ Some caches, such as those conforming to HTTP/1.0, will inevitably
+ cache the Set-Cookie header, because there was no mechanism to
+ suppress caching of headers prior to HTTP/1.1. This caching can lead
+ to security problems. Documents transmitted by an origin server
+ along with Set-Cookie headers will usually either be uncachable, or
+ will be "pre-expired". As long as caches obey instructions not to
+ cache documents (following Expires: <a date in the past> or Pragma:
+ no-cache (HTTP/1.0), or Cache-control: no-cache (HTTP/1.1))
+ uncachable documents present no problem. However, pre-expired
+ documents may be stored in caches. They require validation (a
+ conditional GET) on each new request, but some cache operators loosen
+ the rules for their caches, and sometimes serve expired documents
+ without first validating them. This combination of factors can lead
+ to cookies meant for one user later being sent to another user. The
+ Set-Cookie header is stored in the cache, and, although the document
+ is stale (expired), the cache returns the document in response to
+ later requests, including cached headers.
+
+11. ACKNOWLEDGEMENTS
+
+ This document really represents the collective efforts of the
+ following people, in addition to the authors: Roy Fielding, Marc
+ Hedlund, Ted Hardie, Koen Holtman, Shel Kaphan, Rohit Khare.
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 20]
+
+RFC 2109 HTTP State Management Mechanism February 1997
+
+
+12. AUTHORS' ADDRESSES
+
+ David M. Kristol
+ Bell Laboratories, Lucent Technologies
+ 600 Mountain Ave. Room 2A-227
+ Murray Hill, NJ 07974
+
+ Phone: (908) 582-2250
+ Fax: (908) 582-5809
+ EMail: dmk@bell-labs.com
+
+
+ Lou Montulli
+ Netscape Communications Corp.
+ 501 E. Middlefield Rd.
+ Mountain View, CA 94043
+
+ Phone: (415) 528-2600
+ EMail: montulli@netscape.com
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 21]
+
diff --git a/kioslave/http/kcookiejar/rfc2965 b/kioslave/http/kcookiejar/rfc2965
new file mode 100644
index 000000000..8a4d02b17
--- /dev/null
+++ b/kioslave/http/kcookiejar/rfc2965
@@ -0,0 +1,1459 @@
+
+
+
+
+
+
+Network Working Group D. Kristol
+Request for Comments: 2965 Bell Laboratories, Lucent Technologies
+Obsoletes: 2109 L. Montulli
+Category: Standards Track Epinions.com, Inc.
+ October 2000
+
+
+ HTTP State Management Mechanism
+
+Status of this Memo
+
+ This document specifies an Internet standards track protocol for the
+ Internet community, and requests discussion and suggestions for
+ improvements. Please refer to the current edition of the "Internet
+ Official Protocol Standards" (STD 1) for the standardization state
+ and status of this protocol. Distribution of this memo is unlimited.
+
+Copyright Notice
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+IESG Note
+
+ The IESG notes that this mechanism makes use of the .local top-level
+ domain (TLD) internally when handling host names that don't contain
+ any dots, and that this mechanism might not work in the expected way
+ should an actual .local TLD ever be registered.
+
+Abstract
+
+ This document specifies a way to create a stateful session with
+ Hypertext Transfer Protocol (HTTP) requests and responses. It
+ describes three new headers, Cookie, Cookie2, and Set-Cookie2, which
+ carry state information between participating origin servers and user
+ agents. The method described here differs from Netscape's Cookie
+ proposal [Netscape], but it can interoperate with HTTP/1.0 user
+ agents that use Netscape's method. (See the HISTORICAL section.)
+
+ This document reflects implementation experience with RFC 2109 and
+ obsoletes it.
+
+1. TERMINOLOGY
+
+ The terms user agent, client, server, proxy, origin server, and
+ http_URL have the same meaning as in the HTTP/1.1 specification
+ [RFC2616]. The terms abs_path and absoluteURI have the same meaning
+ as in the URI Syntax specification [RFC2396].
+
+
+
+
+Kristol & Montulli Standards Track [Page 1]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Host name (HN) means either the host domain name (HDN) or the numeric
+ Internet Protocol (IP) address of a host. The fully qualified domain
+ name is preferred; use of numeric IP addresses is strongly
+ discouraged.
+
+ The terms request-host and request-URI refer to the values the client
+ would send to the server as, respectively, the host (but not port)
+ and abs_path portions of the absoluteURI (http_URL) of the HTTP
+ request line. Note that request-host is a HN.
+
+ The term effective host name is related to host name. If a host name
+ contains no dots, the effective host name is that name with the
+ string .local appended to it. Otherwise the effective host name is
+ the same as the host name. Note that all effective host names
+ contain at least one dot.
+
+ The term request-port refers to the port portion of the absoluteURI
+ (http_URL) of the HTTP request line. If the absoluteURI has no
+ explicit port, the request-port is the HTTP default, 80. The
+ request-port of a cookie is the request-port of the request in which
+ a Set-Cookie2 response header was returned to the user agent.
+
+ Host names can be specified either as an IP address or a HDN string.
+ Sometimes we compare one host name with another. (Such comparisons
+ SHALL be case-insensitive.) Host A's name domain-matches host B's if
+
+ * their host name strings string-compare equal; or
+
+ * A is a HDN string and has the form NB, where N is a non-empty
+ name string, B has the form .B', and B' is a HDN string. (So,
+ x.y.com domain-matches .Y.com but not Y.com.)
+
+ Note that domain-match is not a commutative operation: a.b.c.com
+ domain-matches .c.com, but not the reverse.
+
+ The reach R of a host name H is defined as follows:
+
+ * If
+
+ - H is the host domain name of a host; and,
+
+ - H has the form A.B; and
+
+ - A has no embedded (that is, interior) dots; and
+
+ - B has at least one embedded dot, or B is the string "local".
+ then the reach of H is .B.
+
+
+
+
+Kristol & Montulli Standards Track [Page 2]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * Otherwise, the reach of H is H.
+
+ For two strings that represent paths, P1 and P2, P1 path-matches P2
+ if P2 is a prefix of P1 (including the case where P1 and P2 string-
+ compare equal). Thus, the string /tec/waldo path-matches /tec.
+
+ Because it was used in Netscape's original implementation of state
+ management, we will use the term cookie to refer to the state
+ information that passes between an origin server and user agent, and
+ that gets stored by the user agent.
+
+1.1 Requirements
+
+ The key words "MAY", "MUST", "MUST NOT", "OPTIONAL", "RECOMMENDED",
+ "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT" in this
+ document are to be interpreted as described in RFC 2119 [RFC2119].
+
+2. STATE AND SESSIONS
+
+ This document describes a way to create stateful sessions with HTTP
+ requests and responses. Currently, HTTP servers respond to each
+ client request without relating that request to previous or
+ subsequent requests; the state management mechanism allows clients
+ and servers that wish to exchange state information to place HTTP
+ requests and responses within a larger context, which we term a
+ "session". This context might be used to create, for example, a
+ "shopping cart", in which user selections can be aggregated before
+ purchase, or a magazine browsing system, in which a user's previous
+ reading affects which offerings are presented.
+
+ Neither clients nor servers are required to support cookies. A
+ server MAY refuse to provide content to a client that does not return
+ the cookies it sends.
+
+3. DESCRIPTION
+
+ We describe here a way for an origin server to send state information
+ to the user agent, and for the user agent to return the state
+ information to the origin server. The goal is to have a minimal
+ impact on HTTP and user agents.
+
+3.1 Syntax: General
+
+ The two state management headers, Set-Cookie2 and Cookie, have common
+ syntactic properties involving attribute-value pairs. The following
+ grammar uses the notation, and tokens DIGIT (decimal digits), token
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 3]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ (informally, a sequence of non-special, non-white space characters),
+ and http_URL from the HTTP/1.1 specification [RFC2616] to describe
+ their syntax.
+
+ av-pairs = av-pair *(";" av-pair)
+ av-pair = attr ["=" value] ; optional value
+ attr = token
+ value = token | quoted-string
+
+ Attributes (names) (attr) are case-insensitive. White space is
+ permitted between tokens. Note that while the above syntax
+ description shows value as optional, most attrs require them.
+
+ NOTE: The syntax above allows whitespace between the attribute and
+ the = sign.
+
+3.2 Origin Server Role
+
+ 3.2.1 General The origin server initiates a session, if it so
+ desires. To do so, it returns an extra response header to the
+ client, Set-Cookie2. (The details follow later.)
+
+ A user agent returns a Cookie request header (see below) to the
+ origin server if it chooses to continue a session. The origin server
+ MAY ignore it or use it to determine the current state of the
+ session. It MAY send back to the client a Set-Cookie2 response
+ header with the same or different information, or it MAY send no
+ Set-Cookie2 header at all. The origin server effectively ends a
+ session by sending the client a Set-Cookie2 header with Max-Age=0.
+
+ Servers MAY return Set-Cookie2 response headers with any response.
+ User agents SHOULD send Cookie request headers, subject to other
+ rules detailed below, with every request.
+
+ An origin server MAY include multiple Set-Cookie2 headers in a
+ response. Note that an intervening gateway could fold multiple such
+ headers into a single header.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 4]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ 3.2.2 Set-Cookie2 Syntax The syntax for the Set-Cookie2 response
+ header is
+
+ set-cookie = "Set-Cookie2:" cookies
+ cookies = 1#cookie
+ cookie = NAME "=" VALUE *(";" set-cookie-av)
+ NAME = attr
+ VALUE = value
+ set-cookie-av = "Comment" "=" value
+ | "CommentURL" "=" <"> http_URL <">
+ | "Discard"
+ | "Domain" "=" value
+ | "Max-Age" "=" value
+ | "Path" "=" value
+ | "Port" [ "=" <"> portlist <"> ]
+ | "Secure"
+ | "Version" "=" 1*DIGIT
+ portlist = 1#portnum
+ portnum = 1*DIGIT
+
+ Informally, the Set-Cookie2 response header comprises the token Set-
+ Cookie2:, followed by a comma-separated list of one or more cookies.
+ Each cookie begins with a NAME=VALUE pair, followed by zero or more
+ semi-colon-separated attribute-value pairs. The syntax for
+ attribute-value pairs was shown earlier. The specific attributes and
+ the semantics of their values follows. The NAME=VALUE attribute-
+ value pair MUST come first in each cookie. The others, if present,
+ can occur in any order. If an attribute appears more than once in a
+ cookie, the client SHALL use only the value associated with the first
+ appearance of the attribute; a client MUST ignore values after the
+ first.
+
+ The NAME of a cookie MAY be the same as one of the attributes in this
+ specification. However, because the cookie's NAME must come first in
+ a Set-Cookie2 response header, the NAME and its VALUE cannot be
+ confused with an attribute-value pair.
+
+ NAME=VALUE
+ REQUIRED. The name of the state information ("cookie") is NAME,
+ and its value is VALUE. NAMEs that begin with $ are reserved and
+ MUST NOT be used by applications.
+
+ The VALUE is opaque to the user agent and may be anything the
+ origin server chooses to send, possibly in a server-selected
+ printable ASCII encoding. "Opaque" implies that the content is of
+ interest and relevance only to the origin server. The content
+ may, in fact, be readable by anyone that examines the Set-Cookie2
+ header.
+
+
+
+Kristol & Montulli Standards Track [Page 5]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Comment=value
+ OPTIONAL. Because cookies can be used to derive or store private
+ information about a user, the value of the Comment attribute
+ allows an origin server to document how it intends to use the
+ cookie. The user can inspect the information to decide whether to
+ initiate or continue a session with this cookie. Characters in
+ value MUST be in UTF-8 encoding. [RFC2279]
+
+ CommentURL="http_URL"
+ OPTIONAL. Because cookies can be used to derive or store private
+ information about a user, the CommentURL attribute allows an
+ origin server to document how it intends to use the cookie. The
+ user can inspect the information identified by the URL to decide
+ whether to initiate or continue a session with this cookie.
+
+ Discard
+ OPTIONAL. The Discard attribute instructs the user agent to
+ discard the cookie unconditionally when the user agent terminates.
+
+ Domain=value
+ OPTIONAL. The value of the Domain attribute specifies the domain
+ for which the cookie is valid. If an explicitly specified value
+ does not start with a dot, the user agent supplies a leading dot.
+
+ Max-Age=value
+ OPTIONAL. The value of the Max-Age attribute is delta-seconds,
+ the lifetime of the cookie in seconds, a decimal non-negative
+ integer. To handle cached cookies correctly, a client SHOULD
+ calculate the age of the cookie according to the age calculation
+ rules in the HTTP/1.1 specification [RFC2616]. When the age is
+ greater than delta-seconds seconds, the client SHOULD discard the
+ cookie. A value of zero means the cookie SHOULD be discarded
+ immediately.
+
+ Path=value
+ OPTIONAL. The value of the Path attribute specifies the subset of
+ URLs on the origin server to which this cookie applies.
+
+ Port[="portlist"]
+ OPTIONAL. The Port attribute restricts the port to which a cookie
+ may be returned in a Cookie request header. Note that the syntax
+ REQUIREs quotes around the OPTIONAL portlist even if there is only
+ one portnum in portlist.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 6]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Secure
+ OPTIONAL. The Secure attribute (with no value) directs the user
+ agent to use only (unspecified) secure means to contact the origin
+ server whenever it sends back this cookie, to protect the
+ confidentially and authenticity of the information in the cookie.
+
+ The user agent (possibly with user interaction) MAY determine what
+ level of security it considers appropriate for "secure" cookies.
+ The Secure attribute should be considered security advice from the
+ server to the user agent, indicating that it is in the session's
+ interest to protect the cookie contents. When it sends a "secure"
+ cookie back to a server, the user agent SHOULD use no less than
+ the same level of security as was used when it received the cookie
+ from the server.
+
+ Version=value
+ REQUIRED. The value of the Version attribute, a decimal integer,
+ identifies the version of the state management specification to
+ which the cookie conforms. For this specification, Version=1
+ applies.
+
+ 3.2.3 Controlling Caching An origin server must be cognizant of the
+ effect of possible caching of both the returned resource and the
+ Set-Cookie2 header. Caching "public" documents is desirable. For
+ example, if the origin server wants to use a public document such as
+ a "front door" page as a sentinel to indicate the beginning of a
+ session for which a Set-Cookie2 response header must be generated,
+ the page SHOULD be stored in caches "pre-expired" so that the origin
+ server will see further requests. "Private documents", for example
+ those that contain information strictly private to a session, SHOULD
+ NOT be cached in shared caches.
+
+ If the cookie is intended for use by a single user, the Set-Cookie2
+ header SHOULD NOT be cached. A Set-Cookie2 header that is intended
+ to be shared by multiple users MAY be cached.
+
+ The origin server SHOULD send the following additional HTTP/1.1
+ response headers, depending on circumstances:
+
+ * To suppress caching of the Set-Cookie2 header:
+
+ Cache-control: no-cache="set-cookie2"
+
+ and one of the following:
+
+ * To suppress caching of a private document in shared caches:
+
+ Cache-control: private
+
+
+
+Kristol & Montulli Standards Track [Page 7]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * To allow caching of a document and require that it be validated
+ before returning it to the client:
+
+ Cache-Control: must-revalidate, max-age=0
+
+ * To allow caching of a document, but to require that proxy
+ caches (not user agent caches) validate it before returning it
+ to the client:
+
+ Cache-Control: proxy-revalidate, max-age=0
+
+ * To allow caching of a document and request that it be validated
+ before returning it to the client (by "pre-expiring" it):
+
+ Cache-control: max-age=0
+
+ Not all caches will revalidate the document in every case.
+
+ HTTP/1.1 servers MUST send Expires: old-date (where old-date is a
+ date long in the past) on responses containing Set-Cookie2 response
+ headers unless they know for certain (by out of band means) that
+ there are no HTTP/1.0 proxies in the response chain. HTTP/1.1
+ servers MAY send other Cache-Control directives that permit caching
+ by HTTP/1.1 proxies in addition to the Expires: old-date directive;
+ the Cache-Control directive will override the Expires: old-date for
+ HTTP/1.1 proxies.
+
+3.3 User Agent Role
+
+ 3.3.1 Interpreting Set-Cookie2 The user agent keeps separate track
+ of state information that arrives via Set-Cookie2 response headers
+ from each origin server (as distinguished by name or IP address and
+ port). The user agent MUST ignore attribute-value pairs whose
+ attribute it does not recognize. The user agent applies these
+ defaults for optional attributes that are missing:
+
+ Discard The default behavior is dictated by the presence or absence
+ of a Max-Age attribute.
+
+ Domain Defaults to the effective request-host. (Note that because
+ there is no dot at the beginning of effective request-host,
+ the default Domain can only domain-match itself.)
+
+ Max-Age The default behavior is to discard the cookie when the user
+ agent exits.
+
+ Path Defaults to the path of the request URL that generated the
+ Set-Cookie2 response, up to and including the right-most /.
+
+
+
+Kristol & Montulli Standards Track [Page 8]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Port The default behavior is that a cookie MAY be returned to any
+ request-port.
+
+ Secure If absent, the user agent MAY send the cookie over an
+ insecure channel.
+
+ 3.3.2 Rejecting Cookies To prevent possible security or privacy
+ violations, a user agent rejects a cookie according to rules below.
+ The goal of the rules is to try to limit the set of servers for which
+ a cookie is valid, based on the values of the Path, Domain, and Port
+ attributes and the request-URI, request-host and request-port.
+
+ A user agent rejects (SHALL NOT store its information) if the Version
+ attribute is missing. Moreover, a user agent rejects (SHALL NOT
+ store its information) if any of the following is true of the
+ attributes explicitly present in the Set-Cookie2 response header:
+
+ * The value for the Path attribute is not a prefix of the
+ request-URI.
+
+ * The value for the Domain attribute contains no embedded dots,
+ and the value is not .local.
+
+ * The effective host name that derives from the request-host does
+ not domain-match the Domain attribute.
+
+ * The request-host is a HDN (not IP address) and has the form HD,
+ where D is the value of the Domain attribute, and H is a string
+ that contains one or more dots.
+
+ * The Port attribute has a "port-list", and the request-port was
+ not in the list.
+
+ Examples:
+
+ * A Set-Cookie2 from request-host y.x.foo.com for Domain=.foo.com
+ would be rejected, because H is y.x and contains a dot.
+
+ * A Set-Cookie2 from request-host x.foo.com for Domain=.foo.com
+ would be accepted.
+
+ * A Set-Cookie2 with Domain=.com or Domain=.com., will always be
+ rejected, because there is no embedded dot.
+
+ * A Set-Cookie2 with Domain=ajax.com will be accepted, and the
+ value for Domain will be taken to be .ajax.com, because a dot
+ gets prepended to the value.
+
+
+
+
+Kristol & Montulli Standards Track [Page 9]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * A Set-Cookie2 with Port="80,8000" will be accepted if the
+ request was made to port 80 or 8000 and will be rejected
+ otherwise.
+
+ * A Set-Cookie2 from request-host example for Domain=.local will
+ be accepted, because the effective host name for the request-
+ host is example.local, and example.local domain-matches .local.
+
+ 3.3.3 Cookie Management If a user agent receives a Set-Cookie2
+ response header whose NAME is the same as that of a cookie it has
+ previously stored, the new cookie supersedes the old when: the old
+ and new Domain attribute values compare equal, using a case-
+ insensitive string-compare; and, the old and new Path attribute
+ values string-compare equal (case-sensitive). However, if the Set-
+ Cookie2 has a value for Max-Age of zero, the (old and new) cookie is
+ discarded. Otherwise a cookie persists (resources permitting) until
+ whichever happens first, then gets discarded: its Max-Age lifetime is
+ exceeded; or, if the Discard attribute is set, the user agent
+ terminates the session.
+
+ Because user agents have finite space in which to store cookies, they
+ MAY also discard older cookies to make space for newer ones, using,
+ for example, a least-recently-used algorithm, along with constraints
+ on the maximum number of cookies that each origin server may set.
+
+ If a Set-Cookie2 response header includes a Comment attribute, the
+ user agent SHOULD store that information in a human-readable form
+ with the cookie and SHOULD display the comment text as part of a
+ cookie inspection user interface.
+
+ If a Set-Cookie2 response header includes a CommentURL attribute, the
+ user agent SHOULD store that information in a human-readable form
+ with the cookie, or, preferably, SHOULD allow the user to follow the
+ http_URL link as part of a cookie inspection user interface.
+
+ The cookie inspection user interface may include a facility whereby a
+ user can decide, at the time the user agent receives the Set-Cookie2
+ response header, whether or not to accept the cookie. A potentially
+ confusing situation could arise if the following sequence occurs:
+
+ * the user agent receives a cookie that contains a CommentURL
+ attribute;
+
+ * the user agent's cookie inspection interface is configured so
+ that it presents a dialog to the user before the user agent
+ accepts the cookie;
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 10]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ * the dialog allows the user to follow the CommentURL link when
+ the user agent receives the cookie; and,
+
+ * when the user follows the CommentURL link, the origin server
+ (or another server, via other links in the returned content)
+ returns another cookie.
+
+ The user agent SHOULD NOT send any cookies in this context. The user
+ agent MAY discard any cookie it receives in this context that the
+ user has not, through some user agent mechanism, deemed acceptable.
+
+ User agents SHOULD allow the user to control cookie destruction, but
+ they MUST NOT extend the cookie's lifetime beyond that controlled by
+ the Discard and Max-Age attributes. An infrequently-used cookie may
+ function as a "preferences file" for network applications, and a user
+ may wish to keep it even if it is the least-recently-used cookie. One
+ possible implementation would be an interface that allows the
+ permanent storage of a cookie through a checkbox (or, conversely, its
+ immediate destruction).
+
+ Privacy considerations dictate that the user have considerable
+ control over cookie management. The PRIVACY section contains more
+ information.
+
+ 3.3.4 Sending Cookies to the Origin Server When it sends a request
+ to an origin server, the user agent includes a Cookie request header
+ if it has stored cookies that are applicable to the request, based on
+
+ * the request-host and request-port;
+
+ * the request-URI;
+
+ * the cookie's age.
+
+ The syntax for the header is:
+
+cookie = "Cookie:" cookie-version 1*((";" | ",") cookie-value)
+cookie-value = NAME "=" VALUE [";" path] [";" domain] [";" port]
+cookie-version = "$Version" "=" value
+NAME = attr
+VALUE = value
+path = "$Path" "=" value
+domain = "$Domain" "=" value
+port = "$Port" [ "=" <"> value <"> ]
+
+ The value of the cookie-version attribute MUST be the value from the
+ Version attribute of the corresponding Set-Cookie2 response header.
+ Otherwise the value for cookie-version is 0. The value for the path
+
+
+
+Kristol & Montulli Standards Track [Page 11]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ attribute MUST be the value from the Path attribute, if one was
+ present, of the corresponding Set-Cookie2 response header. Otherwise
+ the attribute SHOULD be omitted from the Cookie request header. The
+ value for the domain attribute MUST be the value from the Domain
+ attribute, if one was present, of the corresponding Set-Cookie2
+ response header. Otherwise the attribute SHOULD be omitted from the
+ Cookie request header.
+
+ The port attribute of the Cookie request header MUST mirror the Port
+ attribute, if one was present, in the corresponding Set-Cookie2
+ response header. That is, the port attribute MUST be present if the
+ Port attribute was present in the Set-Cookie2 header, and it MUST
+ have the same value, if any. Otherwise, if the Port attribute was
+ absent from the Set-Cookie2 header, the attribute likewise MUST be
+ omitted from the Cookie request header.
+
+ Note that there is neither a Comment nor a CommentURL attribute in
+ the Cookie request header corresponding to the ones in the Set-
+ Cookie2 response header. The user agent does not return the comment
+ information to the origin server.
+
+ The user agent applies the following rules to choose applicable
+ cookie-values to send in Cookie request headers from among all the
+ cookies it has received.
+
+ Domain Selection
+ The origin server's effective host name MUST domain-match the
+ Domain attribute of the cookie.
+
+ Port Selection
+ There are three possible behaviors, depending on the Port
+ attribute in the Set-Cookie2 response header:
+
+ 1. By default (no Port attribute), the cookie MAY be sent to any
+ port.
+
+ 2. If the attribute is present but has no value (e.g., Port), the
+ cookie MUST only be sent to the request-port it was received
+ from.
+
+ 3. If the attribute has a port-list, the cookie MUST only be
+ returned if the new request-port is one of those listed in
+ port-list.
+
+ Path Selection
+ The request-URI MUST path-match the Path attribute of the cookie.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 12]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Max-Age Selection
+ Cookies that have expired should have been discarded and thus are
+ not forwarded to an origin server.
+
+ If multiple cookies satisfy the criteria above, they are ordered in
+ the Cookie header such that those with more specific Path attributes
+ precede those with less specific. Ordering with respect to other
+ attributes (e.g., Domain) is unspecified.
+
+ Note: For backward compatibility, the separator in the Cookie header
+ is semi-colon (;) everywhere. A server SHOULD also accept comma (,)
+ as the separator between cookie-values for future compatibility.
+
+ 3.3.5 Identifying What Version is Understood: Cookie2 The Cookie2
+ request header facilitates interoperation between clients and servers
+ that understand different versions of the cookie specification. When
+ the client sends one or more cookies to an origin server, if at least
+ one of those cookies contains a $Version attribute whose value is
+ different from the version that the client understands, then the
+ client MUST also send a Cookie2 request header, the syntax for which
+ is
+
+ cookie2 = "Cookie2:" cookie-version
+
+ Here the value for cookie-version is the highest version of cookie
+ specification (currently 1) that the client understands. The client
+ needs to send at most one such request header per request.
+
+ 3.3.6 Sending Cookies in Unverifiable Transactions Users MUST have
+ control over sessions in order to ensure privacy. (See PRIVACY
+ section below.) To simplify implementation and to prevent an
+ additional layer of complexity where adequate safeguards exist,
+ however, this document distinguishes between transactions that are
+ verifiable and those that are unverifiable. A transaction is
+ verifiable if the user, or a user-designated agent, has the option to
+ review the request-URI prior to its use in the transaction. A
+ transaction is unverifiable if the user does not have that option.
+ Unverifiable transactions typically arise when a user agent
+ automatically requests inlined or embedded entities or when it
+ resolves redirection (3xx) responses from an origin server.
+ Typically the origin transaction, the transaction that the user
+ initiates, is verifiable, and that transaction may directly or
+ indirectly induce the user agent to make unverifiable transactions.
+
+ An unverifiable transaction is to a third-party host if its request-
+ host U does not domain-match the reach R of the request-host O in the
+ origin transaction.
+
+
+
+
+Kristol & Montulli Standards Track [Page 13]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ When it makes an unverifiable transaction, a user agent MUST disable
+ all cookie processing (i.e., MUST NOT send cookies, and MUST NOT
+ accept any received cookies) if the transaction is to a third-party
+ host.
+
+ This restriction prevents a malicious service author from using
+ unverifiable transactions to induce a user agent to start or continue
+ a session with a server in a different domain. The starting or
+ continuation of such sessions could be contrary to the privacy
+ expectations of the user, and could also be a security problem.
+
+ User agents MAY offer configurable options that allow the user agent,
+ or any autonomous programs that the user agent executes, to ignore
+ the above rule, so long as these override options default to "off".
+
+ (N.B. Mechanisms may be proposed that will automate overriding the
+ third-party restrictions under controlled conditions.)
+
+ Many current user agents already provide a review option that would
+ render many links verifiable. For instance, some user agents display
+ the URL that would be referenced for a particular link when the mouse
+ pointer is placed over that link. The user can therefore determine
+ whether to visit that site before causing the browser to do so.
+ (Though not implemented on current user agents, a similar technique
+ could be used for a button used to submit a form -- the user agent
+ could display the action to be taken if the user were to select that
+ button.) However, even this would not make all links verifiable; for
+ example, links to automatically loaded images would not normally be
+ subject to "mouse pointer" verification.
+
+ Many user agents also provide the option for a user to view the HTML
+ source of a document, or to save the source to an external file where
+ it can be viewed by another application. While such an option does
+ provide a crude review mechanism, some users might not consider it
+ acceptable for this purpose.
+
+3.4 How an Origin Server Interprets the Cookie Header
+
+ A user agent returns much of the information in the Set-Cookie2
+ header to the origin server when the request-URI path-matches the
+ Path attribute of the cookie. When it receives a Cookie header, the
+ origin server SHOULD treat cookies with NAMEs whose prefix is $
+ specially, as an attribute for the cookie.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 14]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+3.5 Caching Proxy Role
+
+ One reason for separating state information from both a URL and
+ document content is to facilitate the scaling that caching permits.
+ To support cookies, a caching proxy MUST obey these rules already in
+ the HTTP specification:
+
+ * Honor requests from the cache, if possible, based on cache
+ validity rules.
+
+ * Pass along a Cookie request header in any request that the
+ proxy must make of another server.
+
+ * Return the response to the client. Include any Set-Cookie2
+ response header.
+
+ * Cache the received response subject to the control of the usual
+ headers, such as Expires,
+
+ Cache-control: no-cache
+
+ and
+
+ Cache-control: private
+
+ * Cache the Set-Cookie2 subject to the control of the usual
+ header,
+
+ Cache-control: no-cache="set-cookie2"
+
+ (The Set-Cookie2 header should usually not be cached.)
+
+ Proxies MUST NOT introduce Set-Cookie2 (Cookie) headers of their own
+ in proxy responses (requests).
+
+4. EXAMPLES
+
+4.1 Example 1
+
+ Most detail of request and response headers has been omitted. Assume
+ the user agent has no stored cookies.
+
+ 1. User Agent -> Server
+
+ POST /acme/login HTTP/1.1
+ [form data]
+
+ User identifies self via a form.
+
+
+
+Kristol & Montulli Standards Track [Page 15]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ 2. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie2: Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"
+
+ Cookie reflects user's identity.
+
+ 3. User Agent -> Server
+
+ POST /acme/pickitem HTTP/1.1
+ Cookie: $Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"
+ [form data]
+
+ User selects an item for "shopping basket".
+
+ 4. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ Shopping basket contains an item.
+
+ 5. User Agent -> Server
+
+ POST /acme/shipping HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+ [form data]
+
+ User selects shipping method from form.
+
+ 6. Server -> User Agent
+
+ HTTP/1.1 200 OK
+ Set-Cookie2: Shipping="FedEx"; Version="1"; Path="/acme"
+
+ New cookie reflects shipping method.
+
+ 7. User Agent -> Server
+
+ POST /acme/process HTTP/1.1
+ Cookie: $Version="1";
+ Customer="WILE_E_COYOTE"; $Path="/acme";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme";
+ Shipping="FedEx"; $Path="/acme"
+ [form data]
+
+
+
+Kristol & Montulli Standards Track [Page 16]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ User chooses to process order.
+
+ 8. Server -> User Agent
+
+ HTTP/1.1 200 OK
+
+ Transaction is complete.
+
+ The user agent makes a series of requests on the origin server, after
+ each of which it receives a new cookie. All the cookies have the
+ same Path attribute and (default) domain. Because the request-URIs
+ all path-match /acme, the Path attribute of each cookie, each request
+ contains all the cookies received so far.
+
+4.2 Example 2
+
+ This example illustrates the effect of the Path attribute. All
+ detail of request and response headers has been omitted. Assume the
+ user agent has no stored cookies.
+
+ Imagine the user agent has received, in response to earlier requests,
+ the response headers
+
+ Set-Cookie2: Part_Number="Rocket_Launcher_0001"; Version="1";
+ Path="/acme"
+
+ and
+
+ Set-Cookie2: Part_Number="Riding_Rocket_0023"; Version="1";
+ Path="/acme/ammo"
+
+ A subsequent request by the user agent to the (same) server for URLs
+ of the form /acme/ammo/... would include the following request
+ header:
+
+ Cookie: $Version="1";
+ Part_Number="Riding_Rocket_0023"; $Path="/acme/ammo";
+ Part_Number="Rocket_Launcher_0001"; $Path="/acme"
+
+ Note that the NAME=VALUE pair for the cookie with the more specific
+ Path attribute, /acme/ammo, comes before the one with the less
+ specific Path attribute, /acme. Further note that the same cookie
+ name appears more than once.
+
+ A subsequent request by the user agent to the (same) server for a URL
+ of the form /acme/parts/ would include the following request header:
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 17]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Cookie: $Version="1"; Part_Number="Rocket_Launcher_0001";
+ $Path="/acme"
+
+ Here, the second cookie's Path attribute /acme/ammo is not a prefix
+ of the request URL, /acme/parts/, so the cookie does not get
+ forwarded to the server.
+
+5. IMPLEMENTATION CONSIDERATIONS
+
+ Here we provide guidance on likely or desirable details for an origin
+ server that implements state management.
+
+5.1 Set-Cookie2 Content
+
+ An origin server's content should probably be divided into disjoint
+ application areas, some of which require the use of state
+ information. The application areas can be distinguished by their
+ request URLs. The Set-Cookie2 header can incorporate information
+ about the application areas by setting the Path attribute for each
+ one.
+
+ The session information can obviously be clear or encoded text that
+ describes state. However, if it grows too large, it can become
+ unwieldy. Therefore, an implementor might choose for the session
+ information to be a key to a server-side resource. Of course, using
+ a database creates some problems that this state management
+ specification was meant to avoid, namely:
+
+ 1. keeping real state on the server side;
+
+ 2. how and when to garbage-collect the database entry, in case the
+ user agent terminates the session by, for example, exiting.
+
+5.2 Stateless Pages
+
+ Caching benefits the scalability of WWW. Therefore it is important
+ to reduce the number of documents that have state embedded in them
+ inherently. For example, if a shopping-basket-style application
+ always displays a user's current basket contents on each page, those
+ pages cannot be cached, because each user's basket's contents would
+ be different. On the other hand, if each page contains just a link
+ that allows the user to "Look at My Shopping Basket", the page can be
+ cached.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 18]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+5.3 Implementation Limits
+
+ Practical user agent implementations have limits on the number and
+ size of cookies that they can store. In general, user agents' cookie
+ support should have no fixed limits. They should strive to store as
+ many frequently-used cookies as possible. Furthermore, general-use
+ user agents SHOULD provide each of the following minimum capabilities
+ individually, although not necessarily simultaneously:
+
+ * at least 300 cookies
+
+ * at least 4096 bytes per cookie (as measured by the characters
+ that comprise the cookie non-terminal in the syntax description
+ of the Set-Cookie2 header, and as received in the Set-Cookie2
+ header)
+
+ * at least 20 cookies per unique host or domain name
+
+ User agents created for specific purposes or for limited-capacity
+ devices SHOULD provide at least 20 cookies of 4096 bytes, to ensure
+ that the user can interact with a session-based origin server.
+
+ The information in a Set-Cookie2 response header MUST be retained in
+ its entirety. If for some reason there is inadequate space to store
+ the cookie, it MUST be discarded, not truncated.
+
+ Applications should use as few and as small cookies as possible, and
+ they should cope gracefully with the loss of a cookie.
+
+ 5.3.1 Denial of Service Attacks User agents MAY choose to set an
+ upper bound on the number of cookies to be stored from a given host
+ or domain name or on the size of the cookie information. Otherwise a
+ malicious server could attempt to flood a user agent with many
+ cookies, or large cookies, on successive responses, which would force
+ out cookies the user agent had received from other servers. However,
+ the minima specified above SHOULD still be supported.
+
+6. PRIVACY
+
+ Informed consent should guide the design of systems that use cookies.
+ A user should be able to find out how a web site plans to use
+ information in a cookie and should be able to choose whether or not
+ those policies are acceptable. Both the user agent and the origin
+ server must assist informed consent.
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 19]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+6.1 User Agent Control
+
+ An origin server could create a Set-Cookie2 header to track the path
+ of a user through the server. Users may object to this behavior as
+ an intrusive accumulation of information, even if their identity is
+ not evident. (Identity might become evident, for example, if a user
+ subsequently fills out a form that contains identifying information.)
+ This state management specification therefore requires that a user
+ agent give the user control over such a possible intrusion, although
+ the interface through which the user is given this control is left
+ unspecified. However, the control mechanisms provided SHALL at least
+ allow the user
+
+ * to completely disable the sending and saving of cookies.
+
+ * to determine whether a stateful session is in progress.
+
+ * to control the saving of a cookie on the basis of the cookie's
+ Domain attribute.
+
+ Such control could be provided, for example, by mechanisms
+
+ * to notify the user when the user agent is about to send a
+ cookie to the origin server, to offer the option not to begin a
+ session.
+
+ * to display a visual indication that a stateful session is in
+ progress.
+
+ * to let the user decide which cookies, if any, should be saved
+ when the user concludes a window or user agent session.
+
+ * to let the user examine and delete the contents of a cookie at
+ any time.
+
+ A user agent usually begins execution with no remembered state
+ information. It SHOULD be possible to configure a user agent never
+ to send Cookie headers, in which case it can never sustain state with
+ an origin server. (The user agent would then behave like one that is
+ unaware of how to handle Set-Cookie2 response headers.)
+
+ When the user agent terminates execution, it SHOULD let the user
+ discard all state information. Alternatively, the user agent MAY ask
+ the user whether state information should be retained; the default
+ should be "no". If the user chooses to retain state information, it
+ would be restored the next time the user agent runs.
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 20]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ NOTE: User agents should probably be cautious about using files to
+ store cookies long-term. If a user runs more than one instance of
+ the user agent, the cookies could be commingled or otherwise
+ corrupted.
+
+6.2 Origin Server Role
+
+ An origin server SHOULD promote informed consent by adding CommentURL
+ or Comment information to the cookies it sends. CommentURL is
+ preferred because of the opportunity to provide richer information in
+ a multiplicity of languages.
+
+6.3 Clear Text
+
+ The information in the Set-Cookie2 and Cookie headers is unprotected.
+ As a consequence:
+
+ 1. Any sensitive information that is conveyed in them is exposed
+ to intruders.
+
+ 2. A malicious intermediary could alter the headers as they travel
+ in either direction, with unpredictable results.
+
+ These facts imply that information of a personal and/or financial
+ nature should only be sent over a secure channel. For less sensitive
+ information, or when the content of the header is a database key, an
+ origin server should be vigilant to prevent a bad Cookie value from
+ causing failures.
+
+ A user agent in a shared user environment poses a further risk.
+ Using a cookie inspection interface, User B could examine the
+ contents of cookies that were saved when User A used the machine.
+
+7. SECURITY CONSIDERATIONS
+
+7.1 Protocol Design
+
+ The restrictions on the value of the Domain attribute, and the rules
+ concerning unverifiable transactions, are meant to reduce the ways
+ that cookies can "leak" to the "wrong" site. The intent is to
+ restrict cookies to one host, or a closely related set of hosts.
+ Therefore a request-host is limited as to what values it can set for
+ Domain. We consider it acceptable for hosts host1.foo.com and
+ host2.foo.com to share cookies, but not a.com and b.com.
+
+ Similarly, a server can set a Path only for cookies that are related
+ to the request-URI.
+
+
+
+
+Kristol & Montulli Standards Track [Page 21]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+7.2 Cookie Spoofing
+
+ Proper application design can avoid spoofing attacks from related
+ domains. Consider:
+
+ 1. User agent makes request to victim.cracker.edu, gets back
+ cookie session_id="1234" and sets the default domain
+ victim.cracker.edu.
+
+ 2. User agent makes request to spoof.cracker.edu, gets back cookie
+ session-id="1111", with Domain=".cracker.edu".
+
+ 3. User agent makes request to victim.cracker.edu again, and
+ passes
+
+ Cookie: $Version="1"; session_id="1234",
+ $Version="1"; session_id="1111"; $Domain=".cracker.edu"
+
+ The server at victim.cracker.edu should detect that the second
+ cookie was not one it originated by noticing that the Domain
+ attribute is not for itself and ignore it.
+
+7.3 Unexpected Cookie Sharing
+
+ A user agent SHOULD make every attempt to prevent the sharing of
+ session information between hosts that are in different domains.
+ Embedded or inlined objects may cause particularly severe privacy
+ problems if they can be used to share cookies between disparate
+ hosts. For example, a malicious server could embed cookie
+ information for host a.com in a URI for a CGI on host b.com. User
+ agent implementors are strongly encouraged to prevent this sort of
+ exchange whenever possible.
+
+7.4 Cookies For Account Information
+
+ While it is common practice to use them this way, cookies are not
+ designed or intended to be used to hold authentication information,
+ such as account names and passwords. Unless such cookies are
+ exchanged over an encrypted path, the account information they
+ contain is highly vulnerable to perusal and theft.
+
+8. OTHER, SIMILAR, PROPOSALS
+
+ Apart from RFC 2109, three other proposals have been made to
+ accomplish similar goals. This specification began as an amalgam of
+ Kristol's State-Info proposal [DMK95] and Netscape's Cookie proposal
+ [Netscape].
+
+
+
+
+Kristol & Montulli Standards Track [Page 22]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ Brian Behlendorf proposed a Session-ID header that would be user-
+ agent-initiated and could be used by an origin server to track
+ "clicktrails". It would not carry any origin-server-defined state,
+ however. Phillip Hallam-Baker has proposed another client-defined
+ session ID mechanism for similar purposes.
+
+ While both session IDs and cookies can provide a way to sustain
+ stateful sessions, their intended purpose is different, and,
+ consequently, the privacy requirements for them are different. A
+ user initiates session IDs to allow servers to track progress through
+ them, or to distinguish multiple users on a shared machine. Cookies
+ are server-initiated, so the cookie mechanism described here gives
+ users control over something that would otherwise take place without
+ the users' awareness. Furthermore, cookies convey rich, server-
+ selected information, whereas session IDs comprise user-selected,
+ simple information.
+
+9. HISTORICAL
+
+9.1 Compatibility with Existing Implementations
+
+ Existing cookie implementations, based on the Netscape specification,
+ use the Set-Cookie (not Set-Cookie2) header. User agents that
+ receive in the same response both a Set-Cookie and Set-Cookie2
+ response header for the same cookie MUST discard the Set-Cookie
+ information and use only the Set-Cookie2 information. Furthermore, a
+ user agent MUST assume, if it received a Set-Cookie2 response header,
+ that the sending server complies with this document and will
+ understand Cookie request headers that also follow this
+ specification.
+
+ New cookies MUST replace both equivalent old- and new-style cookies.
+ That is, if a user agent that follows both this specification and
+ Netscape's original specification receives a Set-Cookie2 response
+ header, and the NAME and the Domain and Path attributes match (per
+ the Cookie Management section) a Netscape-style cookie, the
+ Netscape-style cookie MUST be discarded, and the user agent MUST
+ retain only the cookie adhering to this specification.
+
+ Older user agents that do not understand this specification, but that
+ do understand Netscape's original specification, will not recognize
+ the Set-Cookie2 response header and will receive and send cookies
+ according to the older specification.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 23]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+ A user agent that supports both this specification and Netscape-style
+ cookies SHOULD send a Cookie request header that follows the older
+ Netscape specification if it received the cookie in a Set-Cookie
+ response header and not in a Set-Cookie2 response header. However,
+ it SHOULD send the following request header as well:
+
+ Cookie2: $Version="1"
+
+ The Cookie2 header advises the server that the user agent understands
+ new-style cookies. If the server understands new-style cookies, as
+ well, it SHOULD continue the stateful session by sending a Set-
+ Cookie2 response header, rather than Set-Cookie. A server that does
+ not understand new-style cookies will simply ignore the Cookie2
+ request header.
+
+9.2 Caching and HTTP/1.0
+
+ Some caches, such as those conforming to HTTP/1.0, will inevitably
+ cache the Set-Cookie2 and Set-Cookie headers, because there was no
+ mechanism to suppress caching of headers prior to HTTP/1.1. This
+ caching can lead to security problems. Documents transmitted by an
+ origin server along with Set-Cookie2 and Set-Cookie headers usually
+ either will be uncachable, or will be "pre-expired". As long as
+ caches obey instructions not to cache documents (following Expires:
+ <a date in the past> or Pragma: no-cache (HTTP/1.0), or Cache-
+ control: no-cache (HTTP/1.1)) uncachable documents present no
+ problem. However, pre-expired documents may be stored in caches.
+ They require validation (a conditional GET) on each new request, but
+ some cache operators loosen the rules for their caches, and sometimes
+ serve expired documents without first validating them. This
+ combination of factors can lead to cookies meant for one user later
+ being sent to another user. The Set-Cookie2 and Set-Cookie headers
+ are stored in the cache, and, although the document is stale
+ (expired), the cache returns the document in response to later
+ requests, including cached headers.
+
+10. ACKNOWLEDGEMENTS
+
+ This document really represents the collective efforts of the HTTP
+ Working Group of the IETF and, particularly, the following people, in
+ addition to the authors: Roy Fielding, Yaron Goland, Marc Hedlund,
+ Ted Hardie, Koen Holtman, Shel Kaphan, Rohit Khare, Foteos Macrides,
+ David W. Morris.
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 24]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+11. AUTHORS' ADDRESSES
+
+ David M. Kristol
+ Bell Laboratories, Lucent Technologies
+ 600 Mountain Ave. Room 2A-333
+ Murray Hill, NJ 07974
+
+ Phone: (908) 582-2250
+ Fax: (908) 582-1239
+ EMail: dmk@bell-labs.com
+
+
+ Lou Montulli
+ Epinions.com, Inc.
+ 2037 Landings Dr.
+ Mountain View, CA 94301
+
+ EMail: lou@montulli.org
+
+12. REFERENCES
+
+ [DMK95] Kristol, D.M., "Proposed HTTP State-Info Mechanism",
+ available at <http://portal.research.bell-
+ labs.com/~dmk/state-info.html>, September, 1995.
+
+ [Netscape] "Persistent Client State -- HTTP Cookies", available at
+ <http://www.netscape.com/newsref/std/cookie_spec.html>,
+ undated.
+
+ [RFC2109] Kristol, D. and L. Montulli, "HTTP State Management
+ Mechanism", RFC 2109, February 1997.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2279] Yergeau, F., "UTF-8, a transformation format of Unicode
+ and ISO-10646", RFC 2279, January 1998.
+
+ [RFC2396] Berners-Lee, T., Fielding, R. and L. Masinter, "Uniform
+ Resource Identifiers (URI): Generic Syntax", RFC 2396,
+ August 1998.
+
+ [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H. and T.
+ Berners-Lee, "Hypertext Transfer Protocol -- HTTP/1.1",
+ RFC 2616, June 1999.
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 25]
+
+RFC 2965 HTTP State Management Mechanism October 2000
+
+
+13. Full Copyright Statement
+
+ Copyright (C) The Internet Society (2000). All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that the above copyright notice and this paragraph are
+ included on all such copies and derivative works. However, this
+ document itself may not be modified in any way, such as by removing
+ the copyright notice or references to the Internet Society or other
+ Internet organizations, except as needed for the purpose of
+ developing Internet standards in which case the procedures for
+ copyrights defined in the Internet Standards process must be
+ followed, or as required to translate it into languages other than
+ English.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked by the Internet Society or its successors or assigns.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+ TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+ BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+ HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+ MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+Acknowledgement
+
+ Funding for the RFC Editor function is currently provided by the
+ Internet Society.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Kristol & Montulli Standards Track [Page 26]
+
diff --git a/kioslave/http/kcookiejar/tests/Makefile.am b/kioslave/http/kcookiejar/tests/Makefile.am
new file mode 100644
index 000000000..b79dd10fb
--- /dev/null
+++ b/kioslave/http/kcookiejar/tests/Makefile.am
@@ -0,0 +1,18 @@
+# $Id$
+# Makefile.am of kdebase/kioslave/http
+
+INCLUDES= $(all_includes)
+
+####### Files
+
+check_PROGRAMS = kcookiejartest
+
+kcookiejartest_SOURCES = kcookiejartest.cpp
+kcookiejartest_LDADD = $(LIB_KIO)
+kcookiejartest_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+check-local: kcookiejartest
+ ./kcookiejartest $(srcdir)/cookie.test
+ ./kcookiejartest $(srcdir)/cookie_rfc.test
+ ./kcookiejartest $(srcdir)/cookie_saving.test
+ ./kcookiejartest $(srcdir)/cookie_settings.test
diff --git a/kioslave/http/kcookiejar/tests/cookie.test b/kioslave/http/kcookiejar/tests/cookie.test
new file mode 100644
index 000000000..6619bf82d
--- /dev/null
+++ b/kioslave/http/kcookiejar/tests/cookie.test
@@ -0,0 +1,162 @@
+## Check setting of cookies
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
+CHECK http://w.y.z/ Cookie: some_value=value1
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value2; Path="/"
+CHECK http://a.b.c/ Cookie: some_value=value2
+## Check if clearing cookie jar works
+CLEAR COOKIES
+CHECK http://w.y.z/
+CHECK http://a.b.c/
+## Check cookie syntax
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value with spaces
+CHECK http://w.y.z/ Cookie: some_value=value with spaces
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value="quoted value"
+CHECK http://a.b.c/ Cookie: some_value="quoted value"
+# Without a = sign, the cookie gets interpreted as the value for a cookie with no name
+# This is what IE and Netscape does
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value
+CHECK http://a.b.c/ Cookie: some_value; some_value="quoted value"
+COOKIE ASK http://a.b.c/ Set-Cookie: some_other_value
+CHECK http://a.b.c/ Cookie: some_other_value; some_value="quoted value"
+CLEAR COOKIES
+# This doesn't work with old-style netscape cookies, it should work with RFC2965 cookies
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value="quoted value; and such"
+# IE & Netscape does this:
+CHECK http://a.b.c/ Cookie: some_value="quoted value
+# Mozilla does:
+# CHECK http://a.b.c/ Cookie: some_value="quoted value; and such"
+# COOKIE ASK http://a.b.c/ Set-Cookie: some_value="quoted value;
+# CHECK http://a.b.c/ Cookie: some_value=
+# Note that we parse RFC2965 cookies like Mozilla does
+CLEAR COOKIES
+## Check if deleting cookies works
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
+CHECK http://w.y.z/ Cookie: some_value=value1
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%LASTYEAR%
+CHECK http://w.y.z/
+## Check if updating cookies works
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value2; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
+CHECK http://w.y.z/ Cookie: some_value=value3
+## Check if multiple cookies work
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value2=foobar; Path="/"; expires=%NEXTYEAR%
+CHECK http://w.y.z/ Cookie: some_value2=foobar; some_value=value3
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=; Path="/"; expires=%LASTYEAR%
+CHECK http://w.y.z/ Cookie: some_value2=foobar
+CLEAR COOKIES
+## Check if path restrictions work
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
+CHECK http://w.y.z/
+CHECK http://w.y.z/Foo Cookie: some_value=value1
+CHECK http://w.y.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y.z/Foo/bar Cookie: some_value=value1
+CLEAR COOKIES
+## Check if default path works
+# RFC2965 says that we should default to the URL path, but netscape cookies default to /
+COOKIE ASK http://w.y.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
+CHECK http://w.y.z/
+CHECK http://w.y.z/Foo Cookie: some_value=value1
+CHECK http://w.y.z/FooBar
+CHECK http://w.y.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y.z/Foo/bar Cookie: some_value=value1
+CLEAR COOKIES
+## Check if cookies are correctly ordered based on path
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value2=value2; Path="/Foo/Bar"; expires=%NEXTYEAR%
+CHECK http://w.y.z/Foo/Bar Cookie: some_value2=value2; some_value=value1
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%
+CHECK http://w.y.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
+CLEAR COOKIES
+## Check cookies with same name but different paths
+COOKIE ASK http://w.y.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
+COOKIE ASK http://w.y.z/Bar/ Set-Cookie: some_value=value2; expires=%NEXTYEAR%
+CHECK http://w.y.z/Foo/Bar Cookie: some_value=value1
+CHECK http://w.y.z/Bar/Foo Cookie: some_value=value2
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value3; expires=%NEXTYEAR%
+CHECK http://w.y.z/Foo/Bar Cookie: some_value=value1; some_value=value3
+## Check secure cookie handling
+COOKIE ASK https://secure.y.z/ Set-Cookie: some_value2=value2; Path="/"; expires=%NEXTYEAR%; secure
+CHECK https://secure.y.z/Foo/bar Cookie: some_value2=value2
+CHECK http://secure.y.z/Foo/bar
+CLEAR COOKIES
+COOKIE ASK http://secure.y.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%; secure
+CHECK https://secure.y.z/Foo/bar Cookie: some_value3=value3
+CHECK http://secure.y.z/Foo/bar
+CLEAR COOKIES
+## Check domain restrictions #1
+COOKIE ASK http://www.acme.com/ Set-Cookie: some_value=value1; Domain=".acme.com"; expires=%NEXTYEAR%
+CHECK http://www.acme.com/ Cookie: some_value=value1
+CHECK http://www.abc.com/
+CHECK http://frop.acme.com/ Cookie: some_value=value1
+CLEAR COOKIES
+## Check domain restrictions #2
+COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; Domain=".novell.com"; expires=%NEXTYEAR%
+CHECK http://novell.com/ Cookie: some_value=value1
+CHECK http://www.novell.com/ Cookie: some_value=value1
+CLEAR COOKIES
+COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; Domain="novell.com"; expires=%NEXTYEAR%
+CHECK http://novell.com/ Cookie: some_value=value1
+CHECK http://www.novell.com/ Cookie: some_value=value1
+CLEAR COOKIES
+## Check domain restrictions #3
+COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
+CHECK http://novell.com/ Cookie: some_value=value1
+# FIXME: Allegedly IE sends cookies to sub-domains as well!
+# See e.g. https://bugzilla.mozilla.org/show_bug.cgi?id=223027
+CHECK http://www.novell.com/
+CLEAR COOKIES
+## Check domain restrictions #4
+COOKIE ASK http://novell.com/ Set-Cookie: some_value=value1; Domain=".com"; expires=%NEXTYEAR%
+CHECK http://novell.com/ Cookie: some_value=value1
+# If the specified domain is too broad, we default to host only
+CHECK http://www.novell.com/
+CHECK http://com/
+CHECK http://sun.com/
+## Check domain restrictions #5
+CLEAR COOKIES
+COOKIE ASK http://novell.co.uk/ Set-Cookie: some_value=value1; Domain=".co.uk"; expires=%NEXTYEAR%
+CHECK http://novell.co.uk/ Cookie: some_value=value1
+# If the specified domain is too broad, we default to host only
+CHECK http://www.novell.co.uk/
+CHECK http://co.uk/
+CHECK http://sun.co.uk/
+COOKIE ASK http://x.y.z.foobar.com/ Set-Cookie: set_by=x.y.z.foobar.com; Domain=".foobar.com"; expires=%NEXTYEAR%
+CHECK http://x.y.z.foobar.com/ Cookie: set_by=x.y.z.foobar.com
+CHECK http://y.z.foobar.com/ Cookie: set_by=x.y.z.foobar.com
+CHECK http://z.foobar.com/ Cookie: set_by=x.y.z.foobar.com
+CHECK http://www.foobar.com/ Cookie: set_by=x.y.z.foobar.com
+CHECK http://foobar.com/ Cookie: set_by=x.y.z.foobar.com
+CLEAR COOKIES
+## Check domain restrictions #6
+COOKIE ASK http://x.y.z.frop.com/ Set-Cookie: set_by=x.y.z.frop.com; Domain=".foobar.com"; expires=%NEXTYEAR%
+COOKIE ASK http://x.y.z.frop.com/ Set-Cookie: set_by2=x.y.z.frop.com; Domain=".com"; expires=%NEXTYEAR%
+CHECK http://x.y.z.foobar.com/
+CHECK http://y.z.foobar.com/
+CHECK http://z.foobar.com/
+CHECK http://www.foobar.com/
+CHECK http://foobar.com/
+CLEAR COOKIES
+## Check domain restrictions #7
+COOKIE ASK http://frop.com/ Set-Cookie: set_by=x.y.z.frop.com; Domain=".foobar.com"; expires=%NEXTYEAR%
+COOKIE ASK http://frop.com/ Set-Cookie: set_by2=x.y.z.frop.com; Domain=".com"; expires=%NEXTYEAR%
+CHECK http://x.y.z.foobar.com/
+CHECK http://y.z.foobar.com/
+CHECK http://z.foobar.com/
+CHECK http://www.foobar.com/
+CHECK http://foobar.com/
+CLEAR COOKIES
+## Check domain restrictions #8
+CONFIG AcceptSessionCookies true
+COOKIE ACCEPT http://www.foobar.com Set-Cookie: from=foobar.com; domain=bar.com; Path="/"
+CHECK http://bar.com
+CLEAR COOKIES
+## Check cookies with IP address hostnames
+COOKIE ASK http://192.168.0.1 Set-Cookie: name1=value1; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://192.168.0.1 Set-Cookie: name11=value11; domain="test.local"; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://192.168.0.1:8080 Set-Cookie: name2=value2; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK https://192.168.0.1 Set-Cookie: name3=value3; Path="/"; expires=%NEXTYEAR%; secure
+CHECK http://192.168.0.1 Cookie: name11=value11; name1=value1
+CHECK http://192.168.0.1:8080 Cookie: name2=value2
+CHECK https://192.168.0.1 Cookie: name3=value3; name11=value11; name1=value1
+CHECK http://192.168.0.10
+CHECK http://192.168.0
diff --git a/kioslave/http/kcookiejar/tests/cookie_rfc.test b/kioslave/http/kcookiejar/tests/cookie_rfc.test
new file mode 100644
index 000000000..e1d8a40de
--- /dev/null
+++ b/kioslave/http/kcookiejar/tests/cookie_rfc.test
@@ -0,0 +1,148 @@
+## Check setting of cookies
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
+# Although the examples in RFC2965 uses $Version="1" the syntax description suggests that
+# such quotes are not allowed, KDE BR59990 reports that the Sun Java server fails to handle
+# cookies that use $Version="1"
+CHECK http://w.y.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value="value2"; Version=1; Path="/"
+CHECK http://a.b.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
+## Check if clearing cookie jar works
+CLEAR COOKIES
+CHECK http://w.y.z/
+CHECK http://a.b.c/
+## Check cookie syntax
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value="value with spaces"; Version=1
+CHECK http://w.y.z/ Cookie: $Version=1; some_value="value with spaces"
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value ="extra space 1"; Version=1
+CHECK http://w.y.z/ Cookie: $Version=1; some_value="extra space 1"
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value= "extra space 2"; Version=1
+CHECK http://w.y.z/ Cookie: $Version=1; some_value="extra space 2"
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=unquoted; Version=1
+CHECK http://a.b.c/ Cookie: $Version=1; some_value=unquoted
+# Note that we parse this different for Netscape-style cookies!
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value="quoted value; and such"; Version=1;
+CHECK http://a.b.c/ Cookie: $Version=1; some_value="quoted value; and such"
+CLEAR COOKIES
+## Check if deleting cookies works #1
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
+CHECK http://w.y.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Path="/"; Max-Age=0
+CHECK http://w.y.z/
+## Check if updating cookies works
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value2; Version=1; Path="/"; Max-Age=3600
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Max-Age=3600
+CHECK http://w.y.z/ Cookie: $Version=1; some_value=value3; $Path="/"
+## Check if multiple cookies work
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value2=foobar; Version=1; Path="/"; Max-Age=3600
+CHECK http://w.y.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"; some_value=value3; $Path="/"
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=; Version=1; Path="/"; Max-Age=0
+CHECK http://w.y.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
+CLEAR COOKIES
+## Check if we prepend domain with a dot
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value2; Version=1; Path="/"; Domain=.y.z; Max-Age=3600
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Domain=y.z.; Max-Age=3600
+CHECK http://w.y.z/ Cookie: $Version=1; some_value=value3; $Path="/"; $Domain=".y.z"
+CLEAR COOKIES
+## Check if multiple cookies on a single line work
+## FIXME
+#COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Max-Age=3600, some_value2=foobar; Version=1; Path="/"; Max-Age=3600
+# CHECK http://w.y.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"; some_value=value3; $Path="/"
+# COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=; Version=1; Path="/"; Max-Age=0
+# CHECK http://w.y.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
+CLEAR COOKIES
+## Check if path restrictions work
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
+CHECK http://w.y.z/
+CHECK http://w.y.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CLEAR COOKIES
+## Check if default path works
+# RFC2965 says that we should default to the URL path
+COOKIE ASK http://w.y.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
+CHECK http://w.y.z/
+CHECK http://w.y.z/Foo Cookie: $Version=1; some_value=value1
+CHECK http://w.y.z/FooBar
+CHECK http://w.y.z/Foo/ Cookie: $Version=1; some_value=value1
+CHECK http://w.y.z/Foo/bar Cookie: $Version=1; some_value=value1
+CLEAR COOKIES
+## Check if cookies are correctly ordered based on path
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/Foo/Bar"; Max-Age=3600
+CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600
+CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
+CLEAR COOKIES
+## Check cookies with same name but different paths
+COOKIE ASK http://w.y.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
+COOKIE ASK http://w.y.z/Bar/ Set-Cookie2: some_value=value2; Version=1; Max-Age=3600
+CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value=value1
+CHECK http://w.y.z/Bar/Foo Cookie: $Version=1; some_value=value2
+COOKIE ASK http://w.y.z/ Set-Cookie2: some_value=value3; Version=1; Max-Age=3600
+CHECK http://w.y.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
+## Check secure cookie handling
+COOKIE ASK https://secure.y.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/"; Max-Age=3600; Secure
+CHECK https://secure.y.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
+CHECK http://secure.y.z/Foo/bar
+CLEAR COOKIES
+COOKIE ASK http://secure.y.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600; Secure
+CHECK https://secure.y.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
+CHECK http://secure.y.z/Foo/bar
+CLEAR COOKIES
+## Check domain restrictions #1
+COOKIE ASK http://www.acme.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".acme.com"; Max-Age=3600
+CHECK http://www.acme.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme.com"
+CHECK http://www.abc.com/
+CHECK http://frop.acme.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme.com"
+CLEAR COOKIES
+## Check domain restrictions #2
+COOKIE ASK http://novell.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".novell.com"; Max-Age=3600
+CHECK http://novell.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell.com"
+CHECK http://www.novell.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell.com"
+CLEAR COOKIES
+## Check domain restrictions #3
+COOKIE ASK http://novell.com/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
+CHECK http://novell.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell.com/
+CLEAR COOKIES
+## Check domain restrictions #4
+COOKIE ASK http://novell.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".com"; Max-Age=3600
+# If the specified domain is too broad, we ignore the Domain
+# FIXME: RFC2965 says we should ignore the cookie completely
+CHECK http://novell.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell.com/
+CHECK http://com/
+CHECK http://sun.com/
+## Check domain restrictions #5
+CLEAR COOKIES
+COOKIE ASK http://novell.co.uk/ Set-Cookie2: some_value=value1; Version=1; Domain=".co.uk"; Max-Age=3600
+# If the specified domain is too broad, we default to host only
+# FIXME: RFC2965 says we should ignore the cookie completely
+CHECK http://novell.co.uk/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell.co.uk/
+CHECK http://co.uk/
+CHECK http://sun.co.uk/
+COOKIE ASK http://x.y.z.foobar.com/ Set-Cookie2: set_by=x.y.z.foobar.com; Version=1; Domain=".foobar.com"; Max-Age=3600
+CHECK http://x.y.z.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
+CHECK http://y.z.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
+CHECK http://z.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
+CHECK http://www.foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
+CHECK http://foobar.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar.com"
+CLEAR COOKIES
+## Check domain restrictions #6
+COOKIE ASK http://x.y.z.frop.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
+COOKIE ASK http://x.y.z.frop.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
+CHECK http://x.y.z.foobar.com/
+CHECK http://y.z.foobar.com/
+CHECK http://z.foobar.com/
+CHECK http://www.foobar.com/
+CHECK http://foobar.com/
+CLEAR COOKIES
+## Check domain restrictions #7
+COOKIE ASK http://frop.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
+COOKIE ASK http://frop.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
+CHECK http://x.y.z.foobar.com/
+CHECK http://y.z.foobar.com/
+CHECK http://z.foobar.com/
+CHECK http://www.foobar.com/
+CHECK http://foobar.com/
diff --git a/kioslave/http/kcookiejar/tests/cookie_saving.test b/kioslave/http/kcookiejar/tests/cookie_saving.test
new file mode 100644
index 000000000..cb9f34c42
--- /dev/null
+++ b/kioslave/http/kcookiejar/tests/cookie_saving.test
@@ -0,0 +1,430 @@
+## Check setting of cookies
+COOKIE ASK http://w.y.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value2; Path="/"
+## Check if clearing cookie jar works
+CLEAR COOKIES
+## Check cookie syntax
+COOKIE ASK http://w.y1.z/ Set-Cookie: some_value=value with spaces; expires=%NEXTYEAR%
+COOKIE ASK http://a.b1.c/ Set-Cookie: some_value="quoted value"; expires=%NEXTYEAR%
+# Without a = sign, the cookie gets interpreted as the value for a cookie with no name
+# This is what IE and Netscape does
+COOKIE ASK http://a.b1.c/ Set-Cookie: some_value
+COOKIE ASK http://a.b1.c/ Set-Cookie: some_other_value; expires=%NEXTYEAR%
+# This doesn't work with old-style netscape cookies, it should work with RFC2965 cookies
+COOKIE ASK http://a.b2.c/ Set-Cookie: some_value="quoted value; and such"; expires=%NEXTYEAR%
+# IE & Netscape does this:
+## Check if deleting cookies works
+COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value1; Path="/"; expires=%LASTYEAR%
+## Check if updating cookies works
+COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value2; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
+## Check if multiple cookies work
+COOKIE ASK http://w.y3.z/ Set-Cookie: some_value2=foobar; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://w.y3.z/ Set-Cookie: some_value=; Path="/"; expires=%LASTYEAR%
+## Check if path restrictions work
+COOKIE ASK http://w.y4.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
+## Check if default path works
+COOKIE ASK http://w.y5.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
+## Check if cookies are correctly ordered based on path
+COOKIE ASK http://w.y6.z/ Set-Cookie: some_value=value1; Path="/Foo"; expires=%NEXTYEAR%
+COOKIE ASK http://w.y6.z/ Set-Cookie: some_value2=value2; Path="/Foo/Bar"; expires=%NEXTYEAR%
+COOKIE ASK http://w.y6.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%
+## Check cookies with same name but different paths
+COOKIE ASK http://w.y7.z/Foo/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
+COOKIE ASK http://w.y7.z/Bar/ Set-Cookie: some_value=value2; expires=%NEXTYEAR%
+COOKIE ASK http://w.y7.z/ Set-Cookie: some_value=value3; expires=%NEXTYEAR%
+## Check secure cookie handling
+COOKIE ASK https://secure.y7.z/ Set-Cookie: some_value2=value2; Path="/"; expires=%NEXTYEAR%; secure
+COOKIE ASK http://secure.y8.z/ Set-Cookie: some_value3=value3; Path="/"; expires=%NEXTYEAR%; secure
+## Check domain restrictions #1
+COOKIE ASK http://www.acme9.com/ Set-Cookie: some_value=value1; Domain=".acme9.com"; expires=%NEXTYEAR%
+## Check domain restrictions #2
+COOKIE ASK http://novell10.com/ Set-Cookie: some_value=value1; Domain=".novell10.com"; expires=%NEXTYEAR%
+COOKIE ASK http://novell11.com/ Set-Cookie: some_value=value1; Domain="novell11.com"; expires=%NEXTYEAR%
+## Check domain restrictions #3
+COOKIE ASK http://novell12.com/ Set-Cookie: some_value=value1; expires=%NEXTYEAR%
+## Check domain restrictions #4
+COOKIE ASK http://novell13.com/ Set-Cookie: some_value=value1; Domain=".com"; expires=%NEXTYEAR%
+# If the specified domain is too broad, we default to host only
+## Check domain restrictions #5
+COOKIE ASK http://novell14.co.uk/ Set-Cookie: some_value=value1; Domain=".co.uk"; expires=%NEXTYEAR%
+COOKIE ASK http://x.y.z.foobar14.com/ Set-Cookie: set_by=x.y.z.foobar14.com; Domain=".foobar14.com"; expires=%NEXTYEAR%
+## Check domain restrictions #6
+COOKIE ASK http://x.y.z.frop15.com/ Set-Cookie: set_by=x.y.z.frop15.com; Domain=".foobar15.com"; expires=%NEXTYEAR%
+COOKIE ASK http://x.y.z.frop15.com/ Set-Cookie: set_by2=x.y.z.frop15.com; Domain=".com"; expires=%NEXTYEAR%
+## Check domain restrictions #7
+COOKIE ASK http://frop16.com/ Set-Cookie: set_by=x.y.z.frop16.com; Domain=".foobar16.com"; expires=%NEXTYEAR%
+COOKIE ASK http://frop16.com/ Set-Cookie: set_by2=x.y.z.frop16.com; Domain=".com"; expires=%NEXTYEAR%
+## RFC Cookies
+## Check setting of cookies
+COOKIE ASK http://w.y20.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
+# Although the examples in RFC2965 uses $Version="1" the syntax description suggests that
+# such quotes are not allowed, KDE BR59990 reports that the Sun Java server fails to handle
+# cookies that use $Version="1"
+COOKIE ASK http://a.b20.c/ Set-Cookie2: some_value="value2"; Version=1; Path="/"; Max-Age=3600
+## Check cookie syntax
+COOKIE ASK http://w.y21.z/ Set-Cookie2: some_value="value with spaces"; Version=1; Max-Age=3600
+COOKIE ASK http://w.y21.z/ Set-Cookie2: some_value ="extra space 1"; Version=1; Max-Age=3600
+COOKIE ASK http://w.y21.z/ Set-Cookie2: some_value= "extra space 2"; Version=1; Max-Age=3600
+COOKIE ASK http://a.b21.c/ Set-Cookie2: some_value=unquoted; Version=1; Max-Age=3600
+# Note that we parse this different for Netscape-style cookies!
+COOKIE ASK http://a.b21.c/ Set-Cookie2: some_value="quoted value; and such"; Version=1; Max-Age=3600
+## Check if deleting cookies works #1
+COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value="value1"; Version=1; Path="/"; Max-Age=3600
+COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=value1; Version=1; Path="/"; Max-Age=0
+## Check if updating cookies works
+COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=value2; Version=1; Path="/"; Max-Age=3600
+COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=value3; Version=1; Path="/"; Max-Age=3600
+## Check if multiple cookies work
+COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value2=foobar; Version=1; Path="/"; Max-Age=3600
+COOKIE ASK http://w.y22.z/ Set-Cookie2: some_value=; Version=1; Path="/"; Max-Age=0
+## Check if path restrictions work
+COOKIE ASK http://w.y23.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
+## Check if default path works
+# RFC2965 says that we should default to the URL path
+COOKIE ASK http://w.y24.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
+## Check if cookies are correctly ordered based on path
+COOKIE ASK http://w.y25.z/ Set-Cookie2: some_value=value1; Version=1; Path="/Foo"; Max-Age=3600
+COOKIE ASK http://w.y25.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/Foo/Bar"; Max-Age=3600
+COOKIE ASK http://w.y25.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600
+## Check cookies with same name but different paths
+COOKIE ASK http://w.y26.z/Foo/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
+COOKIE ASK http://w.y26.z/Bar/ Set-Cookie2: some_value=value2; Version=1; Max-Age=3600
+COOKIE ASK http://w.y26.z/ Set-Cookie2: some_value=value3; Version=1; Max-Age=3600
+## Check secure cookie handling
+COOKIE ASK https://secure.y26.z/ Set-Cookie2: some_value2=value2; Version=1; Path="/"; Max-Age=3600; Secure
+COOKIE ASK http://secure.y27.z/ Set-Cookie2: some_value3=value3; Version=1; Path="/"; Max-Age=3600; Secure
+## Check domain restrictions #1
+COOKIE ASK http://www.acme28.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".acme28.com"; Max-Age=3600
+## Check domain restrictions #2
+COOKIE ASK http://novell29.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".novell29.com"; Max-Age=3600
+## Check domain restrictions #3
+COOKIE ASK http://novell30.com/ Set-Cookie2: some_value=value1; Version=1; Max-Age=3600
+## Check domain restrictions #4
+COOKIE ASK http://novell31.com/ Set-Cookie2: some_value=value1; Version=1; Domain=".com"; Max-Age=3600
+# If the specified domain is too broad, we ignore the Domain
+# FIXME: RFC2965 says we should ignore the cookie completely
+## Check domain restrictions #5
+COOKIE ASK http://novell32.co.uk/ Set-Cookie2: some_value=value1; Version=1; Domain=".co.uk"; Max-Age=3600
+# If the specified domain is too broad, we default to host only
+# FIXME: RFC2965 says we should ignore the cookie completely
+COOKIE ASK http://x.y.z.foobar33.com/ Set-Cookie2: set_by=x.y.z.foobar.com; Version=1; Domain=".foobar33.com"; Max-Age=3600
+## Check domain restrictions #6
+COOKIE ASK http://x.y.z.frop34.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
+COOKIE ASK http://x.y.z.frop34.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
+## Check domain restrictions #7
+COOKIE ASK http://frop35.com/ Set-Cookie2: set_by=x.y.z.frop.com; Version=1; Domain=".foobar.com"; Max-Age=3600
+COOKIE ASK http://frop35.com/ Set-Cookie2: set_by2=x.y.z.frop.com; Version=1; Domain=".com"; Max-Age=3600
+
+## Check results
+CHECK http://w.y.z/
+CHECK http://a.b.c/
+CHECK http://w.y1.z/ Cookie: some_value=value with spaces
+CHECK http://a.b1.c/ Cookie: some_other_value; some_value="quoted value"
+CHECK http://a.b2.c/ Cookie: some_value="quoted value
+CHECK http://w.y3.z/ Cookie: some_value2=foobar
+CHECK http://w.y4.z/
+CHECK http://w.y4.z/Foo Cookie: some_value=value1
+CHECK http://w.y4.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y4.z/Foo/bar Cookie: some_value=value1
+CHECK http://w.y5.z/
+CHECK http://w.y5.z/Foo Cookie: some_value=value1
+CHECK http://w.y5.z/FooBar
+CHECK http://w.y5.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y5.z/Foo/bar Cookie: some_value=value1
+CHECK http://w.y6.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
+CHECK http://w.y7.z/Bar/Foo Cookie: some_value=value2; some_value=value3
+CHECK http://w.y7.z/Foo/Bar Cookie: some_value=value1; some_value=value3
+CHECK https://secure.y7.z/Foo/bar Cookie: some_value2=value2
+CHECK http://secure.y7.z/Foo/bar
+CHECK https://secure.y8.z/Foo/bar Cookie: some_value3=value3
+CHECK http://secure.y8.z/Foo/bar
+CHECK http://www.acme9.com/ Cookie: some_value=value1
+CHECK http://www.abc9.com/
+CHECK http://frop.acme9.com/ Cookie: some_value=value1
+CHECK http://novell10.com/ Cookie: some_value=value1
+CHECK http://www.novell10.com/ Cookie: some_value=value1
+CHECK http://novell11.com/ Cookie: some_value=value1
+CHECK http://www.novell11.com/ Cookie: some_value=value1
+CHECK http://novell12.com/ Cookie: some_value=value1
+CHECK http://www.novell12.com/
+CHECK http://novell13.com/ Cookie: some_value=value1
+CHECK http://www.novell13.com/
+CHECK http://com/
+CHECK http://sun13.com/
+CHECK http://novell14.co.uk/ Cookie: some_value=value1
+CHECK http://www.novell14.co.uk/
+CHECK http://co.uk/
+CHECK http://sun14.co.uk/
+CHECK http://x.y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://www.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://x.y.z.foobar15.com/
+CHECK http://y.z.foobar15.com/
+CHECK http://z.foobar15.com/
+CHECK http://www.foobar15.com/
+CHECK http://foobar15.com/
+CHECK http://x.y.z.foobar16.com/
+CHECK http://y.z.foobar16.com/
+CHECK http://z.foobar16.com/
+CHECK http://www.foobar16.com/
+CHECK http://foobar16.com/
+## Check results for RFC cookies
+CHECK http://w.y20.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
+CHECK http://a.b20.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
+CHECK http://w.y21.z/ Cookie: $Version=1; some_value="extra space 2"
+CHECK http://a.b21.c/ Cookie: $Version=1; some_value="quoted value; and such"
+CHECK http://w.y22.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
+CHECK http://w.y23.z/
+CHECK http://w.y23.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y23.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y23.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y24.z/
+CHECK http://w.y24.z/Foo Cookie: $Version=1; some_value=value1
+CHECK http://w.y24.z/FooBar
+CHECK http://w.y24.z/Foo/ Cookie: $Version=1; some_value=value1
+CHECK http://w.y24.z/Foo/bar Cookie: $Version=1; some_value=value1
+CHECK http://w.y25.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
+CHECK http://w.y26.z/Bar/Foo Cookie: $Version=1; some_value=value2; some_value=value3
+CHECK http://w.y26.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
+CHECK https://secure.y26.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
+CHECK http://secure.y26.z/Foo/bar
+CHECK https://secure.y27.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
+CHECK http://secure.y27.z/Foo/bar
+CHECK http://www.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
+CHECK http://www.abc28.com/
+CHECK http://frop.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
+CHECK http://novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
+CHECK http://www.novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
+CHECK http://novell30.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell30.com/
+CHECK http://novell31.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell31.com/
+CHECK http://com/
+CHECK http://sun31.com/
+CHECK http://novell32.co.uk/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell32.co.uk/
+CHECK http://co.uk/
+CHECK http://sun32.co.uk/
+CHECK http://x.y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://www.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://x.y.z.foobar.com/
+CHECK http://y.z.foobar.com/
+CHECK http://z.foobar.com/
+CHECK http://www.foobar.com/
+CHECK http://foobar.com/
+
+
+SAVE
+## Check result after saving
+CHECK http://w.y.z/
+CHECK http://a.b.c/
+CHECK http://w.y1.z/ Cookie: some_value=value with spaces
+CHECK http://a.b1.c/ Cookie: some_other_value; some_value="quoted value"
+CHECK http://a.b2.c/ Cookie: some_value="quoted value
+CHECK http://w.y3.z/ Cookie: some_value2=foobar
+CHECK http://w.y4.z/
+CHECK http://w.y4.z/Foo Cookie: some_value=value1
+CHECK http://w.y4.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y4.z/Foo/bar Cookie: some_value=value1
+CHECK http://w.y5.z/
+CHECK http://w.y5.z/Foo Cookie: some_value=value1
+CHECK http://w.y5.z/FooBar
+CHECK http://w.y5.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y5.z/Foo/bar Cookie: some_value=value1
+CHECK http://w.y6.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
+CHECK http://w.y7.z/Bar/Foo Cookie: some_value=value2; some_value=value3
+CHECK http://w.y7.z/Foo/Bar Cookie: some_value=value1; some_value=value3
+CHECK https://secure.y7.z/Foo/bar Cookie: some_value2=value2
+CHECK http://secure.y7.z/Foo/bar
+CHECK https://secure.y8.z/Foo/bar Cookie: some_value3=value3
+CHECK http://secure.y8.z/Foo/bar
+CHECK http://www.acme9.com/ Cookie: some_value=value1
+CHECK http://www.abc9.com/
+CHECK http://frop.acme9.com/ Cookie: some_value=value1
+CHECK http://novell10.com/ Cookie: some_value=value1
+CHECK http://www.novell10.com/ Cookie: some_value=value1
+CHECK http://novell11.com/ Cookie: some_value=value1
+CHECK http://www.novell11.com/ Cookie: some_value=value1
+CHECK http://novell12.com/ Cookie: some_value=value1
+CHECK http://www.novell12.com/
+CHECK http://novell13.com/ Cookie: some_value=value1
+CHECK http://www.novell13.com/
+CHECK http://com/
+CHECK http://sun13.com/
+CHECK http://novell14.co.uk/ Cookie: some_value=value1
+CHECK http://www.novell14.co.uk/
+CHECK http://co.uk/
+CHECK http://sun14.co.uk/
+CHECK http://x.y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://www.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://x.y.z.foobar15.com/
+CHECK http://y.z.foobar15.com/
+CHECK http://z.foobar15.com/
+CHECK http://www.foobar15.com/
+CHECK http://foobar15.com/
+CHECK http://x.y.z.foobar16.com/
+CHECK http://y.z.foobar16.com/
+CHECK http://z.foobar16.com/
+CHECK http://www.foobar16.com/
+CHECK http://foobar16.com/
+## Check result for RFC cookies after saving
+CHECK http://w.y20.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
+CHECK http://a.b20.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
+CHECK http://w.y21.z/ Cookie: $Version=1; some_value="extra space 2"
+CHECK http://a.b21.c/ Cookie: $Version=1; some_value="quoted value; and such"
+CHECK http://w.y22.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
+CHECK http://w.y23.z/
+CHECK http://w.y23.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y23.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y23.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y24.z/
+CHECK http://w.y24.z/Foo Cookie: $Version=1; some_value=value1
+CHECK http://w.y24.z/FooBar
+CHECK http://w.y24.z/Foo/ Cookie: $Version=1; some_value=value1
+CHECK http://w.y24.z/Foo/bar Cookie: $Version=1; some_value=value1
+CHECK http://w.y25.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
+CHECK http://w.y26.z/Bar/Foo Cookie: $Version=1; some_value=value2; some_value=value3
+CHECK http://w.y26.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
+CHECK https://secure.y26.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
+CHECK http://secure.y26.z/Foo/bar
+CHECK https://secure.y27.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
+CHECK http://secure.y27.z/Foo/bar
+CHECK http://www.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
+CHECK http://www.abc28.com/
+CHECK http://frop.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
+CHECK http://novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
+CHECK http://www.novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
+CHECK http://novell30.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell30.com/
+CHECK http://novell31.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell31.com/
+CHECK http://com/
+CHECK http://sun31.com/
+CHECK http://novell32.co.uk/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell32.co.uk/
+CHECK http://co.uk/
+CHECK http://sun32.co.uk/
+CHECK http://x.y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://www.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://x.y.z.foobar.com/
+CHECK http://y.z.foobar.com/
+CHECK http://z.foobar.com/
+CHECK http://www.foobar.com/
+CHECK http://foobar.com/
+
+SAVE
+## Check result after saving a second time
+CHECK http://w.y.z/
+CHECK http://a.b.c/
+CHECK http://w.y1.z/ Cookie: some_value=value with spaces
+CHECK http://a.b1.c/ Cookie: some_other_value; some_value="quoted value"
+CHECK http://a.b2.c/ Cookie: some_value="quoted value
+CHECK http://w.y3.z/ Cookie: some_value2=foobar
+CHECK http://w.y4.z/
+CHECK http://w.y4.z/Foo Cookie: some_value=value1
+CHECK http://w.y4.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y4.z/Foo/bar Cookie: some_value=value1
+CHECK http://w.y5.z/
+CHECK http://w.y5.z/Foo Cookie: some_value=value1
+CHECK http://w.y5.z/FooBar
+CHECK http://w.y5.z/Foo/ Cookie: some_value=value1
+CHECK http://w.y5.z/Foo/bar Cookie: some_value=value1
+CHECK http://w.y6.z/Foo/Bar Cookie: some_value2=value2; some_value=value1; some_value3=value3
+CHECK http://w.y7.z/Bar/Foo Cookie: some_value=value2; some_value=value3
+CHECK http://w.y7.z/Foo/Bar Cookie: some_value=value1; some_value=value3
+CHECK https://secure.y7.z/Foo/bar Cookie: some_value2=value2
+CHECK http://secure.y7.z/Foo/bar
+CHECK https://secure.y8.z/Foo/bar Cookie: some_value3=value3
+CHECK http://secure.y8.z/Foo/bar
+CHECK http://www.acme9.com/ Cookie: some_value=value1
+CHECK http://www.abc9.com/
+CHECK http://frop.acme9.com/ Cookie: some_value=value1
+CHECK http://novell10.com/ Cookie: some_value=value1
+CHECK http://www.novell10.com/ Cookie: some_value=value1
+CHECK http://novell11.com/ Cookie: some_value=value1
+CHECK http://www.novell11.com/ Cookie: some_value=value1
+CHECK http://novell12.com/ Cookie: some_value=value1
+CHECK http://www.novell12.com/
+CHECK http://novell13.com/ Cookie: some_value=value1
+CHECK http://www.novell13.com/
+CHECK http://com/
+CHECK http://sun13.com/
+CHECK http://novell14.co.uk/ Cookie: some_value=value1
+CHECK http://www.novell14.co.uk/
+CHECK http://co.uk/
+CHECK http://sun14.co.uk/
+CHECK http://x.y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://y.z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://z.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://www.foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://foobar14.com/ Cookie: set_by=x.y.z.foobar14.com
+CHECK http://x.y.z.foobar15.com/
+CHECK http://y.z.foobar15.com/
+CHECK http://z.foobar15.com/
+CHECK http://www.foobar15.com/
+CHECK http://foobar15.com/
+CHECK http://x.y.z.foobar16.com/
+CHECK http://y.z.foobar16.com/
+CHECK http://z.foobar16.com/
+CHECK http://www.foobar16.com/
+CHECK http://foobar16.com/
+## Check result for rfc cookies after saving a second time
+CHECK http://w.y20.z/ Cookie: $Version=1; some_value="value1"; $Path="/"
+CHECK http://a.b20.c/ Cookie: $Version=1; some_value="value2"; $Path="/"
+CHECK http://w.y21.z/ Cookie: $Version=1; some_value="extra space 2"
+CHECK http://a.b21.c/ Cookie: $Version=1; some_value="quoted value; and such"
+CHECK http://w.y22.z/ Cookie: $Version=1; some_value2=foobar; $Path="/"
+CHECK http://w.y23.z/
+CHECK http://w.y23.z/Foo Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y23.z/Foo/ Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y23.z/Foo/bar Cookie: $Version=1; some_value=value1; $Path="/Foo"
+CHECK http://w.y24.z/
+CHECK http://w.y24.z/Foo Cookie: $Version=1; some_value=value1
+CHECK http://w.y24.z/FooBar
+CHECK http://w.y24.z/Foo/ Cookie: $Version=1; some_value=value1
+CHECK http://w.y24.z/Foo/bar Cookie: $Version=1; some_value=value1
+CHECK http://w.y25.z/Foo/Bar Cookie: $Version=1; some_value2=value2; $Path="/Foo/Bar"; some_value=value1; $Path="/Foo"; some_value3=value3; $Path="/"
+CHECK http://w.y26.z/Bar/Foo Cookie: $Version=1; some_value=value2; some_value=value3
+CHECK http://w.y26.z/Foo/Bar Cookie: $Version=1; some_value=value1; some_value=value3
+CHECK https://secure.y26.z/Foo/bar Cookie: $Version=1; some_value2=value2; $Path="/"
+CHECK http://secure.y26.z/Foo/bar
+CHECK https://secure.y27.z/Foo/bar Cookie: $Version=1; some_value3=value3; $Path="/"
+CHECK http://secure.y27.z/Foo/bar
+CHECK http://www.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
+CHECK http://www.abc28.com/
+CHECK http://frop.acme28.com/ Cookie: $Version=1; some_value=value1; $Domain=".acme28.com"
+CHECK http://novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
+CHECK http://www.novell29.com/ Cookie: $Version=1; some_value=value1; $Domain=".novell29.com"
+CHECK http://novell30.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell30.com/
+CHECK http://novell31.com/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell31.com/
+CHECK http://com/
+CHECK http://sun31.com/
+CHECK http://novell32.co.uk/ Cookie: $Version=1; some_value=value1
+CHECK http://www.novell32.co.uk/
+CHECK http://co.uk/
+CHECK http://sun32.co.uk/
+CHECK http://x.y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://y.z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://z.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://www.foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://foobar33.com/ Cookie: $Version=1; set_by=x.y.z.foobar.com; $Domain=".foobar33.com"
+CHECK http://x.y.z.foobar.com/
+CHECK http://y.z.foobar.com/
+CHECK http://z.foobar.com/
+CHECK http://www.foobar.com/
+CHECK http://foobar.com/
diff --git a/kioslave/http/kcookiejar/tests/cookie_settings.test b/kioslave/http/kcookiejar/tests/cookie_settings.test
new file mode 100644
index 000000000..7fc1a03a7
--- /dev/null
+++ b/kioslave/http/kcookiejar/tests/cookie_settings.test
@@ -0,0 +1,116 @@
+## Check CookieGlobalAdvice setting
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value1; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value2; Path="/"
+CONFIG CookieGlobalAdvice Reject
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value3; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value4; Path="/"
+CONFIG CookieGlobalAdvice Accept
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value5; Path="/"; expires=%NEXTYEAR%
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value6; Path="/"
+CONFIG CookieGlobalAdvice Ask
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value7; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value8; Path="/"
+CONFIG AcceptSessionCookies true
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+# FIXME: Shouldn't this be considered a session cookie?
+# COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="0"
+# COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%LASTYEAR%
+# FIXME: The 'Discard' attribute makes the cookie a session cookie
+# COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+## Treat all cookies as session cookies
+CONFIG IgnoreExpirationDate true
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check host-based domain policies
+CONFIG IgnoreExpirationDate false
+CONFIG AcceptSessionCookies false
+CONFIG CookieDomainAdvice a.b.c:Reject
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check resetting of domain policies
+CONFIG CookieDomainAdvice
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check domain policies
+CONFIG CookieDomainAdvice .b.c:Reject
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check overriding of domain policies #1
+CONFIG CookieDomainAdvice .b.c:Reject,a.b.c:Accept
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check overriding of domain policies #2
+CONFIG CookieDomainAdvice a.b.c:Reject,.b.c:Accept
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE ACCEPT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ACCEPT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ACCEPT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ACCEPT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check resetting of domain policies
+CONFIG CookieDomainAdvice
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ASK http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ASK http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ASK http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ASK http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check overriding of domain policies #3
+CONFIG CookieDomainAdvice b.c:Reject,.b.c:Accept
+COOKIE REJECT http://b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE REJECT http://b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE REJECT http://b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+## Check overriding of domain policies #4
+CONFIG CookieDomainAdvice .a.b.c.d:Reject,.b.c.d:Accept,.c.d:Ask
+COOKIE REJECT http://www.a.b.c.d/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ACCEPT http://www.b.c.d/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE ASK http://www.c.d/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+## Check interaction with session policy
+CONFIG AcceptSessionCookies true
+CONFIG CookieDomainAdvice .b.c:Reject
+COOKIE REJECT http://a.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://a.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ACCEPT http://a.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
+COOKIE REJECT http://d.b.c/ Set-Cookie: some_value=value9; Path="/"; expires=%NEXTYEAR%
+COOKIE REJECT http://d.b.c/ Set-Cookie2: some_value=value10; Version=1; Path="/"; max-age="600"
+COOKIE ACCEPT http://d.b.c/ Set-Cookie: some_value=value11; Path="/"
+COOKIE ACCEPT http://d.b.c/ Set-Cookie2: some_value=value12; Version=1; Path="/"
diff --git a/kioslave/http/kcookiejar/tests/kcookiejartest.cpp b/kioslave/http/kcookiejar/tests/kcookiejartest.cpp
new file mode 100644
index 000000000..f196f1820
--- /dev/null
+++ b/kioslave/http/kcookiejar/tests/kcookiejartest.cpp
@@ -0,0 +1,270 @@
+/*
+ This file is part of KDE
+
+ Copyright (C) 2004 Waldo Bastian (bastian@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ This software is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <qdatetime.h>
+#include <qstring.h>
+
+#include <kapplication.h>
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kstandarddirs.h>
+
+#include "../kcookiejar.cpp"
+
+static const char *description = "KCookiejar regression test";
+
+static KCookieJar *jar;
+static QCString *lastYear;
+static QCString *nextYear;
+static KConfig *config = 0;
+
+
+static KCmdLineOptions options[] =
+{
+ { "+testfile", "Regression test to run", 0},
+ KCmdLineLastOption
+};
+
+static void FAIL(const QString &msg)
+{
+ qWarning("%s", msg.local8Bit().data());
+ exit(1);
+}
+
+static void popArg(QCString &command, QCString & line)
+{
+ int i = line.find(' ');
+ if (i != -1)
+ {
+ command = line.left(i);
+ line = line.mid(i+1);
+ }
+ else
+ {
+ command = line;
+ line = 0;
+ }
+}
+
+
+static void popArg(QString &command, QCString & line)
+{
+ int i = line.find(' ');
+ if (i != -1)
+ {
+ command = QString::fromLatin1(line.left(i));
+ line = line.mid(i+1);
+ }
+ else
+ {
+ command = QString::fromLatin1(line);
+ line = 0;
+ }
+}
+
+static void clearConfig()
+{
+ delete config;
+ QString file = locateLocal("config", "kcookiejar-testconfig");
+ QFile::remove(file);
+ config = new KConfig(file);
+ config->setGroup("Cookie Policy");
+ config->writeEntry("RejectCrossDomainCookies", false);
+ config->writeEntry("AcceptSessionCookies", false);
+ config->writeEntry("IgnoreExpirationDate", false);
+ config->writeEntry("CookieGlobalAdvice", "Ask");
+ jar->loadConfig(config, false);
+}
+
+static void clearCookies()
+{
+ jar->eatAllCookies();
+}
+
+static void saveCookies()
+{
+ QString file = locateLocal("config", "kcookiejar-testcookies");
+ QFile::remove(file);
+ jar->saveCookies(file);
+ delete jar;
+ jar = new KCookieJar();
+ clearConfig();
+ jar->loadCookies(file);
+}
+
+static void processCookie(QCString &line)
+{
+ QString policy;
+ popArg(policy, line);
+ KCookieAdvice expectedAdvice = KCookieJar::strToAdvice(policy);
+ if (expectedAdvice == KCookieDunno)
+ FAIL(QString("Unknown accept policy '%1'").arg(policy));
+
+ QString urlStr;
+ popArg(urlStr, line);
+ KURL url(urlStr);
+ if (!url.isValid())
+ FAIL(QString("Invalid URL '%1'").arg(urlStr));
+ if (url.isEmpty())
+ FAIL(QString("Missing URL"));
+
+ line.replace("%LASTYEAR%", *lastYear);
+ line.replace("%NEXTYEAR%", *nextYear);
+
+ KHttpCookieList list = jar->makeCookies(urlStr, line, 0);
+
+ if (list.isEmpty())
+ FAIL(QString("Failed to make cookies from: '%1'").arg(line));
+
+ for(KHttpCookie *cookie = list.first();
+ cookie; cookie = list.next())
+ {
+ KCookieAdvice cookieAdvice = jar->cookieAdvice(cookie);
+ if (cookieAdvice != expectedAdvice)
+ FAIL(urlStr+QString("\n'%2'\nGot advice '%3' expected '%4'").arg(line)
+ .arg(KCookieJar::adviceToStr(cookieAdvice))
+ .arg(KCookieJar::adviceToStr(expectedAdvice)));
+ jar->addCookie(cookie);
+ }
+}
+
+static void processCheck(QCString &line)
+{
+ QString urlStr;
+ popArg(urlStr, line);
+ KURL url(urlStr);
+ if (!url.isValid())
+ FAIL(QString("Invalid URL '%1'").arg(urlStr));
+ if (url.isEmpty())
+ FAIL(QString("Missing URL"));
+
+ QString expectedCookies = QString::fromLatin1(line);
+
+ QString cookies = jar->findCookies(urlStr, false, 0, 0).stripWhiteSpace();
+ if (cookies != expectedCookies)
+ FAIL(urlStr+QString("\nGot '%1' expected '%2'")
+ .arg(cookies, expectedCookies));
+}
+
+static void processClear(QCString &line)
+{
+ if (line == "CONFIG")
+ clearConfig();
+ else if (line == "COOKIES")
+ clearCookies();
+ else
+ FAIL(QString("Unknown command 'CLEAR %1'").arg(line));
+}
+
+static void processConfig(QCString &line)
+{
+ QCString key;
+ popArg(key, line);
+
+ if (key.isEmpty())
+ FAIL(QString("Missing Key"));
+
+ config->setGroup("Cookie Policy");
+ config->writeEntry(key.data(), line.data());
+ jar->loadConfig(config, false);
+}
+
+static void processLine(QCString line)
+{
+ if (line.isEmpty())
+ return;
+
+ if (line[0] == '#')
+ {
+ if (line[1] == '#')
+ qWarning("%s", line.data());
+ return;
+ }
+
+ QCString command;
+ popArg(command, line);
+ if (command.isEmpty())
+ return;
+
+ if (command == "COOKIE")
+ processCookie(line);
+ else if (command == "CHECK")
+ processCheck(line);
+ else if (command == "CLEAR")
+ processClear(line);
+ else if (command == "CONFIG")
+ processConfig(line);
+ else if (command == "SAVE")
+ saveCookies();
+ else
+ FAIL(QString("Unknown command '%1'").arg(command));
+}
+
+static void runRegression(const QString &filename)
+{
+ FILE *file = fopen(filename.local8Bit(), "r");
+ if (!file)
+ FAIL(QString("Can't open '%1'").arg(filename));
+
+ char buf[4096];
+ while (fgets(buf, sizeof(buf), file))
+ {
+ int l = strlen(buf);
+ if (l)
+ {
+ l--;
+ buf[l] = 0;
+ }
+ processLine(buf);
+ }
+ qWarning("%s OK", filename.local8Bit().data());
+}
+
+int main(int argc, char *argv[])
+{
+ QString arg1;
+ QCString arg2;
+ QString result;
+
+ lastYear = new QCString(QString("Fri, 04-May-%1 01:00:00 GMT").arg(QDate::currentDate().year()-1).utf8());
+ nextYear = new QCString(QString(" expires=Fri, 04-May-%1 01:00:00 GMT").arg(QDate::currentDate().year()+1).utf8());
+
+ KAboutData about("kcookietest", "kcookietest", "1.0", description, KAboutData::License_GPL, "(C) 2004 Waldo Bastian");
+ KCmdLineArgs::init( argc, argv, &about);
+
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ KInstance a("kcookietest");
+
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+ if (args->count() != 1)
+ KCmdLineArgs::usage();
+
+ jar = new KCookieJar;
+
+ clearConfig();
+
+ QString file = args->url(0).path();
+ runRegression(file);
+ return 0;
+}