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

KIOSlave

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

kdelibs

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.5
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