00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037 #include "kcookiejar.h"
00038
00039 #include <config.h>
00040 #include <sys/types.h>
00041 #include <sys/stat.h>
00042 #ifdef HAVE_SYS_PARAM_H
00043 #include <sys/param.h>
00044 #endif
00045 #include <fcntl.h>
00046 #include <unistd.h>
00047 #include <stdio.h>
00048 #include <string.h>
00049
00050 #ifdef USE_SOLARIS
00051 #include <strings.h>
00052 #endif
00053
00054 #include <stdlib.h>
00055
00056
00057
00058
00059 #include <QtCore/QString>
00060 #include <QtCore/QFile>
00061 #include <QtCore/QDir>
00062 #include <QtCore/QRegExp>
00063 #include <QtCore/QTextStream>
00064
00065 #include <kurl.h>
00066 #include <kdatetime.h>
00067 #include <kconfig.h>
00068 #include <kconfiggroup.h>
00069 #include <ksavefile.h>
00070 #include <kdebug.h>
00071
00072 #include <algorithm>
00073
00074
00075
00076
00077
00078
00079
00080 #undef MAX_COOKIE_LIMIT
00081
00082 #define MAX_COOKIES_PER_HOST 25
00083 #define READ_BUFFER_SIZE 8192
00084 #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]?)"
00085
00086
00087
00088
00089
00090
00091 #define L1(x) QLatin1String(x)
00092
00093 QString KCookieJar::adviceToStr(KCookieAdvice _advice)
00094 {
00095 switch( _advice )
00096 {
00097 case KCookieAccept: return L1("Accept");
00098 case KCookieReject: return L1("Reject");
00099 case KCookieAsk: return L1("Ask");
00100 default: return L1("Dunno");
00101 }
00102 }
00103
00104 KCookieAdvice KCookieJar::strToAdvice(const QString &_str)
00105 {
00106 if (_str.isEmpty())
00107 return KCookieDunno;
00108
00109 QString advice = _str.toLower();
00110
00111 if (advice == QLatin1String("accept"))
00112 return KCookieAccept;
00113 else if (advice == QLatin1String("reject"))
00114 return KCookieReject;
00115 else if (advice == QLatin1String("ask"))
00116 return KCookieAsk;
00117
00118 return KCookieDunno;
00119 }
00120
00121
00123
00124
00125
00126
00127 KHttpCookie::KHttpCookie(const QString &_host,
00128 const QString &_domain,
00129 const QString &_path,
00130 const QString &_name,
00131 const QString &_value,
00132 time_t _expireDate,
00133 int _protocolVersion,
00134 bool _secure,
00135 bool _httpOnly,
00136 bool _explicitPath) :
00137 mHost(_host),
00138 mDomain(_domain),
00139 mPath(_path.isEmpty() ? QString() : _path),
00140 mName(_name),
00141 mValue(_value),
00142 mExpireDate(_expireDate),
00143 mProtocolVersion(_protocolVersion),
00144 mSecure(_secure),
00145 mHttpOnly(_httpOnly),
00146 mExplicitPath(_explicitPath)
00147 {
00148 }
00149
00150
00151
00152
00153 bool KHttpCookie::isExpired(time_t currentDate) const
00154 {
00155 return (mExpireDate != 0) && (mExpireDate < currentDate);
00156 }
00157
00158
00159
00160
00161 QString KHttpCookie::cookieStr(bool useDOMFormat) const
00162 {
00163 QString result;
00164
00165 if (useDOMFormat || (mProtocolVersion == 0))
00166 {
00167 if ( !mName.isEmpty() )
00168 result = mName + '=';
00169 result += mValue;
00170 }
00171 else
00172 {
00173 result = mName + '=' + mValue;
00174 if (mExplicitPath)
00175 result += L1("; $Path=\"") + mPath + L1("\"");
00176 if (!mDomain.isEmpty())
00177 result += L1("; $Domain=\"") + mDomain + L1("\"");
00178 }
00179 return result;
00180 }
00181
00182
00183
00184 bool KHttpCookie::match(const QString &fqdn, const QStringList &domains,
00185 const QString &path) const
00186 {
00187
00188 if (mDomain.isEmpty())
00189 {
00190 if (fqdn != mHost)
00191 return false;
00192 }
00193 else if (!domains.contains(mDomain))
00194 {
00195 if (mDomain[0] == '.')
00196 return false;
00197
00198
00199 QString domain = '.' + mDomain;
00200 if ( !domains.contains( domain ) )
00201 if ( fqdn != mDomain )
00202 return false;
00203 }
00204
00205
00206 if (mPath.isEmpty())
00207 return true;
00208
00209
00210
00211
00212
00213
00214 if( path.startsWith(mPath) &&
00215 (
00216 (path.length() == mPath.length() ) ||
00217 mPath.endsWith('/') ||
00218 (path[mPath.length()] == '/')
00219 ))
00220 return true;
00221
00222 return false;
00223 }
00224
00225
00227
00228
00229
00230
00231
00232
00233 KCookieJar::KCookieJar()
00234 {
00235 m_globalAdvice = KCookieDunno;
00236 m_configChanged = false;
00237 m_cookiesChanged = false;
00238
00239 KConfig cfg( "khtml/domain_info", KConfig::NoGlobals, "data" );
00240 KConfigGroup group( &cfg, QString() );
00241 QStringList countries = group.readEntry( "twoLevelTLD", QStringList() );
00242 foreach ( const QString& country, countries ) {
00243 m_twoLevelTLD.insert( country, 1 );
00244 }
00245 }
00246
00247
00248
00249
00250
00251
00252 KCookieJar::~KCookieJar()
00253 {
00254 qDeleteAll(m_cookieDomains);
00255
00256 }
00257
00258
00259 static void removeDuplicateFromList(KHttpCookieList *list, KHttpCookie& cookiePtr, bool nameMatchOnly=false, bool updateWindowId=false)
00260 {
00261 QString domain1 = cookiePtr.domain();
00262 if (domain1.isEmpty())
00263 domain1 = cookiePtr.host();
00264
00265 QMutableListIterator<KHttpCookie> cookieIterator(*list);
00266 while (cookieIterator.hasNext()) {
00267 const KHttpCookie& cookie = cookieIterator.next();
00268 QString domain2 = cookie.domain();
00269 if (domain2.isEmpty())
00270 domain2 = cookie.host();
00271
00272 if (
00273 (cookiePtr.name() == cookie.name()) &&
00274 (
00275 nameMatchOnly ||
00276 ( (domain1 == domain2) && (cookiePtr.path() == cookie.path()) )
00277 )
00278 ) {
00279 if (updateWindowId) {
00280 Q_FOREACH(long windowId, cookie.windowIds()) {
00281 if (windowId && (!cookiePtr.windowIds().contains(windowId))) {
00282 cookiePtr.windowIds().append(windowId);
00283 }
00284 }
00285 }
00286 cookieIterator.remove();
00287 break;
00288 }
00289 }
00290 }
00291
00292
00293
00294
00295
00296
00297
00298 QString KCookieJar::findCookies(const QString &_url, bool useDOMFormat, long windowId, KHttpCookieList *pendingCookies)
00299 {
00300 QString cookieStr;
00301 QStringList domains;
00302 QString fqdn;
00303 QString path;
00304 KCookieAdvice advice = m_globalAdvice;
00305
00306 if (!parseUrl(_url, fqdn, path))
00307 return cookieStr;
00308
00309 bool secureRequest = _url.startsWith( L1("https://"), Qt::CaseInsensitive ) ||
00310 _url.startsWith( L1("webdavs://"), Qt::CaseInsensitive );
00311
00312 extractDomains(fqdn, domains);
00313
00314 KHttpCookieList allCookies;
00315
00316 for(QStringList::ConstIterator it = domains.constBegin();
00317 true;
00318 ++it)
00319 {
00320 KHttpCookieList *cookieList;
00321 if (it == domains.constEnd())
00322 {
00323 cookieList = pendingCookies;
00324 pendingCookies = 0;
00325 if (!cookieList)
00326 break;
00327 }
00328 else
00329 {
00330 QString key = (*it).isNull() ? L1("") : (*it);
00331 cookieList = m_cookieDomains.value(key);
00332 if (!cookieList)
00333 continue;
00334 }
00335
00336 if (cookieList->getAdvice() != KCookieDunno)
00337 advice = cookieList->getAdvice();
00338
00339 for (KHttpCookieList::iterator cookieIterator = cookieList->begin();
00340 cookieIterator != cookieList->end();
00341 ++cookieIterator ) {
00342 KHttpCookie& cookie = *cookieIterator;
00343
00344
00345
00346 if (advice == KCookieReject &&
00347 !(m_autoAcceptSessionCookies &&
00348 (m_ignoreCookieExpirationDate || cookie.expireDate() == 0)))
00349 continue;
00350
00351 if (!cookie.match(fqdn, domains, path))
00352 continue;
00353
00354 if( cookie.isSecure() && !secureRequest )
00355 continue;
00356
00357 if( cookie.isHttpOnly() && useDOMFormat )
00358 continue;
00359
00360
00361 if ( cookie.isExpired (time(0)) )
00362 {
00363
00364
00365
00366
00367 m_cookiesChanged = true;
00368 continue;
00369 }
00370
00371 if (windowId && (cookie.windowIds().indexOf(windowId) == -1))
00372 {
00373 cookie.windowIds().append(windowId);
00374 }
00375
00376 if (it == domains.constEnd())
00377 removeDuplicateFromList(&allCookies, cookie);
00378
00379 allCookies.append(cookie);
00380 }
00381 if (it == domains.constEnd())
00382 break;
00383 }
00384
00385 int cookieCount = 0;
00386
00387 int protVersion=0;
00388 Q_FOREACH(const KHttpCookie& cookie, allCookies) {
00389 if (cookie.protocolVersion() > protVersion)
00390 protVersion = cookie.protocolVersion();
00391 }
00392
00393 Q_FOREACH(const KHttpCookie& cookie, allCookies) {
00394 if (useDOMFormat) {
00395 if (cookieCount > 0)
00396 cookieStr += L1("; ");
00397 cookieStr += cookie.cookieStr(true);
00398 } else {
00399 if (cookieCount == 0) {
00400 cookieStr += L1("Cookie: ");
00401 if (protVersion > 0) {
00402 QString version;
00403 version.sprintf("$Version=%d; ", protVersion);
00404 cookieStr += version;
00405 }
00406 } else {
00407 cookieStr += L1("; ");
00408 }
00409 cookieStr += cookie.cookieStr(false);
00410 }
00411 cookieCount++;
00412 }
00413
00414 return cookieStr;
00415 }
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427 static const char * parseNameValue(const char *header,
00428 QString &Name,
00429 QString &Value,
00430 bool keepQuotes=false,
00431 bool rfcQuotes=false)
00432 {
00433 const char *s = header;
00434
00435 for(; (*s != '='); s++)
00436 {
00437 if ((*s=='\0') || (*s==';') || (*s=='\n'))
00438 {
00439
00440
00441 Name = "";
00442 Value = QString::fromLatin1(header);
00443 Value.truncate( s - header );
00444 Value = Value.trimmed();
00445 return (s);
00446 }
00447 }
00448
00449 Name = header;
00450 Name.truncate( s - header );
00451 Name = Name.trimmed();
00452
00453
00454 s++;
00455
00456
00457 for(; (*s == ' ') || (*s == '\t'); s++)
00458 {
00459 if ((*s=='\0') || (*s==';') || (*s=='\n'))
00460 {
00461
00462 Value = "";
00463 return (s);
00464 }
00465 }
00466
00467 if ((rfcQuotes || !keepQuotes) && (*s == '\"'))
00468 {
00469
00470 if (keepQuotes)
00471 header = s++;
00472 else
00473 header = ++s;
00474 for(;(*s != '\"');s++)
00475 {
00476 if ((*s=='\0') || (*s=='\n'))
00477 {
00478
00479 Value = QString::fromLatin1(header);
00480 Value.truncate(s - header);
00481 return (s);
00482 }
00483 }
00484 Value = QString::fromLatin1(header);
00485
00486 if (keepQuotes)
00487 Value.truncate( ++s - header );
00488 else
00489 Value.truncate( s++ - header );
00490
00491
00492 for(;; s++)
00493 {
00494 if ((*s=='\0') || (*s==';') || (*s=='\n'))
00495 break;
00496 }
00497 }
00498 else
00499 {
00500
00501 header = s;
00502 while ((*s != '\0') && (*s != ';') && (*s != '\n'))
00503 s++;
00504
00505 Value = QString::fromLatin1(header);
00506 Value.truncate( s - header );
00507 Value = Value.trimmed();
00508 }
00509 return (s);
00510
00511 }
00512
00513 void KCookieJar::stripDomain(const QString &_fqdn, QString &_domain)
00514 {
00515 QStringList domains;
00516 extractDomains(_fqdn, domains);
00517 if (domains.count() > 3)
00518 _domain = domains[3];
00519 else if ( domains.count() > 0 )
00520 _domain = domains[0];
00521 else
00522 _domain = L1("");
00523 }
00524
00525 QString KCookieJar::stripDomain(const KHttpCookie& cookie)
00526 {
00527 QString domain;
00528 if (cookie.domain().isEmpty())
00529 stripDomain( cookie.host(), domain);
00530 else
00531 stripDomain( cookie.domain(), domain);
00532 return domain;
00533 }
00534
00535 bool KCookieJar::parseUrl(const QString &_url,
00536 QString &_fqdn,
00537 QString &_path)
00538 {
00539 KUrl kurl(_url);
00540 if (!kurl.isValid())
00541 return false;
00542
00543 _fqdn = kurl.host().toLower();
00544 if (kurl.port() > 0)
00545 {
00546 if (((kurl.protocol() == L1("http")) && (kurl.port() != 80)) ||
00547 ((kurl.protocol() == L1("https")) && (kurl.port() != 443)))
00548 {
00549
00550 _fqdn = QString::fromLatin1("%1:%2").arg(kurl.port()).arg(_fqdn);
00551 }
00552 }
00553
00554
00555
00556
00557 if(_fqdn.contains('/') || _fqdn.contains('%'))
00558 {
00559 return false;
00560 }
00561
00562 _path = kurl.path();
00563 if (_path.isEmpty())
00564 _path = L1("/");
00565
00566 QRegExp exp(L1("[\\\\/]\\.\\.[\\\\/]"));
00567
00568 if (exp.indexIn(_path) != -1)
00569 return false;
00570
00571 return true;
00572 }
00573
00574 void KCookieJar::extractDomains(const QString &_fqdn,
00575 QStringList &_domains) const
00576 {
00577 if (_fqdn.isEmpty())
00578 return;
00579
00580
00581 if (_fqdn[0] == '[')
00582 {
00583 _domains.append( _fqdn );
00584 return;
00585 }
00586
00587 if ((_fqdn[0] >= '0') && (_fqdn[0] <= '9'))
00588 {
00589 if (_fqdn.indexOf(QRegExp(IP_ADDRESS_EXPRESSION)) > -1)
00590 {
00591 _domains.append( _fqdn );
00592 return;
00593 }
00594 }
00595
00596 QStringList partList = _fqdn.split('.', QString::SkipEmptyParts);
00597
00598 if (partList.count())
00599 partList.erase(partList.begin());
00600
00601 while(partList.count())
00602 {
00603
00604 if (partList.count() == 1)
00605 break;
00606
00607 if ((partList.count() == 2) && (m_twoLevelTLD.value(partList[1].toLower(), 0) == 1))
00608 {
00609
00610 break;
00611 }
00612
00613 if ((partList.count() == 2) && (partList[1].length() == 2))
00614 {
00615
00616
00617 if (partList[0].length() <= 2)
00618 break;
00619
00620
00621
00622 const QString t = partList[0].toLower();
00623 if ((t == "com") || (t == "net") || (t == "org") || (t == "gov") || (t == "edu") || (t == "mil") || (t == "int"))
00624 break;
00625 }
00626
00627 QString domain = partList.join(L1("."));
00628 _domains.append(domain);
00629 _domains.append('.' + domain);
00630 partList.erase(partList.begin());
00631 }
00632
00633
00634
00635 _domains.prepend( '.' + _fqdn );
00636 _domains.prepend( _fqdn );
00637 }
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649 static QString fixupDateTime(const QString& date)
00650 {
00651 QStringList list = date.split(' ');
00652 const int index = list.indexOf(QRegExp("[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}"));
00653
00654 if (index > -1 && (index+1) < list.count())
00655 {
00656 list.insert(index+1, list.takeAt(index));
00657 return list.join(" ");
00658 }
00659
00660 return date;
00661 }
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671 KHttpCookieList KCookieJar::makeCookies(const QString &_url,
00672 const QByteArray &cookie_headers,
00673 long windowId)
00674 {
00675 KHttpCookieList cookieList;
00676 KHttpCookieList cookieList2;
00677 KHttpCookieList::iterator lastCookie = cookieList.end();
00678 const char *cookieStr = cookie_headers.data();
00679 QString Name;
00680 QString Value;
00681 QString fqdn;
00682 QString path;
00683 bool crossDomain = false;
00684
00685 if (!parseUrl(_url, fqdn, path))
00686 {
00687
00688 return KHttpCookieList();
00689 }
00690 QString defaultPath;
00691 int i = path.lastIndexOf('/');
00692 if (i > 0)
00693 defaultPath = path.left(i);
00694
00695
00696 for(;;)
00697 {
00698
00699 if (strncmp(cookieStr, "Cross-Domain\n", 13) == 0)
00700 {
00701 cookieStr += 13;
00702 crossDomain = true;
00703 }
00704 else if (strncasecmp(cookieStr, "Set-Cookie:", 11) == 0)
00705 {
00706 cookieStr = parseNameValue(cookieStr+11, Name, Value, true);
00707
00708
00709
00710
00711
00712 KHttpCookie cookie(fqdn, L1(""), defaultPath, Name, Value);
00713 if (windowId)
00714 cookie.mWindowIds.append(windowId);
00715 cookie.mCrossDomain = crossDomain;
00716
00717
00718 cookieList.append(cookie);
00719 lastCookie = cookieList.end(); --lastCookie;
00720 }
00721 else if (strncasecmp(cookieStr, "Set-Cookie2:", 12) == 0)
00722 {
00723
00724 cookieStr = parseNameValue(cookieStr+12, Name, Value, true, true);
00725
00726
00727
00728
00729
00730 KHttpCookie cookie(fqdn, L1(""), defaultPath, Name, Value);
00731 if (windowId)
00732 cookie.mWindowIds.append(windowId);
00733 cookie.mCrossDomain = crossDomain;
00734
00735
00736 cookieList2.append(cookie);
00737 lastCookie = cookieList2.end(); --lastCookie;
00738 }
00739 else
00740 {
00741
00742 while (*cookieStr && *cookieStr != '\n')
00743 cookieStr++;
00744
00745 if (*cookieStr == '\n')
00746 cookieStr++;
00747
00748 if (!*cookieStr)
00749 break;
00750 else
00751 continue;
00752 }
00753
00754 while ((*cookieStr == ';') || (*cookieStr == ' '))
00755 {
00756 cookieStr++;
00757
00758
00759 cookieStr = parseNameValue(cookieStr, Name, Value);
00760
00761 QString cName = Name.toLower();
00762 if (cName == "domain")
00763 {
00764 QString dom = Value.toLower();
00765
00766
00767 if(dom.length() && dom[0] != '.')
00768 dom.prepend(".");
00769
00770 if(dom.length() > 2 && dom[dom.length()-1] == '.')
00771 dom = dom.left(dom.length()-1);
00772
00773 if(dom.count('.') > 1 || dom == ".local")
00774 lastCookie->mDomain = dom;
00775 }
00776 else if (cName == "max-age")
00777 {
00778 int max_age = Value.toInt();
00779 if (max_age == 0)
00780 lastCookie->mExpireDate = 1;
00781 else
00782 lastCookie->mExpireDate = time(0)+max_age;
00783 }
00784 else if (cName == "expires")
00785 {
00786
00787 lastCookie->mExpireDate = KDateTime::fromString(Value, KDateTime::RFCDate).toTime_t();
00788
00789
00790
00791 if (lastCookie->mExpireDate == -1)
00792 lastCookie->mExpireDate = KDateTime::fromString(fixupDateTime(Value), KDateTime::RFCDate).toTime_t();
00793
00794
00795 if (lastCookie->mExpireDate == -1)
00796 lastCookie->mExpireDate = 0;
00797 }
00798 else if (cName == "path")
00799 {
00800 if (Value.isEmpty())
00801 lastCookie->mPath.clear();
00802 else
00803 lastCookie->mPath = QUrl::fromPercentEncoding(Value.toLatin1());
00804 lastCookie->mExplicitPath = true;
00805 }
00806 else if (cName == "version")
00807 {
00808 lastCookie->mProtocolVersion = Value.toInt();
00809 }
00810 else if ((cName == "secure") ||
00811 (cName.isEmpty() && Value.toLower() == L1("secure")))
00812 {
00813 lastCookie->mSecure = true;
00814 }
00815 else if ((cName == "httponly") ||
00816 (cName.isEmpty() && Value.toLower() == L1("httponly")))
00817 {
00818 lastCookie->mHttpOnly = true;
00819 }
00820 }
00821
00822 if (*cookieStr == '\0')
00823 break;
00824
00825
00826 cookieStr++;
00827 }
00828
00829
00830 while(!cookieList2.isEmpty()) {
00831 lastCookie = cookieList2.begin();
00832 removeDuplicateFromList(&cookieList, *lastCookie, true);
00833 cookieList.append(*lastCookie);
00834 cookieList2.removeFirst();
00835 }
00836
00837 return cookieList;
00838 }
00839
00846 KHttpCookieList KCookieJar::makeDOMCookies(const QString &_url,
00847 const QByteArray &cookie_domstring,
00848 long windowId)
00849 {
00850
00851 KHttpCookieList cookieList;
00852
00853 const char *cookieStr = cookie_domstring.data();
00854 QString fqdn;
00855 QString path;
00856
00857 if (!parseUrl(_url, fqdn, path))
00858 {
00859
00860 return KHttpCookieList();
00861 }
00862
00863 QString Name;
00864 QString Value;
00865
00866 while(*cookieStr)
00867 {
00868 cookieStr = parseNameValue(cookieStr, Name, Value);
00869
00870
00871
00872
00873 KHttpCookie cookie(fqdn, QString(), QString(),
00874 Name, Value );
00875 if (windowId)
00876 cookie.mWindowIds.append(windowId);
00877
00878 cookieList.append(cookie);
00879
00880 if (*cookieStr != '\0')
00881 cookieStr++;
00882 }
00883
00884 return cookieList;
00885 }
00886
00887
00889
00890
00891 static bool compareCookies(const KHttpCookie& item1, const KHttpCookie& item2)
00892 {
00893 return item1.path().length() > item2.path().length();
00894 }
00895
00896
00897 #ifdef MAX_COOKIE_LIMIT
00898 static void makeRoom(KHttpCookieList *cookieList, KHttpCookiePtr &cookiePtr)
00899 {
00900
00901 KHttpCookiePtr lastCookie = 0;
00902 for(KHttpCookiePtr cookie = cookieList->first(); cookie; cookie = cookieList->next())
00903 {
00904 if (compareCookies(cookie, cookiePtr))
00905 break;
00906 lastCookie = cookie;
00907 }
00908 if (!lastCookie)
00909 lastCookie = cookieList->first();
00910 cookieList->removeRef(lastCookie);
00911 }
00912 #endif
00913
00914
00915
00916
00917 void KCookieJar::addCookie(KHttpCookie &cookie)
00918 {
00919 QStringList domains;
00920 KHttpCookieList *cookieList = 0L;
00921
00922
00923
00924
00925 extractDomains( cookie.host(), domains );
00926 for ( QStringList::ConstIterator it = domains.constBegin();
00927 (it != domains.constEnd() && !cookieList);
00928 ++it )
00929 {
00930 QString key = (*it).isNull() ? QString::fromLatin1("") : (*it);
00931 KHttpCookieList *list= m_cookieDomains.value(key);
00932 if ( !list ) continue;
00933
00934 removeDuplicateFromList(list, cookie, false, true);
00935 }
00936
00937 QString domain = stripDomain( cookie );
00938 QString key = domain.isNull() ? QString::fromLatin1("") : domain;
00939 cookieList = m_cookieDomains.value(key);
00940 if (!cookieList)
00941 {
00942
00943 cookieList = new KHttpCookieList();
00944
00945
00946
00947
00948 cookieList->setAdvice( KCookieDunno );
00949
00950 m_cookieDomains.insert( domain, cookieList);
00951
00952
00953 m_domainList.append(domain);
00954 }
00955
00956
00957
00958 if (!cookie.isExpired(time(0)))
00959 {
00960 #ifdef MAX_COOKIE_LIMIT
00961 if (cookieList->count() >= MAX_COOKIES_PER_HOST)
00962 makeRoom(cookieList, cookie);
00963 #endif
00964 cookieList->push_back(cookie);
00965
00966
00967 qStableSort(cookieList->begin(), cookieList->end(), compareCookies);
00968
00969 m_cookiesChanged = true;
00970 }
00971 }
00972
00973
00974
00975
00976
00977 KCookieAdvice KCookieJar::cookieAdvice(KHttpCookie& cookie)
00978 {
00979 if (m_rejectCrossDomainCookies && cookie.isCrossDomain())
00980 return KCookieReject;
00981
00982 QStringList domains;
00983 extractDomains(cookie.host(), domains);
00984
00985
00986
00987
00988 if (!cookie.domain().isEmpty())
00989 {
00990 if (!domains.contains(cookie.domain()) &&
00991 !cookie.domain().endsWith('.'+cookie.host()))
00992 cookie.fixDomain(QString());
00993 }
00994
00995 if (m_autoAcceptSessionCookies && (cookie.expireDate() == 0 ||
00996 m_ignoreCookieExpirationDate))
00997 return KCookieAccept;
00998
00999 KCookieAdvice advice = KCookieDunno;
01000 bool isFQDN = true;
01001 QStringList::Iterator it = domains.begin();
01002 while( (advice == KCookieDunno) && (it != domains.end()))
01003 {
01004 QString domain = *it;
01005
01006 if ( domain.startsWith('.') || isFQDN )
01007 {
01008 isFQDN = false;
01009 KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01010 if (cookieList)
01011 advice = cookieList->getAdvice();
01012 }
01013 domains.erase(it);
01014 it = domains.begin();
01015 }
01016
01017 if (advice == KCookieDunno)
01018 advice = m_globalAdvice;
01019
01020 return advice;
01021 }
01022
01023
01024
01025
01026
01027 KCookieAdvice KCookieJar::getDomainAdvice(const QString &_domain)
01028 {
01029 KHttpCookieList *cookieList = m_cookieDomains.value(_domain);
01030 KCookieAdvice advice;
01031
01032 if (cookieList)
01033 {
01034 advice = cookieList->getAdvice();
01035 }
01036 else
01037 {
01038 advice = KCookieDunno;
01039 }
01040
01041 return advice;
01042 }
01043
01044
01045
01046
01047
01048 void KCookieJar::setDomainAdvice(const QString &_domain, KCookieAdvice _advice)
01049 {
01050 QString domain(_domain);
01051 KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01052
01053 if (cookieList)
01054 {
01055 if (cookieList->getAdvice() != _advice)
01056 {
01057 m_configChanged = true;
01058
01059 cookieList->setAdvice( _advice);
01060 }
01061
01062 if ((cookieList->isEmpty()) &&
01063 (_advice == KCookieDunno))
01064 {
01065
01066 delete m_cookieDomains.take(domain);
01067 m_domainList.removeAll(domain);
01068 }
01069 }
01070 else
01071 {
01072
01073 if (_advice != KCookieDunno)
01074 {
01075
01076 m_configChanged = true;
01077
01078 cookieList = new KHttpCookieList();
01079 cookieList->setAdvice(_advice);
01080 m_cookieDomains.insert(domain, cookieList);
01081
01082 m_domainList.append( domain);
01083 }
01084 }
01085 }
01086
01087
01088
01089
01090
01091 void KCookieJar::setDomainAdvice(const KHttpCookie& cookie, KCookieAdvice _advice)
01092 {
01093 QString domain;
01094 stripDomain(cookie.host(), domain);
01095
01096 setDomainAdvice(domain, _advice);
01097 }
01098
01099
01100
01101
01102 void KCookieJar::setGlobalAdvice(KCookieAdvice _advice)
01103 {
01104 if (m_globalAdvice != _advice)
01105 m_configChanged = true;
01106 m_globalAdvice = _advice;
01107 }
01108
01109
01110
01111
01112 const QStringList& KCookieJar::getDomainList()
01113 {
01114 return m_domainList;
01115 }
01116
01117
01118
01119
01120 KHttpCookieList *KCookieJar::getCookieList(const QString & _domain,
01121 const QString & _fqdn )
01122 {
01123 QString domain;
01124
01125 if (_domain.isEmpty())
01126 stripDomain( _fqdn, domain );
01127 else
01128 domain = _domain;
01129
01130 return m_cookieDomains.value(domain);
01131 }
01132
01133
01134
01135
01136
01137 void KCookieJar::eatCookie(KHttpCookieList::iterator cookieIterator)
01138 {
01139 const KHttpCookie& cookie = *cookieIterator;
01140 QString domain = stripDomain(cookie);
01141 KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01142
01143 if (cookieList) {
01144
01145 cookieList->erase(cookieIterator);
01146
01147 if ((cookieList->isEmpty()) &&
01148 (cookieList->getAdvice() == KCookieDunno))
01149 {
01150
01151 delete m_cookieDomains.take(domain);
01152
01153 m_domainList.removeAll(domain);
01154 }
01155 }
01156 }
01157
01158 void KCookieJar::eatCookiesForDomain(const QString &domain)
01159 {
01160 KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01161 if (!cookieList || cookieList->isEmpty()) return;
01162
01163 cookieList->clear();
01164 if (cookieList->getAdvice() == KCookieDunno)
01165 {
01166
01167 delete m_cookieDomains.take(domain);
01168 m_domainList.removeAll(domain);
01169 }
01170 m_cookiesChanged = true;
01171 }
01172
01173 void KCookieJar::eatSessionCookies( long windowId )
01174 {
01175 if (!windowId)
01176 return;
01177
01178 QStringList::const_iterator it=m_domainList.constBegin();
01179 for ( ; it != m_domainList.constEnd(); ++it )
01180 eatSessionCookies( *it, windowId, false );
01181 }
01182
01183 void KCookieJar::eatAllCookies()
01184 {
01185 Q_FOREACH(const QString& domain, m_domainList) {
01186
01187 eatCookiesForDomain(domain);
01188 }
01189 }
01190
01191 void KCookieJar::eatSessionCookies( const QString& fqdn, long windowId,
01192 bool isFQDN )
01193 {
01194 KHttpCookieList* cookieList;
01195 if ( !isFQDN )
01196 cookieList = m_cookieDomains.value(fqdn);
01197 else {
01198 QString domain;
01199 stripDomain( fqdn, domain );
01200 cookieList = m_cookieDomains.value(domain);
01201 }
01202
01203 if (cookieList) {
01204 QMutableListIterator<KHttpCookie> cookieIterator(*cookieList);
01205 while (cookieIterator.hasNext()) {
01206 KHttpCookie& cookie = cookieIterator.next();
01207 if ((cookie.expireDate() != 0) && !m_ignoreCookieExpirationDate) {
01208 continue;
01209 }
01210
01211 QList<long> &ids = cookie.windowIds();
01212
01213 #ifndef NDEBUG
01214 if (ids.contains(windowId)) {
01215 if (ids.count() > 1)
01216 kDebug() << "removing window id" << windowId << "from session cookie";
01217 else
01218 kDebug() << "deleting session cookie";
01219 }
01220 #endif
01221 if (!ids.removeAll(windowId) || !ids.isEmpty()) {
01222 continue;
01223 }
01224 cookieIterator.remove();
01225 }
01226 }
01227 }
01228
01229
01230
01231
01232
01233 bool KCookieJar::saveCookies(const QString &_filename)
01234 {
01235 KSaveFile saveFile(_filename);
01236
01237 if (!saveFile.open())
01238 return false;
01239 saveFile.setPermissions(QFile::ReadUser|QFile::WriteUser);
01240
01241 QTextStream ts(&saveFile);
01242
01243 time_t curTime = time(0);
01244
01245 ts << "# KDE Cookie File v2\n#\n";
01246
01247 QString s;
01248 s.sprintf("%-20s %-20s %-12s %-10s %-4s %-20s %-4s %s\n",
01249 "# Host", "Domain", "Path", "Exp.date", "Prot",
01250 "Name", "Sec", "Value");
01251 ts << s.toLatin1().constData();
01252
01253 for ( QStringList::const_iterator it=m_domainList.constBegin(); it != m_domainList.constEnd();
01254 it++ )
01255 {
01256 const QString &domain = *it;
01257 bool domainPrinted = false;
01258
01259 KHttpCookieList *cookieList = m_cookieDomains.value(domain);
01260 QMutableListIterator<KHttpCookie> cookieIterator(*cookieList);
01261 while (cookieIterator.hasNext()) {
01262 const KHttpCookie& cookie = cookieIterator.next();
01263 if (cookie.isExpired(curTime)) {
01264
01265 cookieIterator.remove();
01266 } else if (cookie.expireDate() != 0 && !m_ignoreCookieExpirationDate) {
01267
01268 if (!domainPrinted) {
01269 domainPrinted = true;
01270 ts << '[' << domain.toLocal8Bit().data() << "]\n";
01271 }
01272
01273 QString path = L1("\"");
01274 path += cookie.path();
01275 path += '"';
01276 QString domain = L1("\"");
01277 domain += cookie.domain();
01278 domain += '"';
01279
01280 s.sprintf("%-20s %-20s %-12s %10lu %3d %-20s %-4i %s\n",
01281 cookie.host().toLatin1().constData(), domain.toLatin1().constData(),
01282 path.toLatin1().constData(), (unsigned long) cookie.expireDate(),
01283 cookie.protocolVersion(),
01284 cookie.name().isEmpty() ? cookie.value().toLatin1().constData() : cookie.name().toLatin1().constData(),
01285 (cookie.isSecure() ? 1 : 0) + (cookie.isHttpOnly() ? 2 : 0) +
01286 (cookie.hasExplicitPath() ? 4 : 0) + (cookie.name().isEmpty() ? 8 : 0),
01287 cookie.value().toLatin1().constData());
01288 ts << s.toLatin1().constData();
01289 }
01290 }
01291 }
01292
01293 return saveFile.finalize();
01294 }
01295
01296 static const char *parseField(char* &buffer, bool keepQuotes=false)
01297 {
01298 char *result;
01299 if (!keepQuotes && (*buffer == '\"'))
01300 {
01301
01302 buffer++;
01303 result = buffer;
01304 while((*buffer != '\"') && (*buffer))
01305 buffer++;
01306 }
01307 else
01308 {
01309
01310 result = buffer;
01311 while((*buffer != ' ') && (*buffer != '\t') && (*buffer != '\n') && (*buffer))
01312 buffer++;
01313 }
01314
01315 if (!*buffer)
01316 return result;
01317 *buffer++ = '\0';
01318
01319
01320 while((*buffer == ' ') || (*buffer == '\t') || (*buffer == '\n'))
01321 buffer++;
01322
01323 return result;
01324 }
01325
01326
01327
01328
01329
01330
01331 bool KCookieJar::loadCookies(const QString &_filename)
01332 {
01333 FILE *fStream = fopen( QFile::encodeName(_filename), "r");
01334 if (fStream == 0)
01335 {
01336 return false;
01337 }
01338
01339 time_t curTime = time(0);
01340
01341 char *buffer = new char[READ_BUFFER_SIZE];
01342
01343 bool err = false;
01344 err = (fgets(buffer, READ_BUFFER_SIZE, fStream) == 0);
01345
01346 int version = 1;
01347 if (!err)
01348 {
01349 if (strcmp(buffer, "# KDE Cookie File\n") == 0)
01350 {
01351
01352 }
01353 else if (sscanf(buffer, "# KDE Cookie File v%d\n", &version) != 1)
01354 {
01355 err = true;
01356 }
01357 }
01358
01359 if (!err)
01360 {
01361 while(fgets(buffer, READ_BUFFER_SIZE, fStream) != 0)
01362 {
01363 char *line = buffer;
01364
01365 if ((line[0] == '#') || (line[0] == '['))
01366 continue;
01367
01368 const QString host = QString::fromLatin1( parseField(line) );
01369 const QString domain = QString::fromLatin1( parseField(line) );
01370 if (host.isEmpty() && domain.isEmpty())
01371 continue;
01372 const QString path = QString::fromLatin1( parseField(line) );
01373 const QString expStr = QString::fromLatin1( parseField(line) );
01374 if (expStr.isEmpty()) continue;
01375 const int expDate = expStr.toInt();
01376 const QString verStr = QString::fromLatin1( parseField(line) );
01377 if (verStr.isEmpty()) continue;
01378 int protVer = verStr.toInt();
01379 QString name = QString::fromLatin1( parseField(line) );
01380 bool keepQuotes = false;
01381 bool secure = false;
01382 bool httpOnly = false;
01383 bool explicitPath = false;
01384 const char *value = 0;
01385 if ((version == 2) || (protVer >= 200))
01386 {
01387 if (protVer >= 200)
01388 protVer -= 200;
01389 int i = atoi( parseField(line) );
01390 secure = i & 1;
01391 httpOnly = i & 2;
01392 explicitPath = i & 4;
01393 if (i & 8)
01394 name = "";
01395 line[strlen(line)-1] = '\0';
01396 value = line;
01397 }
01398 else
01399 {
01400 if (protVer >= 100)
01401 {
01402 protVer -= 100;
01403 keepQuotes = true;
01404 }
01405 value = parseField(line, keepQuotes);
01406 secure = atoi( parseField(line) );
01407 }
01408
01409
01410 if (!value) continue;
01411
01412
01413 if ((expDate == 0) || (expDate < curTime))
01414 continue;
01415
01416 KHttpCookie cookie(host,
01417 domain,
01418 path,
01419 name,
01420 value,
01421 expDate, protVer,
01422 secure, httpOnly, explicitPath);
01423 addCookie(cookie);
01424 }
01425 }
01426 delete [] buffer;
01427 m_cookiesChanged = false;
01428
01429 fclose( fStream);
01430 return err;
01431 }
01432
01433
01434
01435
01436
01437 void KCookieJar::saveConfig(KConfig *_config)
01438 {
01439 if (!m_configChanged)
01440 return;
01441
01442 KConfigGroup dlgGroup(_config, "Cookie Dialog");
01443 dlgGroup.writeEntry("PreferredPolicy", m_preferredPolicy);
01444 dlgGroup.writeEntry("ShowCookieDetails", m_showCookieDetails );
01445 KConfigGroup policyGroup(_config,"Cookie Policy");
01446 policyGroup.writeEntry("CookieGlobalAdvice", adviceToStr( m_globalAdvice));
01447
01448 QStringList domainSettings;
01449 for ( QStringList::const_iterator it=m_domainList.constBegin();
01450 it != m_domainList.constEnd();
01451 ++it )
01452 {
01453 const QString &domain = *it;
01454 KCookieAdvice advice = getDomainAdvice( domain);
01455 if (advice != KCookieDunno)
01456 {
01457 QString value(domain);
01458 value += ':';
01459 value += adviceToStr(advice);
01460 domainSettings.append(value);
01461 }
01462 }
01463 policyGroup.writeEntry("CookieDomainAdvice", domainSettings);
01464 _config->sync();
01465 m_configChanged = false;
01466 }
01467
01468
01469
01470
01471
01472
01473 void KCookieJar::loadConfig(KConfig *_config, bool reparse )
01474 {
01475 if ( reparse )
01476 _config->reparseConfiguration();
01477
01478 KConfigGroup dlgGroup(_config, "Cookie Dialog");
01479 m_showCookieDetails = dlgGroup.readEntry( "ShowCookieDetails" , false );
01480 m_preferredPolicy = dlgGroup.readEntry( "PreferredPolicy", 0 );
01481
01482 KConfigGroup policyGroup(_config,"Cookie Policy");
01483 const QStringList domainSettings = policyGroup.readEntry("CookieDomainAdvice", QStringList());
01484 m_rejectCrossDomainCookies = policyGroup.readEntry("RejectCrossDomainCookies", true);
01485 m_autoAcceptSessionCookies = policyGroup.readEntry("AcceptSessionCookies", true);
01486 m_ignoreCookieExpirationDate = policyGroup.readEntry("IgnoreExpirationDate", false);
01487 QString value = policyGroup.readEntry("CookieGlobalAdvice", QString::fromLatin1("Accept"));
01488 m_globalAdvice = strToAdvice(value);
01489
01490
01491
01492 const QStringList domains = m_domainList;
01493 foreach( const QString &domain, domains )
01494 {
01495 setDomainAdvice(domain, KCookieDunno);
01496 }
01497
01498
01499 for ( QStringList::const_iterator it=domainSettings.begin();
01500 it != domainSettings.end(); )
01501 {
01502 const QString &value = *it++;
01503
01504 int sepPos = value.lastIndexOf(':');
01505
01506 if (sepPos <= 0)
01507 continue;
01508
01509 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 }