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

KIOSlave

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal