kcookieserver.cpp
00001 /* 00002 This file is part of KDE 00003 00004 Copyright (C) 1998-2000 Waldo Bastian (bastian@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 00010 copies of the Software, and to permit persons to whom the Software is 00011 furnished to do so, 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 00020 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 00021 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00022 */ 00023 //---------------------------------------------------------------------------- 00024 // 00025 // KDE Cookie Server 00026 // $Id: kcookieserver.cpp 707375 2007-09-01 17:19:13Z adawit $ 00027 00028 #define SAVE_DELAY 3 // Save after 3 minutes 00029 00030 #include <unistd.h> 00031 00032 #include <qtimer.h> 00033 #include <qptrlist.h> 00034 #include <qfile.h> 00035 00036 #include <dcopclient.h> 00037 00038 #include <kconfig.h> 00039 #include <kdebug.h> 00040 #include <kapplication.h> 00041 #include <kcmdlineargs.h> 00042 #include <kstandarddirs.h> 00043 00044 #include "kcookiejar.h" 00045 #include "kcookiewin.h" 00046 #include "kcookieserver.h" 00047 00048 extern "C" { 00049 KDE_EXPORT KDEDModule *create_kcookiejar(const QCString &name) 00050 { 00051 return new KCookieServer(name); 00052 } 00053 } 00054 00055 00056 // Cookie field indexes 00057 enum CookieDetails { CF_DOMAIN=0, CF_PATH, CF_NAME, CF_HOST, 00058 CF_VALUE, CF_EXPIRE, CF_PROVER, CF_SECURE }; 00059 00060 00061 class CookieRequest { 00062 public: 00063 DCOPClient *client; 00064 DCOPClientTransaction *transaction; 00065 QString url; 00066 bool DOM; 00067 long windowId; 00068 }; 00069 00070 template class QPtrList<CookieRequest>; 00071 00072 class RequestList : public QPtrList<CookieRequest> 00073 { 00074 public: 00075 RequestList() : QPtrList<CookieRequest>() { } 00076 }; 00077 00078 KCookieServer::KCookieServer(const QCString &name) 00079 :KDEDModule(name) 00080 { 00081 mOldCookieServer = new DCOPClient(); // backwards compatibility. 00082 mOldCookieServer->registerAs("kcookiejar", false); 00083 mOldCookieServer->setDaemonMode( true ); 00084 mCookieJar = new KCookieJar; 00085 mPendingCookies = new KHttpCookieList; 00086 mPendingCookies->setAutoDelete(true); 00087 mRequestList = new RequestList; 00088 mAdvicePending = false; 00089 mTimer = new QTimer(); 00090 connect( mTimer, SIGNAL( timeout()), SLOT( slotSave())); 00091 mConfig = new KConfig("kcookiejarrc"); 00092 mCookieJar->loadConfig( mConfig ); 00093 00094 QString filename = locateLocal("data", "kcookiejar/cookies"); 00095 00096 // Stay backwards compatible! 00097 QString filenameOld = locate("data", "kfm/cookies"); 00098 if (!filenameOld.isEmpty()) 00099 { 00100 mCookieJar->loadCookies( filenameOld ); 00101 if (mCookieJar->saveCookies( filename)) 00102 { 00103 unlink(QFile::encodeName(filenameOld)); // Remove old kfm cookie file 00104 } 00105 } 00106 else 00107 { 00108 mCookieJar->loadCookies( filename); 00109 } 00110 connect(this, SIGNAL(windowUnregistered(long)), 00111 this, SLOT(slotDeleteSessionCookies(long))); 00112 } 00113 00114 KCookieServer::~KCookieServer() 00115 { 00116 if (mCookieJar->changed()) 00117 slotSave(); 00118 delete mOldCookieServer; 00119 delete mCookieJar; 00120 delete mTimer; 00121 delete mPendingCookies; 00122 delete mConfig; 00123 } 00124 00125 bool KCookieServer::cookiesPending( const QString &url, KHttpCookieList *cookieList ) 00126 { 00127 QString fqdn; 00128 QStringList domains; 00129 QString path; 00130 // Check whether 'url' has cookies on the pending list 00131 if (mPendingCookies->isEmpty()) 00132 return false; 00133 if (!KCookieJar::parseURL(url, fqdn, path)) 00134 return false; 00135 00136 mCookieJar->extractDomains( fqdn, domains ); 00137 for( KHttpCookie *cookie = mPendingCookies->first(); 00138 cookie != 0L; 00139 cookie = mPendingCookies->next()) 00140 { 00141 if (cookie->match( fqdn, domains, path)) 00142 { 00143 if (!cookieList) 00144 return true; 00145 cookieList->append(cookie); 00146 } 00147 } 00148 if (!cookieList) 00149 return false; 00150 return cookieList->isEmpty(); 00151 } 00152 00153 void KCookieServer::addCookies( const QString &url, const QCString &cookieHeader, 00154 long windowId, bool useDOMFormat ) 00155 { 00156 KHttpCookieList cookieList; 00157 if (useDOMFormat) 00158 cookieList = mCookieJar->makeDOMCookies(url, cookieHeader, windowId); 00159 else 00160 cookieList = mCookieJar->makeCookies(url, cookieHeader, windowId); 00161 00162 checkCookies(&cookieList); 00163 00164 for(KHttpCookiePtr cookie = cookieList.first(); cookie; cookie = cookieList.first()) 00165 mPendingCookies->append(cookieList.take()); 00166 00167 if (!mAdvicePending) 00168 { 00169 mAdvicePending = true; 00170 while (!mPendingCookies->isEmpty()) 00171 { 00172 checkCookies(0); 00173 } 00174 mAdvicePending = false; 00175 } 00176 } 00177 00178 void KCookieServer::checkCookies( KHttpCookieList *cookieList) 00179 { 00180 KHttpCookieList *list; 00181 00182 if (cookieList) 00183 list = cookieList; 00184 else 00185 list = mPendingCookies; 00186 00187 KHttpCookiePtr cookie = list->first(); 00188 while (cookie) 00189 { 00190 kdDebug(7104) << "checkCookies: Asking cookie advice for " << cookie->host() << endl; 00191 KCookieAdvice advice = mCookieJar->cookieAdvice(cookie); 00192 switch(advice) 00193 { 00194 case KCookieAccept: 00195 list->take(); 00196 mCookieJar->addCookie(cookie); 00197 cookie = list->current(); 00198 break; 00199 00200 case KCookieReject: 00201 list->take(); 00202 delete cookie; 00203 cookie = list->current(); 00204 break; 00205 00206 default: 00207 cookie = list->next(); 00208 break; 00209 } 00210 } 00211 00212 if (cookieList || list->isEmpty()) 00213 return; 00214 00215 KHttpCookiePtr currentCookie = mPendingCookies->first(); 00216 00217 KHttpCookieList currentList; 00218 currentList.append(currentCookie); 00219 QString currentHost = currentCookie->host(); 00220 00221 cookie = mPendingCookies->next(); 00222 while (cookie) 00223 { 00224 if (cookie->host() == currentHost) 00225 { 00226 currentList.append(cookie); 00227 } 00228 cookie = mPendingCookies->next(); 00229 } 00230 00231 KCookieWin *kw = new KCookieWin( 0L, currentList, 00232 mCookieJar->preferredDefaultPolicy(), 00233 mCookieJar->showCookieDetails() ); 00234 KCookieAdvice userAdvice = kw->advice(mCookieJar, currentCookie); 00235 delete kw; 00236 // Save the cookie config if it has changed 00237 mCookieJar->saveConfig( mConfig ); 00238 00239 // Apply the user's choice to all cookies that are currently 00240 // queued for this host. 00241 cookie = mPendingCookies->first(); 00242 while (cookie) 00243 { 00244 if (cookie->host() == currentHost) 00245 { 00246 switch(userAdvice) 00247 { 00248 case KCookieAccept: 00249 mPendingCookies->take(); 00250 mCookieJar->addCookie(cookie); 00251 cookie = mPendingCookies->current(); 00252 break; 00253 00254 case KCookieReject: 00255 mPendingCookies->take(); 00256 delete cookie; 00257 cookie = mPendingCookies->current(); 00258 break; 00259 00260 default: 00261 qWarning(__FILE__":%d Problem!", __LINE__); 00262 cookie = mPendingCookies->next(); 00263 break; 00264 } 00265 } 00266 else 00267 { 00268 cookie = mPendingCookies->next(); 00269 } 00270 } 00271 00272 00273 // Check if we can handle any request 00274 for ( CookieRequest *request = mRequestList->first(); request;) 00275 { 00276 if (!cookiesPending( request->url )) 00277 { 00278 QCString replyType; 00279 QByteArray replyData; 00280 QString res = mCookieJar->findCookies( request->url, request->DOM, request->windowId ); 00281 00282 QDataStream stream2(replyData, IO_WriteOnly); 00283 stream2 << res; 00284 replyType = "QString"; 00285 request->client->endTransaction( request->transaction, 00286 replyType, replyData); 00287 CookieRequest *tmp = request; 00288 request = mRequestList->next(); 00289 mRequestList->removeRef( tmp ); 00290 delete tmp; 00291 } 00292 else 00293 { 00294 request = mRequestList->next(); 00295 } 00296 } 00297 if (mCookieJar->changed()) 00298 saveCookieJar(); 00299 } 00300 00301 void KCookieServer::slotSave() 00302 { 00303 QString filename = locateLocal("data", "kcookiejar/cookies"); 00304 mCookieJar->saveCookies(filename); 00305 } 00306 00307 void KCookieServer::saveCookieJar() 00308 { 00309 if( mTimer->isActive() ) 00310 return; 00311 00312 mTimer->start( 1000*60*SAVE_DELAY, true ); 00313 } 00314 00315 void KCookieServer::putCookie( QStringList& out, KHttpCookie *cookie, 00316 const QValueList<int>& fields ) 00317 { 00318 QValueList<int>::ConstIterator i = fields.begin(); 00319 for ( ; i != fields.end(); ++i ) 00320 { 00321 switch(*i) 00322 { 00323 case CF_DOMAIN : 00324 out << cookie->domain(); 00325 break; 00326 case CF_NAME : 00327 out << cookie->name(); 00328 break; 00329 case CF_PATH : 00330 out << cookie->path(); 00331 break; 00332 case CF_HOST : 00333 out << cookie->host(); 00334 break; 00335 case CF_VALUE : 00336 out << cookie->value(); 00337 break; 00338 case CF_EXPIRE : 00339 out << QString::number(cookie->expireDate()); 00340 break; 00341 case CF_PROVER : 00342 out << QString::number(cookie->protocolVersion()); 00343 break; 00344 case CF_SECURE : 00345 out << QString::number( cookie->isSecure() ? 1 : 0 ); 00346 break; 00347 default : 00348 out << QString::null; 00349 } 00350 } 00351 } 00352 00353 bool KCookieServer::cookieMatches( KHttpCookiePtr c, 00354 QString domain, QString fqdn, 00355 QString path, QString name ) 00356 { 00357 if( c ) 00358 { 00359 bool hasDomain = !domain.isEmpty(); 00360 return 00361 ((hasDomain && c->domain() == domain) || 00362 fqdn == c->host()) && 00363 (c->path() == path) && 00364 (c->name() == name) && 00365 (!c->isExpired(time(0))); 00366 } 00367 return false; 00368 } 00369 00370 // DCOP function 00371 QString 00372 KCookieServer::findCookies(QString url) 00373 { 00374 return findCookies(url, 0); 00375 } 00376 00377 // DCOP function 00378 QString 00379 KCookieServer::findCookies(QString url, long windowId) 00380 { 00381 if (cookiesPending(url)) 00382 { 00383 CookieRequest *request = new CookieRequest; 00384 request->client = callingDcopClient(); 00385 request->transaction = request->client->beginTransaction(); 00386 request->url = url; 00387 request->DOM = false; 00388 request->windowId = windowId; 00389 mRequestList->append( request ); 00390 return QString::null; // Talk to you later :-) 00391 } 00392 00393 QString cookies = mCookieJar->findCookies(url, false, windowId); 00394 00395 if (mCookieJar->changed()) 00396 saveCookieJar(); 00397 00398 return cookies; 00399 } 00400 00401 // DCOP function 00402 QStringList 00403 KCookieServer::findDomains() 00404 { 00405 QStringList result; 00406 const QStringList domains = mCookieJar->getDomainList(); 00407 for ( QStringList::ConstIterator domIt = domains.begin(); 00408 domIt != domains.end(); ++domIt ) 00409 { 00410 // Ignore domains that have policy set for but contain 00411 // no cookies whatsoever... 00412 const KHttpCookieList* list = mCookieJar->getCookieList(*domIt, ""); 00413 if ( list && !list->isEmpty() ) 00414 result << *domIt; 00415 } 00416 return result; 00417 } 00418 00419 // DCOP function 00420 QStringList 00421 KCookieServer::findCookies(QValueList<int> fields, 00422 QString domain, 00423 QString fqdn, 00424 QString path, 00425 QString name) 00426 { 00427 QStringList result; 00428 bool allDomCookies = name.isEmpty(); 00429 00430 const KHttpCookieList* list = mCookieJar->getCookieList(domain, fqdn); 00431 if ( list && !list->isEmpty() ) 00432 { 00433 QPtrListIterator<KHttpCookie>it( *list ); 00434 for ( ; it.current(); ++it ) 00435 { 00436 if ( !allDomCookies ) 00437 { 00438 if ( cookieMatches(it.current(), domain, fqdn, path, name) ) 00439 { 00440 putCookie(result, it.current(), fields); 00441 break; 00442 } 00443 } 00444 else 00445 putCookie(result, it.current(), fields); 00446 } 00447 } 00448 return result; 00449 } 00450 00451 // DCOP function 00452 QString 00453 KCookieServer::findDOMCookies(QString url) 00454 { 00455 return findDOMCookies(url, 0); 00456 } 00457 00458 // DCOP function 00459 QString 00460 KCookieServer::findDOMCookies(QString url, long windowId) 00461 { 00462 // We don't wait for pending cookies because it locks up konqueror 00463 // which can cause a deadlock if it happens to have a popup-menu up. 00464 // Instead we just return pending cookies as if they had been accepted already. 00465 KHttpCookieList pendingCookies; 00466 cookiesPending(url, &pendingCookies); 00467 00468 return mCookieJar->findCookies(url, true, windowId, &pendingCookies); 00469 } 00470 00471 // DCOP function 00472 void 00473 KCookieServer::addCookies(QString arg1, QCString arg2, long arg3) 00474 { 00475 addCookies(arg1, arg2, arg3, false); 00476 } 00477 00478 // DCOP function 00479 void 00480 KCookieServer::deleteCookie(QString domain, QString fqdn, 00481 QString path, QString name) 00482 { 00483 const KHttpCookieList* list = mCookieJar->getCookieList( domain, fqdn ); 00484 if ( list && !list->isEmpty() ) 00485 { 00486 QPtrListIterator<KHttpCookie>it (*list); 00487 for ( ; it.current(); ++it ) 00488 { 00489 if( cookieMatches(it.current(), domain, fqdn, path, name) ) 00490 { 00491 mCookieJar->eatCookie( it.current() ); 00492 saveCookieJar(); 00493 break; 00494 } 00495 } 00496 } 00497 } 00498 00499 // DCOP function 00500 void 00501 KCookieServer::deleteCookiesFromDomain(QString domain) 00502 { 00503 mCookieJar->eatCookiesForDomain(domain); 00504 saveCookieJar(); 00505 } 00506 00507 00508 // Qt function 00509 void 00510 KCookieServer::slotDeleteSessionCookies( long windowId ) 00511 { 00512 deleteSessionCookies(windowId); 00513 } 00514 00515 // DCOP function 00516 void 00517 KCookieServer::deleteSessionCookies( long windowId ) 00518 { 00519 mCookieJar->eatSessionCookies( windowId ); 00520 saveCookieJar(); 00521 } 00522 00523 void 00524 KCookieServer::deleteSessionCookiesFor(QString fqdn, long windowId) 00525 { 00526 mCookieJar->eatSessionCookies( fqdn, windowId ); 00527 saveCookieJar(); 00528 } 00529 00530 // DCOP function 00531 void 00532 KCookieServer::deleteAllCookies() 00533 { 00534 mCookieJar->eatAllCookies(); 00535 saveCookieJar(); 00536 } 00537 00538 // DCOP function 00539 void 00540 KCookieServer::addDOMCookies(QString arg1, QCString arg2, long arg3) 00541 { 00542 addCookies(arg1, arg2, arg3, true); 00543 } 00544 00545 // DCOP function 00546 void 00547 KCookieServer::setDomainAdvice(QString url, QString advice) 00548 { 00549 QString fqdn; 00550 QString dummy; 00551 if (KCookieJar::parseURL(url, fqdn, dummy)) 00552 { 00553 QStringList domains; 00554 mCookieJar->extractDomains(fqdn, domains); 00555 00556 mCookieJar->setDomainAdvice(domains[domains.count() > 3 ? 3 : 0], 00557 KCookieJar::strToAdvice(advice)); 00558 // Save the cookie config if it has changed 00559 mCookieJar->saveConfig( mConfig ); 00560 } 00561 } 00562 00563 // DCOP function 00564 QString 00565 KCookieServer::getDomainAdvice(QString url) 00566 { 00567 KCookieAdvice advice = KCookieDunno; 00568 QString fqdn; 00569 QString dummy; 00570 if (KCookieJar::parseURL(url, fqdn, dummy)) 00571 { 00572 QStringList domains; 00573 mCookieJar->extractDomains(fqdn, domains); 00574 00575 QStringList::ConstIterator it = domains.begin(); 00576 while ( (advice == KCookieDunno) && (it != domains.end()) ) 00577 { 00578 // Always check advice in both ".domain" and "domain". Note 00579 // that we only want to check "domain" if it matches the 00580 // fqdn of the requested URL. 00581 if ( (*it)[0] == '.' || (*it) == fqdn ) 00582 advice = mCookieJar->getDomainAdvice(*it); 00583 ++it; 00584 } 00585 if (advice == KCookieDunno) 00586 advice = mCookieJar->getGlobalAdvice(); 00587 } 00588 return KCookieJar::adviceToStr(advice); 00589 } 00590 00591 // DCOP function 00592 void 00593 KCookieServer::reloadPolicy() 00594 { 00595 mCookieJar->loadConfig( mConfig, true ); 00596 } 00597 00598 // DCOP function 00599 void 00600 KCookieServer::shutdown() 00601 { 00602 deleteLater(); 00603 } 00604 00605 #include "kcookieserver.moc" 00606