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