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 }
KDE 4.7 API Reference