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