• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.9.5 API Reference
  • KDE Home
  • Contact Us
 

KIOSlave

kcookiejar.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE File Manager
00002 
00003    Copyright (C) 1998-2000 Waldo Bastian (bastian@kde.org)
00004    Copyright (C) 2000,2001 Dawit Alemayehu (adawit@kde.org)
00005 
00006    Permission is hereby granted, free of charge, to any person obtaining a copy
00007    of this software and associated documentation files (the "Software"), to deal
00008    in the Software without restriction, including without limitation the rights
00009    to use, copy, modify, merge, publish, distribute, and/or sell copies of the
00010    Software, and to permit persons to whom the Software is furnished to do so,
00011    subject to the following conditions:
00012 
00013    The above copyright notice and this permission notice shall be included in
00014    all copies or substantial portions of the Software.
00015 
00016    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00017    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00018    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
00019    AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
00020    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00021    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00022 */
00023 //----------------------------------------------------------------------------
00024 //
00025 // KDE File Manager -- HTTP Cookies
00026 
00027 //
00028 // The cookie protocol is a mess. RFC2109 is a joke since nobody seems to
00029 // use it. Apart from that it is badly written.
00030 // We try to implement Netscape Cookies and try to behave us according to
00031 // RFC2109 as much as we can.
00032 //
00033 // We assume cookies do not contain any spaces (Netscape spec.)
00034 // According to RFC2109 this is allowed though.
00035 //
00036 
00037 #include "kcookiejar.h"
00038 
00039 #include <kurl.h>
00040 #include <kdatetime.h>
00041 #include <ksystemtimezone.h>
00042 #include <kconfig.h>
00043 #include <kconfiggroup.h>
00044 #include <ksavefile.h>
00045 #include <kdebug.h>
00046 
00047 #include <QtCore/QString>
00048 #include <QtCore/QFile>
00049 #include <QtCore/QDir>
00050 #include <QtCore/QRegExp>
00051 #include <QtCore/QTextStream>
00052 
00053 // BR87227
00054 // Waba: Should the number of cookies be limited?
00055 // I am not convinced of the need of such limit
00056 // Mozilla seems to limit to 20 cookies / domain
00057 // but it is unclear which policy it uses to expire
00058 // cookies when it exceeds that amount
00059 #undef MAX_COOKIE_LIMIT
00060 
00061 #define MAX_COOKIES_PER_HOST 25
00062 #define READ_BUFFER_SIZE 8192
00063 #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]?)"
00064 
00065 // Note with respect to QLatin1String( )....
00066 // Cookies are stored as 8 bit data and passed to kio_http as Latin1
00067 // regardless of their actual encoding.
00068 #define QL1S(x)   QLatin1String(x)
00069 #define QL1C(x)   QLatin1Char(x)
00070 
00071 
00072 static QString removeWeekday(const QString& value)
00073 {
00074     const int index = value.indexOf(QL1C(' '));
00075     if (index > -1) {
00076         int pos = 0;
00077         const QString weekday = value.left(index);
00078         for (int i = 1; i < 8; ++i) {
00079             if (weekday.startsWith(QDate::shortDayName(i), Qt::CaseInsensitive) ||
00080                 weekday.startsWith(QDate::longDayName(i), Qt::CaseInsensitive)) {
00081                 pos = index + 1;
00082                 break;
00083             }
00084         }
00085         if (pos > 0) {
00086             return value.mid(pos);
00087         }
00088     }
00089     return value;
00090 }
00091 
00092 static QDateTime parseDate(const QString& _value)
00093 {
00094     // Handle sites sending invalid weekday as part of the date. #298660
00095     const QString value (removeWeekday(_value));
00096 
00097     // Check if expiration date matches RFC dates as specified under
00098     // RFC 2616 sec 3.3.1 & RFC 6265 sec 4.1.1
00099     KDateTime dt = KDateTime::fromString(value, KDateTime::RFCDate);
00100 
00101     // In addition to the RFC date formats we support the ANSI C asctime format
00102     // per RFC 2616 sec 3.3.1 and a variation of that detected @ amazon.com
00103     if (!dt.isValid()) {
00104         static const char* date_formats[] = {
00105             "%:B%t%d%t%H:%M:%S%t%Y%t%Z", /* ANSI C's asctime() format (#145244): Jan 01 00:00:00 1970 GMT */
00106             "%:B%t%d%t%Y%t%H:%M:%S%t%Z", /* A variation on the above format seen @ amazon.com: Jan 01 1970 00:00:00 GMT */
00107             0
00108         };
00109 
00110         for (int i = 0; date_formats[i]; ++i) {
00111             dt = KDateTime::fromString(value, QL1S(date_formats[i]));
00112             if (dt.isValid()) {
00113                 break;
00114             }
00115         }
00116     }
00117 
00118     return dt.toUtc().dateTime();  // Per RFC 2616 sec 3.3.1 always convert to UTC.
00119 }
00120 
00121 static qint64 toEpochSecs(const QDateTime& dt)
00122 {
00123     return (dt.toMSecsSinceEpoch()/1000); // convert to seconds...
00124 }
00125 
00126 static qint64 epoch()
00127 {
00128     return toEpochSecs(QDateTime::currentDateTimeUtc());
00129 }
00130 
00131 QString KCookieJar::adviceToStr(KCookieAdvice _advice)
00132 {
00133     switch( _advice )
00134     {
00135     case KCookieAccept: return QL1S("Accept");
00136     case KCookieReject: return QL1S("Reject");
00137     case KCookieAsk: return QL1S("Ask");
00138     default: return QL1S("Dunno");
00139     }
00140 }
00141 
00142 KCookieAdvice KCookieJar::strToAdvice(const QString &_str)
00143 {
00144     if (_str.isEmpty())
00145         return KCookieDunno;
00146 
00147     QString advice = _str.toLower();
00148 
00149     if (advice == QL1S("accept"))
00150         return KCookieAccept;
00151     else if (advice == QL1S("reject"))
00152         return KCookieReject;
00153     else if (advice == QL1S("ask"))
00154         return KCookieAsk;
00155 
00156     return KCookieDunno;
00157 }
00158 
00159 // KHttpCookie
00161 
00162 //
00163 // Cookie constructor
00164 //
00165 KHttpCookie::KHttpCookie(const QString &_host,
00166                  const QString &_domain,
00167                  const QString &_path,
00168                  const QString &_name,
00169                  const QString &_value,
00170                  qint64 _expireDate,
00171                  int _protocolVersion,
00172                  bool _secure,
00173                  bool _httpOnly,
00174                  bool _explicitPath) :
00175        mHost(_host),
00176        mDomain(_domain),
00177        mPath(_path.isEmpty() ? QString() : _path),
00178        mName(_name),
00179        mValue(_value),
00180        mExpireDate(_expireDate),
00181        mProtocolVersion(_protocolVersion),
00182        mSecure(_secure),
00183        mHttpOnly(_httpOnly),
00184        mExplicitPath(_explicitPath)
00185 {
00186 }
00187 
00188 //
00189 // Checks if a cookie has been expired
00190 //
00191 bool KHttpCookie::isExpired(qint64 currentDate) const
00192 {
00193     if (currentDate == -1)
00194         currentDate = epoch();
00195 
00196     return (mExpireDate != 0) && (mExpireDate < currentDate);
00197 }
00198 
00199 //
00200 // Returns a string for a HTTP-header
00201 //
00202 QString KHttpCookie::cookieStr(bool useDOMFormat) const
00203 {
00204     QString result;
00205 
00206     if (useDOMFormat || (mProtocolVersion == 0)) {
00207         if ( mName.isEmpty() )
00208            result = mValue;
00209         else
00210            result = mName + QL1C('=') + mValue;
00211     } else {
00212         result = mName + QL1C('=') + mValue;
00213         if (mExplicitPath)
00214             result += QL1S("; $Path=\"") + mPath + QL1C('"');
00215         if (!mDomain.isEmpty())
00216             result += QL1S("; $Domain=\"") + mDomain + QL1C('"');
00217         if (!mPorts.isEmpty()) {
00218             if (mPorts.length() == 2 && mPorts.at(0) == -1)
00219                 result += QL1S("; $Port");
00220             else {
00221                 QString portNums;
00222                 Q_FOREACH(int port, mPorts)
00223                     portNums += QString::number(port) + QL1C(' ');
00224                 result += QL1S("; $Port=\"") + portNums.trimmed() + QL1C('"');
00225             }
00226         }
00227     }
00228     return result;
00229 }
00230 
00231 //
00232 // Returns whether this cookie should be send to this location.
00233 bool KHttpCookie::match(const QString &fqdn, const QStringList &domains,
00234                         const QString &path, int port) const
00235 {
00236     // Cookie domain match check
00237     if (mDomain.isEmpty())
00238     {
00239        if (fqdn != mHost)
00240           return false;
00241     }
00242     else if (!domains.contains(mDomain))
00243     {
00244         if (mDomain[0] == '.')
00245             return false;
00246 
00247         // Maybe the domain needs an extra dot.
00248         const QString domain = QL1C('.') + mDomain;
00249         if ( !domains.contains( domain ) )
00250           if ( fqdn != mDomain )
00251             return false;
00252     }
00253     else if (mProtocolVersion != 0 && port != -1 &&
00254              !mPorts.isEmpty() && !mPorts.contains(port))
00255     {
00256         return false;
00257     }
00258 
00259     // Cookie path match check
00260     if (mPath.isEmpty())
00261         return true;
00262 
00263     // According to the netscape spec http://www.acme.com/foobar,
00264     // http://www.acme.com/foo.bar and http://www.acme.com/foo/bar
00265     // should all match http://www.acme.com/foo...
00266     // We only match http://www.acme.com/foo/bar
00267     if( path.startsWith(mPath) &&
00268         (
00269          (path.length() == mPath.length() ) ||  // Paths are exact match
00270           mPath.endsWith(QL1C('/')) ||          // mPath ended with a slash
00271          (path[mPath.length()] == QL1C('/'))        // A slash follows
00272          ))
00273         return true; // Path of URL starts with cookie-path
00274 
00275     return false;
00276 }
00277 
00278 // KCookieJar
00280 
00281 //
00282 // Constructs a new cookie jar
00283 //
00284 // One jar should be enough for all cookies.
00285 //
00286 KCookieJar::KCookieJar()
00287 {
00288     m_globalAdvice = KCookieDunno;
00289     m_configChanged = false;
00290     m_cookiesChanged = false;
00291 
00292     KConfig cfg( "khtml/domain_info", KConfig::NoGlobals, "data" );
00293     KConfigGroup group( &cfg, QString() );
00294     m_gTLDs = QSet<QString>::fromList(group.readEntry("gTLDs", QStringList()));
00295     m_twoLevelTLD = QSet<QString>::fromList(group.readEntry("twoLevelTLD", QStringList()));
00296 }
00297 
00298 //
00299 // Destructs the cookie jar
00300 //
00301 // Poor little cookies, they will all be eaten by the cookie monster!
00302 //
00303 KCookieJar::~KCookieJar()
00304 {
00305     qDeleteAll(m_cookieDomains);
00306     // Not much to do here
00307 }
00308 
00309 // cookiePtr is modified: the window ids of the existing cookie in the list are added to it
00310 static void removeDuplicateFromList(KHttpCookieList *list, KHttpCookie& cookiePtr, bool nameMatchOnly=false, bool updateWindowId=false)
00311 {
00312     QString domain1 = cookiePtr.domain();
00313     if (domain1.isEmpty())
00314        domain1 = cookiePtr.host();
00315 
00316     QMutableListIterator<KHttpCookie> cookieIterator(*list);
00317     while (cookieIterator.hasNext()) {
00318         const KHttpCookie& cookie = cookieIterator.next();
00319         QString domain2 = cookie.domain();
00320         if (domain2.isEmpty())
00321             domain2 = cookie.host();
00322 
00323         if (cookiePtr.name() == cookie.name() &&
00324             (nameMatchOnly || (domain1 == domain2 && cookiePtr.path() == cookie.path())))
00325         {
00326             if (updateWindowId) {
00327                 Q_FOREACH(long windowId, cookie.windowIds()) {
00328                     if (windowId && (!cookiePtr.windowIds().contains(windowId))) {
00329                         cookiePtr.windowIds().append(windowId);
00330                     }
00331                 }
00332             }
00333             cookieIterator.remove();
00334             break;
00335         }
00336     }
00337 }
00338 
00339 
00340 //
00341 // Looks for cookies in the cookie jar which are appropriate for _url.
00342 // Returned is a string containing all appropriate cookies in a format
00343 // which can be added to a HTTP-header without any additional processing.
00344 //
00345 QString KCookieJar::findCookies(const QString &_url, bool useDOMFormat, long windowId, KHttpCookieList *pendingCookies)
00346 {
00347     QString cookieStr, fqdn, path;
00348     QStringList domains;
00349     int port = -1;
00350     KCookieAdvice advice = m_globalAdvice;
00351 
00352     if (!parseUrl(_url, fqdn, path, &port))
00353         return cookieStr;
00354 
00355     const bool secureRequest = (_url.startsWith(QL1S("https://"), Qt::CaseInsensitive) ||
00356                                 _url.startsWith(QL1S("webdavs://"), Qt::CaseInsensitive));
00357     if (port == -1)
00358         port = (secureRequest ? 443 : 80);
00359 
00360     extractDomains(fqdn, domains);
00361 
00362     KHttpCookieList allCookies;
00363     for (QStringList::ConstIterator it = domains.constBegin(), itEnd = domains.constEnd();;++it)
00364     {
00365        KHttpCookieList *cookieList = 0;
00366        if (it == itEnd)
00367        {
00368           cookieList = pendingCookies; // Add pending cookies
00369           pendingCookies = 0;
00370           if (!cookieList)
00371              break;
00372        }
00373        else
00374        {
00375           if ((*it).isNull())
00376               cookieList = m_cookieDomains.value(QL1S(""));
00377           else
00378               cookieList = m_cookieDomains.value(*it);
00379 
00380           if (!cookieList)
00381              continue; // No cookies for this domain
00382        }
00383 
00384        if (cookieList->getAdvice() != KCookieDunno)
00385           advice = cookieList->getAdvice();
00386 
00387        QMutableListIterator<KHttpCookie> cookieIt (*cookieList);
00388        while (cookieIt.hasNext()) 
00389        {
00390           KHttpCookie& cookie = cookieIt.next();
00391           // If the we are setup to automatically accept all session cookies and to
00392           // treat all cookies as session cookies or the current cookie is a session
00393           // cookie, then send the cookie back regardless of domain policy.
00394           if (advice == KCookieReject &&
00395               !(m_autoAcceptSessionCookies &&
00396                 (m_ignoreCookieExpirationDate || cookie.expireDate() == 0)))
00397               continue;
00398 
00399           if (!cookie.match(fqdn, domains, path, port))
00400              continue;
00401 
00402           if( cookie.isSecure() && !secureRequest )
00403              continue;
00404 
00405           if( cookie.isHttpOnly() && useDOMFormat )
00406              continue;
00407 
00408           // Do not send expired cookies.
00409           if ( cookie.isExpired())
00410           {
00411              // NOTE: there is no need to delete the cookie here because the
00412              // cookieserver will invoke its saveCookieJar function as a result
00413              // of the state change below. This will then result in the cookie
00414              // being deleting at that point.
00415              m_cookiesChanged = true;
00416              continue;
00417           }
00418 
00419           if (windowId && (cookie.windowIds().indexOf(windowId) == -1))
00420              cookie.windowIds().append(windowId);
00421 
00422           if (it == itEnd) // Only needed when processing pending cookies
00423              removeDuplicateFromList(&allCookies, cookie);
00424 
00425           allCookies.append(cookie);
00426        }
00427 
00428        if (it == itEnd)
00429           break; // Finished.
00430     }
00431 
00432     int protVersion = 0;
00433     Q_FOREACH(const KHttpCookie& cookie, allCookies) {
00434         if (cookie.protocolVersion() > protVersion)
00435             protVersion = cookie.protocolVersion();
00436     }
00437 
00438     if (!allCookies.isEmpty())
00439     {
00440         if (!useDOMFormat)
00441             cookieStr = QL1S("Cookie: ");
00442 
00443         if (protVersion > 0)
00444             cookieStr = cookieStr + QL1S("$Version=") + QString::number(protVersion) + QL1S("; ");
00445 
00446         Q_FOREACH(const KHttpCookie& cookie, allCookies)
00447             cookieStr = cookieStr + cookie.cookieStr(useDOMFormat) + QL1S("; ");
00448 
00449         cookieStr.truncate(cookieStr.length() - 2); // Remove the trailing ';'
00450     }
00451 
00452     return cookieStr;
00453 }
00454 
00455 //
00456 // This function parses a string like 'my_name="my_value";' and returns
00457 // 'my_name' in Name and 'my_value' in Value.
00458 //
00459 // A pointer to the end of the parsed part is returned.
00460 // This pointer points either to:
00461 // '\0' - The end of the string has reached.
00462 // ';'  - Another my_name="my_value" pair follows
00463 // ','  - Another cookie follows
00464 // '\n' - Another header follows
00465 static const char * parseNameValue(const char *header,
00466                                   QString &Name,
00467                                   QString &Value,
00468                                   bool keepQuotes=false,
00469                                   bool rfcQuotes=false)
00470 {
00471     const char *s = header;
00472     // Parse 'my_name' part
00473     for(; (*s != '='); s++)
00474     {
00475         if ((*s=='\0') || (*s==';') || (*s=='\n'))
00476         {
00477             // No '=' sign -> use string as the value, name is empty
00478             // (behavior found in Mozilla and IE)
00479             Name = QL1S("");
00480             Value = QL1S(header);
00481             Value.truncate( s - header );
00482             Value = Value.trimmed();
00483             return s;
00484         }
00485     }
00486 
00487     Name = QL1S(header);
00488     Name.truncate( s - header );
00489     Name = Name.trimmed();
00490 
00491     // *s == '='
00492     s++;
00493 
00494     // Skip any whitespace
00495     for(; (*s == ' ') || (*s == '\t'); s++)
00496     {
00497         if ((*s=='\0') || (*s==';') || (*s=='\n'))
00498         {
00499             // End of Name
00500             Value = "";
00501             return s;
00502         }
00503     }
00504 
00505     if ((rfcQuotes || !keepQuotes) && (*s == '\"'))
00506     {
00507         // Parse '"my_value"' part (quoted value)
00508         if (keepQuotes)
00509            header = s++;
00510         else
00511            header = ++s; // skip "
00512         for(;(*s != '\"');s++)
00513         {
00514             if ((*s=='\0') || (*s=='\n'))
00515             {
00516                 // End of Name
00517                 Value = QL1S(header);
00518                 Value.truncate(s - header);
00519                 return s;
00520             }
00521         }
00522         Value = QL1S(header);
00523         // *s == '\"';
00524         if (keepQuotes)
00525            Value.truncate( ++s - header );
00526         else
00527            Value.truncate( s++ - header );
00528 
00529         // Skip any remaining garbage
00530         for(;; s++)
00531         {
00532             if ((*s=='\0') || (*s==';') || (*s=='\n'))
00533                 break;
00534         }
00535     }
00536     else
00537     {
00538         // Parse 'my_value' part (unquoted value)
00539         header = s;
00540         while ((*s != '\0') && (*s != ';') && (*s != '\n'))
00541             s++;
00542         // End of Name
00543         Value = QL1S(header);
00544         Value.truncate( s - header );
00545         Value = Value.trimmed();
00546     }
00547     return s;
00548 
00549 }
00550 
00551 void KCookieJar::stripDomain(const QString &_fqdn, QString &_domain)
00552 {
00553    QStringList domains;
00554    extractDomains(_fqdn, domains);
00555    if (domains.count() > 3)
00556       _domain = domains[3];
00557    else if ( domains.count() > 0 )
00558       _domain = domains[0];
00559    else
00560       _domain = QL1S("");
00561 }
00562 
00563 QString KCookieJar::stripDomain(const KHttpCookie& cookie)
00564 {
00565     QString domain; // We file the cookie under this domain.
00566     if (cookie.domain().isEmpty())
00567        stripDomain( cookie.host(), domain);
00568     else
00569        domain = cookie.domain();
00570     return domain;
00571 }
00572 
00573 bool KCookieJar::parseUrl(const QString &_url,
00574                           QString &_fqdn,
00575                           QString &_path,
00576                           int *port)
00577 {
00578     KUrl kurl(_url);
00579     if (!kurl.isValid() || kurl.protocol().isEmpty())
00580        return false;
00581 
00582     _fqdn = kurl.host().toLower();
00583     // Cookie spoofing protection.  Since there is no way a path separator,
00584     // a space or the escape encoding character is allowed in the hostname
00585     // according to RFC 2396, reject attempts to include such things there!
00586     if (_fqdn.contains(QL1C('/')) || _fqdn.contains(QL1C('%')))
00587         return false;  // deny everything!!
00588 
00589     // Set the port number from the protocol when one is found...
00590     if (port)
00591         *port = kurl.port();
00592 
00593     _path = kurl.path();
00594     if (_path.isEmpty())
00595        _path = QL1S("/");
00596 
00597     return true;
00598 }
00599 
00600 // not static because it uses m_twoLevelTLD
00601 void KCookieJar::extractDomains(const QString &_fqdn,
00602                                 QStringList &_domains) const
00603 {
00604     if (_fqdn.isEmpty()) {
00605        _domains.append( QL1S("localhost") );
00606        return;
00607     }
00608 
00609     // Return numeric IPv6 addresses as is...
00610     if (_fqdn[0] == '[')
00611     {
00612        _domains.append( _fqdn );
00613        return;
00614     }
00615     // Return numeric IPv4 addresses as is...
00616     if (_fqdn[0] >= '0' && _fqdn[0] <= '9' && _fqdn.indexOf(QRegExp(IP_ADDRESS_EXPRESSION)) > -1)
00617     {
00618        _domains.append( _fqdn );
00619        return;
00620     }
00621 
00622     // Always add the FQDN at the start of the list for
00623     // hostname == cookie-domainname checks!
00624     _domains.append(_fqdn);
00625     _domains.append(QL1C('.') + _fqdn);
00626 
00627     QStringList partList = _fqdn.split(QL1C('.'), QString::SkipEmptyParts);
00628 
00629     if (partList.count())
00630         partList.erase(partList.begin()); // Remove hostname
00631 
00632     while(partList.count())
00633     {
00634 
00635        if (partList.count() == 1)
00636          break; // We only have a TLD left.
00637 
00638        if ((partList.count() == 2) && m_twoLevelTLD.contains(partList[1].toLower()))
00639        {
00640           // This domain uses two-level TLDs in the form xxxx.yy
00641           break;
00642        }
00643 
00644        if ((partList.count() == 2) && (partList[1].length() == 2))
00645        {
00646           // If this is a TLD, we should stop. (e.g. co.uk)
00647           // We assume this is a TLD if it ends with .xx.yy or .x.yy
00648           if (partList[0].length() <= 2)
00649              break; // This is a TLD.
00650 
00651           // Catch some TLDs that we miss with the previous check
00652           // e.g. com.au, org.uk, mil.co
00653           if (m_gTLDs.contains(partList[0].toLower()))
00654               break;
00655        }
00656 
00657        QString domain = partList.join(QL1S("."));
00658        _domains.append(domain);
00659        _domains.append(QL1C('.') + domain);
00660        partList.erase(partList.begin()); // Remove part
00661     }
00662 }
00663 
00664 //
00665 // This function parses cookie_headers and returns a linked list of
00666 // KHttpCookie objects for all cookies found in cookie_headers.
00667 // If no cookies could be found 0 is returned.
00668 //
00669 // cookie_headers should be a concatenation of all lines of a HTTP-header
00670 // which start with "Set-Cookie". The lines should be separated by '\n's.
00671 //
00672 KHttpCookieList KCookieJar::makeCookies(const QString &_url,
00673                                        const QByteArray &cookie_headers,
00674                                        long windowId)
00675 {
00676     QString fqdn, path;
00677 
00678     if (!parseUrl(_url, fqdn, path))
00679         return KHttpCookieList(); // Error parsing _url
00680 
00681     QString Name, Value;
00682     KHttpCookieList cookieList, cookieList2;
00683 
00684     bool isRFC2965 = false;
00685     bool crossDomain = false;
00686     const char *cookieStr = cookie_headers.constData();    
00687 
00688     QString defaultPath;
00689     const int index = path.lastIndexOf(QL1C('/'));
00690     if (index > 0)
00691        defaultPath = path.left(index);
00692 
00693     // Check for cross-domain flag from kio_http
00694     if (qstrncmp(cookieStr, "Cross-Domain\n", 13) == 0)
00695     {
00696         cookieStr += 13;
00697         crossDomain = true;
00698     }
00699 
00700     //  The hard stuff :)
00701     for(;;)
00702     {
00703         // check for "Set-Cookie"
00704         if (qstrnicmp(cookieStr, "Set-Cookie:", 11) == 0)
00705         {
00706             cookieStr = parseNameValue(cookieStr+11, Name, Value, true);
00707 
00708             // Host = FQDN
00709             // Default domain = ""
00710             // Default path according to rfc2109
00711 
00712 
00713             KHttpCookie cookie(fqdn, QL1S(""), defaultPath, Name, Value);
00714             if (windowId)
00715                cookie.mWindowIds.append(windowId);
00716             cookie.mCrossDomain = crossDomain;
00717 
00718             // Insert cookie in chain
00719             cookieList.append(cookie);
00720         }
00721         else if (qstrnicmp(cookieStr, "Set-Cookie2:", 12) == 0)
00722         {
00723             // Attempt to follow rfc2965
00724             isRFC2965 = true;
00725             cookieStr = parseNameValue(cookieStr+12, Name, Value, true, true);
00726 
00727             // Host = FQDN
00728             // Default domain = ""
00729             // Default path according to rfc2965
00730 
00731             KHttpCookie cookie(fqdn, QL1S(""), defaultPath, Name, Value);
00732             if (windowId)
00733                cookie.mWindowIds.append(windowId);
00734             cookie.mCrossDomain = crossDomain;
00735 
00736             // Insert cookie in chain
00737             cookieList2.append(cookie);
00738         }
00739         else
00740         {
00741             // This is not the start of a cookie header, skip till next line.
00742             while (*cookieStr && *cookieStr != '\n')
00743                 cookieStr++;
00744 
00745             if (*cookieStr == '\n')
00746                 cookieStr++;
00747 
00748             if (!*cookieStr)
00749                 break; // End of cookie_headers
00750             else
00751                 continue; // end of this header, continue with next.
00752         }
00753 
00754         while ((*cookieStr == ';') || (*cookieStr == ' '))
00755         {
00756             cookieStr++;
00757 
00758             // Name-Value pair follows
00759             cookieStr = parseNameValue(cookieStr, Name, Value);
00760             KHttpCookie& lastCookie = (isRFC2965 ? cookieList2.last() : cookieList.last());
00761 
00762             if (Name.compare(QL1S("domain"), Qt::CaseInsensitive) == 0)
00763             {
00764                 QString dom = Value.toLower();
00765                 // RFC2965 3.2.2: If an explicitly specified value does not
00766                 // start with a dot, the user agent supplies a leading dot
00767                 if(dom.length() && dom[0] != '.')
00768                     dom.prepend(".");
00769                 // remove a trailing dot
00770                 if(dom.length() > 2 && dom[dom.length()-1] == '.')
00771                     dom = dom.left(dom.length()-1);
00772 
00773                 if(dom.count(QL1C('.')) > 1 || dom == ".local")
00774                     lastCookie.mDomain = dom;
00775             }
00776             else if (Name.compare(QL1S("max-age"), Qt::CaseInsensitive) == 0)
00777             {
00778                 int max_age = Value.toInt();
00779                 if (max_age == 0)
00780                     lastCookie.mExpireDate = 1;
00781                 else
00782                     lastCookie.mExpireDate = toEpochSecs(QDateTime::currentDateTimeUtc().addSecs(max_age));
00783             }
00784             else if (Name.compare(QL1S("expires"), Qt::CaseInsensitive) == 0)
00785             {
00786                 const QDateTime dt = parseDate(Value);
00787 
00788                 if (dt.isValid()) {
00789                     lastCookie.mExpireDate = toEpochSecs(dt);
00790                     if (lastCookie.mExpireDate == 0)
00791                         lastCookie.mExpireDate = 1;
00792                 }
00793             }
00794             else if (Name.compare(QL1S("path"), Qt::CaseInsensitive) == 0)
00795             {
00796                 if (Value.isEmpty())
00797                    lastCookie.mPath.clear(); // Catch "" <> QString()
00798                 else
00799                    lastCookie.mPath = QUrl::fromPercentEncoding(Value.toLatin1());
00800                 lastCookie.mExplicitPath = true;
00801             }
00802             else if (Name.compare(QL1S("version"), Qt::CaseInsensitive) == 0)
00803             {
00804                 lastCookie.mProtocolVersion = Value.toInt();
00805             }
00806             else if (Name.compare(QL1S("secure"), Qt::CaseInsensitive) == 0 ||
00807                      (Name.isEmpty() && Value.compare(QL1S("secure"), Qt::CaseInsensitive) == 0))
00808             {
00809                 lastCookie.mSecure = true;
00810             }
00811             else if (Name.compare(QL1S("httponly"), Qt::CaseInsensitive) == 0 ||
00812                     (Name.isEmpty() && Value.compare(QL1S("httponly"), Qt::CaseInsensitive) == 0))
00813             {
00814                 lastCookie.mHttpOnly = true;
00815             }
00816             else if (isRFC2965 && (Name.compare(QL1S("port"), Qt::CaseInsensitive) == 0 ||
00817                      (Name.isEmpty() && Value.compare(QL1S("port"), Qt::CaseInsensitive) == 0)))
00818             {
00819                 // Based on the port selection rule of RFC 2965 section 3.3.4...
00820                 if (Name.isEmpty())
00821                 {
00822                     // We intentionally append a -1 first in order to distinguish
00823                     // between only a 'Port' vs a 'Port="80 443"' in the sent cookie.
00824                     lastCookie.mPorts.append(-1);
00825                     const bool secureRequest = (_url.startsWith(QL1S("https://"), Qt::CaseInsensitive) ||
00826                                                 _url.startsWith(QL1S("webdavs://"), Qt::CaseInsensitive));
00827                     if (secureRequest)
00828                       lastCookie.mPorts.append(443);
00829                     else
00830                       lastCookie.mPorts.append(80);
00831                 }
00832                 else
00833                 {
00834                     bool ok;
00835                     const QStringList portNums = Value.split(QL1C(' '), QString::SkipEmptyParts);
00836                     Q_FOREACH(const QString& portNum, portNums)
00837                     {
00838                         const int port = portNum.toInt(&ok);
00839                         if (ok)
00840                             lastCookie.mPorts.append(port);
00841                     }
00842                 }
00843             }
00844         }
00845 
00846         if (*cookieStr == '\0')
00847             break; // End of header
00848 
00849         // Skip ';' or '\n'
00850         cookieStr++;
00851     }
00852 
00853     // RFC2965 cookies come last so that they override netscape cookies.
00854     while(!cookieList2.isEmpty()) {
00855         KHttpCookie& lastCookie = cookieList2.first();
00856         removeDuplicateFromList(&cookieList, lastCookie, true);
00857         cookieList.append(lastCookie);
00858         cookieList2.removeFirst();
00859     }
00860 
00861     return cookieList;
00862 }
00863 
00870 KHttpCookieList KCookieJar::makeDOMCookies(const QString &_url,
00871                                            const QByteArray &cookie_domstring,
00872                                            long windowId)
00873 {
00874     // A lot copied from above
00875     KHttpCookieList cookieList;
00876 
00877     const char *cookieStr = cookie_domstring.data();
00878     QString fqdn;
00879     QString path;
00880 
00881     if (!parseUrl(_url, fqdn, path))
00882     {
00883         // Error parsing _url
00884         return KHttpCookieList();
00885     }
00886 
00887     QString Name;
00888     QString Value;
00889     //  This time it's easy
00890     while(*cookieStr)
00891     {
00892         cookieStr = parseNameValue(cookieStr, Name, Value);
00893 
00894         // Host = FQDN
00895         // Default domain = ""
00896         // Default path = ""
00897         KHttpCookie cookie(fqdn, QString(), QString(),
00898                            Name, Value );
00899         if (windowId)
00900             cookie.mWindowIds.append(windowId);
00901 
00902         cookieList.append(cookie);
00903 
00904         if (*cookieStr != '\0')
00905             cookieStr++;         // Skip ';' or '\n'
00906     }
00907 
00908     return cookieList;
00909 }
00910 
00911 // KHttpCookieList sorting
00913 
00914 // We want the longest path first
00915 static bool compareCookies(const KHttpCookie& item1, const KHttpCookie& item2)
00916 {
00917     return item1.path().length() > item2.path().length();
00918 }
00919 
00920 
00921 #ifdef MAX_COOKIE_LIMIT
00922 static void makeRoom(KHttpCookieList *cookieList, KHttpCookiePtr &cookiePtr)
00923 {
00924      // Too many cookies: throw one away, try to be somewhat clever
00925      KHttpCookiePtr lastCookie = 0;
00926      for(KHttpCookiePtr cookie = cookieList->first(); cookie; cookie = cookieList->next())
00927      {
00928          if (compareCookies(cookie, cookiePtr))
00929             break;
00930          lastCookie = cookie;
00931      }
00932      if (!lastCookie)
00933          lastCookie = cookieList->first();
00934      cookieList->removeRef(lastCookie);
00935 }
00936 #endif
00937 
00938 //
00939 // This function hands a KHttpCookie object over to the cookie jar.
00940 //
00941 void KCookieJar::addCookie(KHttpCookie &cookie)
00942 {
00943     QStringList domains;
00944     // We always need to do this to make sure that the
00945     // that cookies of type hostname == cookie-domainname
00946     // are properly removed and/or updated as necessary!
00947     extractDomains( cookie.host(), domains );
00948 
00949     QStringListIterator it (domains);
00950     while (it.hasNext())
00951     {
00952         const QString& key = it.next();
00953         KHttpCookieList* list;
00954 
00955         if (key.isNull())
00956             list = m_cookieDomains.value(QL1S(""));
00957         else
00958             list = m_cookieDomains.value(key);
00959 
00960         if (list)
00961             removeDuplicateFromList(list, cookie, false, true);
00962     }
00963 
00964     const QString domain = stripDomain( cookie );
00965     KHttpCookieList* cookieList;
00966     if (domain.isNull())
00967         cookieList = m_cookieDomains.value(QL1S(""));
00968     else
00969         cookieList = m_cookieDomains.value(domain);
00970 
00971     if (!cookieList)
00972     {
00973         // Make a new cookie list
00974         cookieList = new KHttpCookieList();
00975 
00976         // All cookies whose domain is not already
00977         // known to us should be added with KCookieDunno.
00978         // KCookieDunno means that we use the global policy.
00979         cookieList->setAdvice( KCookieDunno );
00980 
00981         m_cookieDomains.insert( domain, cookieList);
00982 
00983         // Update the list of domains
00984         m_domainList.append(domain);
00985     }
00986 
00987     // Add the cookie to the cookie list
00988     // The cookie list is sorted 'longest path first'
00989     if (!cookie.isExpired())
00990     {
00991 #ifdef MAX_COOKIE_LIMIT
00992         if (cookieList->count() >= MAX_COOKIES_PER_HOST)
00993            makeRoom(cookieList, cookie); // Delete a cookie
00994 #endif
00995         cookieList->push_back(cookie);
00996         // Use a stable sort so that unit tests are reliable.
00997         // In practice it doesn't matter though.
00998         qStableSort(cookieList->begin(), cookieList->end(), compareCookies);
00999 
01000         m_cookiesChanged = true;
01001     }
01002 }
01003 
01004 //
01005 // This function advices whether a single KHttpCookie object should
01006 // be added to the cookie jar.
01007 //
01008 KCookieAdvice KCookieJar::cookieAdvice(KHttpCookie& cookie)
01009 {
01010     if (m_rejectCrossDomainCookies && cookie.isCrossDomain())
01011        return KCookieReject;
01012 
01013     QStringList domains;
01014     extractDomains(cookie.host(), domains);
01015 
01016     // If the cookie specifies a domain, check whether it is valid. Otherwise,
01017     // accept the cookie anyways but removes the domain="" value to prevent
01018     // cross-site cookie injection.
01019     if (!cookie.domain().isEmpty()) {
01020       if (!domains.contains(cookie.domain()) &&
01021           !cookie.domain().endsWith(QL1C('.') + cookie.host()))
01022          cookie.fixDomain(QString());
01023     }
01024 
01025     if (m_autoAcceptSessionCookies && (cookie.expireDate() == 0 ||
01026         m_ignoreCookieExpirationDate))
01027        return KCookieAccept;
01028 
01029     KCookieAdvice advice = KCookieDunno;
01030     QStringListIterator it (domains);
01031     while(advice == KCookieDunno && it.hasNext()) {
01032        const QString& domain = it.next();
01033        if (domain.startsWith(QL1C('.')) || cookie.host() == domain) {
01034               KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01035           if (cookieList)
01036              advice = cookieList->getAdvice();
01037        }
01038     }
01039 
01040     if (advice == KCookieDunno)
01041         advice = m_globalAdvice;
01042 
01043     return advice;
01044 }
01045 
01046 //
01047 // This function gets the advice for all cookies originating from
01048 // _domain.
01049 //
01050 KCookieAdvice KCookieJar::getDomainAdvice(const QString &_domain)
01051 {
01052     KHttpCookieList *cookieList = m_cookieDomains.value(_domain);
01053     KCookieAdvice advice;
01054 
01055     if (cookieList)
01056         advice = cookieList->getAdvice();
01057     else
01058         advice = KCookieDunno;
01059 
01060     return advice;
01061 }
01062 
01063 //
01064 // This function sets the advice for all cookies originating from
01065 // _domain.
01066 //
01067 void KCookieJar::setDomainAdvice(const QString &_domain, KCookieAdvice _advice)
01068 {
01069     QString domain(_domain);
01070     KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01071 
01072     if (cookieList) {
01073         if (cookieList->getAdvice() != _advice) {
01074            m_configChanged = true;
01075            // domain is already known
01076            cookieList->setAdvice( _advice);
01077         }
01078 
01079         if ((cookieList->isEmpty()) && (_advice == KCookieDunno)) {
01080             // This deletes cookieList!
01081             delete m_cookieDomains.take(domain);
01082             m_domainList.removeAll(domain);
01083         }
01084     } else {
01085         // domain is not yet known
01086         if (_advice != KCookieDunno) {
01087             // We should create a domain entry
01088             m_configChanged = true;
01089             // Make a new cookie list
01090             cookieList = new KHttpCookieList();
01091             cookieList->setAdvice(_advice);
01092             m_cookieDomains.insert(domain, cookieList);
01093             // Update the list of domains
01094             m_domainList.append( domain);
01095         }
01096     }
01097 }
01098 
01099 //
01100 // This function sets the advice for all cookies originating from
01101 // the same domain as _cookie
01102 //
01103 void KCookieJar::setDomainAdvice(const KHttpCookie& cookie, KCookieAdvice _advice)
01104 {
01105     QString domain;
01106     stripDomain(cookie.host(), domain); // We file the cookie under this domain.
01107     setDomainAdvice(domain, _advice);
01108 }
01109 
01110 //
01111 // This function sets the global advice for cookies
01112 //
01113 void KCookieJar::setGlobalAdvice(KCookieAdvice _advice)
01114 {
01115     if (m_globalAdvice != _advice)
01116        m_configChanged = true;
01117     m_globalAdvice = _advice;
01118 }
01119 
01120 //
01121 // Get a list of all domains known to the cookie jar.
01122 //
01123 const QStringList& KCookieJar::getDomainList()
01124 {
01125     return m_domainList;
01126 }
01127 
01128 //
01129 // Get a list of all cookies in the cookie jar originating from _domain.
01130 //
01131 KHttpCookieList *KCookieJar::getCookieList(const QString & _domain,
01132                                            const QString & _fqdn )
01133 {
01134     QString domain;
01135 
01136     if (_domain.isEmpty())
01137         stripDomain(_fqdn, domain);
01138     else
01139         domain = _domain;
01140 
01141     return m_cookieDomains.value(domain);
01142 }
01143 
01144 //
01145 // Eat a cookie out of the jar.
01146 // cookieIterator should be one of the cookies returned by getCookieList()
01147 //
01148 void KCookieJar::eatCookie(KHttpCookieList::iterator cookieIterator)
01149 {
01150     const KHttpCookie& cookie = *cookieIterator;
01151     const QString domain = stripDomain(cookie); // We file the cookie under this domain.
01152     KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01153 
01154     if (cookieList) {
01155         // This deletes cookie!
01156         cookieList->erase(cookieIterator);
01157 
01158         if ((cookieList->isEmpty()) &&
01159             (cookieList->getAdvice() == KCookieDunno))
01160         {
01161             // This deletes cookieList!
01162             delete m_cookieDomains.take(domain);
01163             m_domainList.removeAll(domain);
01164         }
01165     }
01166 }
01167 
01168 void KCookieJar::eatCookiesForDomain(const QString &domain)
01169 {
01170    KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01171    if (!cookieList || cookieList->isEmpty()) return;
01172 
01173    cookieList->clear();
01174    if (cookieList->getAdvice() == KCookieDunno)
01175    {
01176        // This deletes cookieList!
01177        delete m_cookieDomains.take(domain);
01178        m_domainList.removeAll(domain);
01179    }
01180    m_cookiesChanged = true;
01181 }
01182 
01183 void KCookieJar::eatSessionCookies( long windowId )
01184 {
01185     if (!windowId)
01186         return;
01187 
01188     Q_FOREACH(const QString& domain, m_domainList)
01189         eatSessionCookies( domain, windowId, false );
01190 }
01191 
01192 void KCookieJar::eatAllCookies()
01193 {
01194     Q_FOREACH(const QString& domain, m_domainList)
01195         eatCookiesForDomain(domain);    // This might remove domain from m_domainList!
01196 }
01197 
01198 void KCookieJar::eatSessionCookies( const QString& fqdn, long windowId,
01199                                     bool isFQDN )
01200 {
01201     KHttpCookieList* cookieList;
01202     if ( !isFQDN )
01203         cookieList = m_cookieDomains.value(fqdn);
01204     else {
01205         QString domain;
01206         stripDomain( fqdn, domain );
01207         cookieList = m_cookieDomains.value(domain);
01208     }
01209 
01210     if (cookieList) {
01211         QMutableListIterator<KHttpCookie> cookieIterator(*cookieList);
01212         while (cookieIterator.hasNext()) {
01213             KHttpCookie& cookie = cookieIterator.next();
01214             if ((cookie.expireDate() != 0) && !m_ignoreCookieExpirationDate) {
01215                continue;
01216             }
01217 
01218             QList<long> &ids = cookie.windowIds();
01219 
01220 #ifndef NDEBUG
01221             if (ids.contains(windowId)) {
01222                 if (ids.count() > 1)
01223                     kDebug(7104) << "removing window id" << windowId << "from session cookie";
01224                 else
01225                     kDebug(7104) << "deleting session cookie";
01226             }
01227 #endif
01228             if (!ids.removeAll(windowId) || !ids.isEmpty()) {
01229                continue;
01230             }
01231             cookieIterator.remove();
01232         }
01233     }
01234 }
01235 
01236 static QString hostWithPort(const KHttpCookie* cookie)
01237 {
01238     const QList<int>& ports = cookie->ports();
01239 
01240     if (ports.isEmpty())
01241         return cookie->host();
01242 
01243     QStringList portList;
01244     Q_FOREACH(int port, ports)
01245         portList << QString::number(port);
01246 
01247     return (cookie->host() + QL1C(':') + portList.join(QL1S(",")));
01248 }
01249 
01250 //
01251 // Saves all cookies to the file '_filename'.
01252 // On succes 'true' is returned.
01253 // On failure 'false' is returned.
01254 bool KCookieJar::saveCookies(const QString &_filename)
01255 {
01256     KSaveFile cookieFile(_filename);
01257 
01258     if (!cookieFile.open())
01259        return false;
01260     cookieFile.setPermissions(QFile::ReadUser|QFile::WriteUser);
01261 
01262     QTextStream ts(&cookieFile);
01263 
01264     ts << "# KDE Cookie File v2\n#\n";
01265 
01266     QString s;
01267     s.sprintf("%-20s %-20s %-12s %-10s %-4s %-20s %-4s %s\n",
01268               "# Host", "Domain", "Path", "Exp.date", "Prot",
01269               "Name", "Sec", "Value");
01270     ts << s.toLatin1().constData();
01271 
01272     QStringListIterator it(m_domainList);
01273     while (it.hasNext())
01274     {
01275         const QString& domain = it.next();
01276         bool domainPrinted = false;
01277 
01278         KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01279         if (!cookieList)
01280             continue;
01281 
01282         QMutableListIterator<KHttpCookie> cookieIterator(*cookieList);
01283         while (cookieIterator.hasNext()) {
01284             const KHttpCookie& cookie = cookieIterator.next();
01285             if (cookie.isExpired()) {
01286                 // Delete expired cookies
01287                 cookieIterator.remove();
01288                 continue;
01289             }
01290             if (cookie.expireDate() != 0 && !m_ignoreCookieExpirationDate) {
01291                 // Only save cookies that are not "session-only cookies"
01292                 if (!domainPrinted) {
01293                     domainPrinted = true;
01294                     ts << '[' << domain.toLocal8Bit().data() << "]\n";
01295                 }
01296                 // Store persistent cookies
01297                 const QString path = QL1S("\"") + cookie.path() + QL1C('"');
01298                 const QString domain = QL1S("\"") + cookie.domain() + QL1C('"');
01299                 const QString host = hostWithPort(&cookie);
01300 
01301                 // TODO: replace with direct QTextStream output ?
01302                 s.sprintf("%-20s %-20s %-12s %10lld  %3d %-20s %-4i %s\n",
01303                         host.toLatin1().constData(), domain.toLatin1().constData(),
01304                         path.toLatin1().constData(), cookie.expireDate(),
01305                         cookie.protocolVersion(),
01306                         cookie.name().isEmpty() ? cookie.value().toLatin1().constData() : cookie.name().toLatin1().constData(),
01307                         (cookie.isSecure() ? 1 : 0) + (cookie.isHttpOnly() ? 2 : 0) +
01308                         (cookie.hasExplicitPath() ? 4 : 0) + (cookie.name().isEmpty() ? 8 : 0),
01309                         cookie.value().toLatin1().constData());
01310                 ts << s.toLatin1().constData();
01311             }
01312         }
01313     }
01314 
01315     return cookieFile.finalize();
01316 }
01317 
01318 static const char *parseField(char* &buffer, bool keepQuotes=false)
01319 {
01320     char *result;
01321     if (!keepQuotes && (*buffer == '\"'))
01322     {
01323         // Find terminating "
01324         buffer++;
01325         result = buffer;
01326         while((*buffer != '\"') && (*buffer))
01327             buffer++;
01328     }
01329     else
01330     {
01331         // Find first white space
01332         result = buffer;
01333         while((*buffer != ' ') && (*buffer != '\t') && (*buffer != '\n') && (*buffer))
01334             buffer++;
01335     }
01336 
01337     if (!*buffer)
01338         return result; //
01339     *buffer++ = '\0';
01340 
01341     // Skip white-space
01342     while((*buffer == ' ') || (*buffer == '\t') || (*buffer == '\n'))
01343         buffer++;
01344 
01345     return result;
01346 }
01347 
01348 
01349 static QString extractHostAndPorts(const QString& str, QList<int>* ports = 0)
01350 {
01351     if (str.isEmpty())
01352         return str;
01353 
01354     const int index = str.indexOf(QL1C(':'));
01355     if (index == -1)
01356         return str;
01357 
01358     const QString host = str.left(index);
01359     if (ports) {
01360         bool ok;
01361         QStringList portList = str.mid(index+1).split(QL1C(','));
01362         Q_FOREACH(const QString& portStr, portList) {
01363            const int portNum = portStr.toInt(&ok);
01364            if (ok)
01365               ports->append(portNum);
01366         }
01367     }
01368 
01369     return host;
01370 }
01371 
01372 //
01373 // Reloads all cookies from the file '_filename'.
01374 // On succes 'true' is returned.
01375 // On failure 'false' is returned.
01376 bool KCookieJar::loadCookies(const QString &_filename)
01377 {
01378     QFile cookieFile (_filename);
01379 
01380     if (!cookieFile.open(QIODevice::ReadOnly))
01381         return false;
01382 
01383     int version = 1;
01384     bool success = false;
01385     char *buffer = new char[READ_BUFFER_SIZE];
01386     qint64 len = cookieFile.readLine(buffer, READ_BUFFER_SIZE-1);
01387 
01388     if (len != -1)
01389     {
01390         if (qstrcmp(buffer, "# KDE Cookie File\n") == 0)
01391         {
01392             success = true;
01393         }
01394         else if(qstrcmp(buffer, "# KDE Cookie File v") > 0)
01395         {
01396             bool ok = false;
01397             const int verNum = QByteArray(buffer+19, len-19).trimmed().toInt(&ok);
01398             if (ok)
01399             {
01400                 version = verNum;
01401                 success = true;
01402             }
01403         }
01404     }
01405 
01406     if (success)
01407     {
01408         const qint64 currentTime = epoch();
01409         QList<int> ports;
01410 
01411         while(cookieFile.readLine(buffer, READ_BUFFER_SIZE-1) != -1)
01412         {
01413             char *line = buffer;
01414             // Skip lines which begin with '#' or '['
01415             if ((line[0] == '#') || (line[0] == '['))
01416                 continue;
01417 
01418             const QString host = extractHostAndPorts(QL1S(parseField(line)), &ports);
01419             const QString domain = QL1S( parseField(line) );
01420             if (host.isEmpty() && domain.isEmpty())
01421                 continue;
01422             const QString path = QL1S( parseField(line) );
01423             const QString expStr = QL1S( parseField(line) );
01424             if (expStr.isEmpty()) continue;
01425             const qint64 expDate = expStr.toLongLong();
01426             const QString verStr = QL1S( parseField(line) );
01427             if (verStr.isEmpty()) continue;
01428             int protVer  = verStr.toInt();
01429             QString name = QL1S( parseField(line) );
01430             bool keepQuotes = false;
01431             bool secure = false;
01432             bool httpOnly = false;
01433             bool explicitPath = false;
01434             const char *value = 0;
01435             if ((version == 2) || (protVer >= 200))
01436             {
01437                 if (protVer >= 200)
01438                     protVer -= 200;
01439                 int i = atoi( parseField(line) );
01440                 secure = i & 1;
01441                 httpOnly = i & 2;
01442                 explicitPath = i & 4;
01443                 if (i & 8)
01444                     name = "";
01445                 line[strlen(line)-1] = '\0'; // Strip LF.
01446                 value = line;
01447             }
01448             else
01449             {
01450                 if (protVer >= 100)
01451                 {
01452                     protVer -= 100;
01453                     keepQuotes = true;
01454                 }
01455                 value = parseField(line, keepQuotes);
01456                 secure = QByteArray(parseField(line)).toShort();
01457             }
01458 
01459             // Expired or parse error
01460             if (!value || expDate == 0 || expDate < currentTime)
01461                 continue;
01462 
01463             KHttpCookie cookie(host, domain, path, name, value, expDate,
01464                                protVer, secure, httpOnly, explicitPath);
01465             if (ports.count())
01466                 cookie.mPorts = ports;            
01467             addCookie(cookie);
01468         }
01469     }
01470 
01471     delete [] buffer;
01472     m_cookiesChanged = false;
01473     return success;
01474 }
01475 
01476 //
01477 // Save the cookie configuration
01478 //
01479 
01480 void KCookieJar::saveConfig(KConfig *_config)
01481 {
01482     if (!m_configChanged)
01483         return;
01484 
01485     KConfigGroup dlgGroup(_config, "Cookie Dialog");
01486     dlgGroup.writeEntry("PreferredPolicy", static_cast<int>(m_preferredPolicy));
01487     dlgGroup.writeEntry("ShowCookieDetails", m_showCookieDetails );
01488     KConfigGroup policyGroup(_config,"Cookie Policy");
01489     policyGroup.writeEntry("CookieGlobalAdvice", adviceToStr( m_globalAdvice));
01490 
01491     QStringList domainSettings;
01492     QStringListIterator it (m_domainList);    
01493     while (it.hasNext())
01494     {
01495          const QString& domain = it.next();
01496          KCookieAdvice advice = getDomainAdvice( domain);
01497          if (advice != KCookieDunno)
01498          {
01499              const QString value = domain + QL1C(':') + adviceToStr(advice);
01500              domainSettings.append(value);
01501          }
01502     }
01503     policyGroup.writeEntry("CookieDomainAdvice", domainSettings);
01504     _config->sync();
01505     m_configChanged = false;
01506 }
01507 
01508 
01509 //
01510 // Load the cookie configuration
01511 //
01512 
01513 void KCookieJar::loadConfig(KConfig *_config, bool reparse )
01514 {
01515     if ( reparse )
01516         _config->reparseConfiguration();
01517 
01518     KConfigGroup dlgGroup(_config, "Cookie Dialog");
01519     m_showCookieDetails = dlgGroup.readEntry( "ShowCookieDetails" , false );
01520     m_preferredPolicy = static_cast<KCookieDefaultPolicy>(dlgGroup.readEntry("PreferredPolicy", 0));
01521 
01522     KConfigGroup policyGroup(_config,"Cookie Policy");
01523     const QStringList domainSettings = policyGroup.readEntry("CookieDomainAdvice", QStringList());
01524     // Warning: those default values are duplicated in the kcm (kio/kcookiespolicies.cpp)
01525     m_rejectCrossDomainCookies = policyGroup.readEntry("RejectCrossDomainCookies", true);
01526     m_autoAcceptSessionCookies = policyGroup.readEntry("AcceptSessionCookies", true);
01527     m_ignoreCookieExpirationDate = policyGroup.readEntry("IgnoreExpirationDate", false);
01528     m_globalAdvice = strToAdvice(policyGroup.readEntry("CookieGlobalAdvice", QString(QL1S("Accept"))));
01529 
01530     // Reset current domain settings first.
01531     Q_FOREACH( const QString &domain, m_domainList )
01532         setDomainAdvice(domain, KCookieDunno);
01533 
01534     // Now apply the domain settings read from config file...
01535     for (QStringList::ConstIterator it = domainSettings.constBegin(), itEnd = domainSettings.constEnd();
01536          it != itEnd; ++it)
01537     {
01538         const QString& value = *it;
01539         const int sepPos = value.lastIndexOf(QL1C(':'));
01540         if (sepPos <= 0)
01541           continue;
01542 
01543         const QString domain(value.left(sepPos));
01544         KCookieAdvice advice = strToAdvice( value.mid(sepPos + 1) );
01545         setDomainAdvice(domain, advice);
01546     }
01547 }
01548 
01549 QDebug operator<<(QDebug dbg, const KHttpCookie& cookie)
01550 {
01551     dbg.nospace() << cookie.cookieStr(false);
01552     return dbg.space();
01553 }
01554 
01555 QDebug operator<<(QDebug dbg, const KHttpCookieList& list)
01556 {
01557     Q_FOREACH(const KHttpCookie& cookie, list)
01558         dbg << cookie;
01559     return dbg;
01560 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:37:22 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIOSlave

Skip menu "KIOSlave"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.9.5 API Reference

Skip menu "kdelibs-4.9.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal