static void processCookie(QString &line) { QString policy; popArg(policy, line); KCookieAdvice expectedAdvice = KCookieJar::strToAdvice(policy); if (expectedAdvice == KCookieDunno) FAIL(QString("Unknown accept policy '%1'").arg(policy)); QString urlStr; popArg(urlStr, line); KUrl url(urlStr); if (!url.isValid()) FAIL(QString("Invalid URL '%1'").arg(urlStr)); if (url.isEmpty()) FAIL(QString("Missing URL")); line.replace("%LASTYEAR%", *lastYear); line.replace("%NEXTYEAR%", *nextYear); KHttpCookieList list = jar->makeCookies(urlStr, line.toUtf8(), windowId); if (list.isEmpty()) FAIL(QString("Failed to make cookies from: '%1'").arg(line)); for(KHttpCookieList::iterator cookieIterator = list.begin(); cookieIterator != list.end(); ++cookieIterator) { KHttpCookie& cookie = *cookieIterator; const KCookieAdvice cookieAdvice = jar->cookieAdvice(cookie); if (cookieAdvice != expectedAdvice) FAIL(urlStr+QString("\n'%2'\nGot advice '%3' expected '%4'").arg(line) .arg(KCookieJar::adviceToStr(cookieAdvice)) .arg(KCookieJar::adviceToStr(expectedAdvice))); jar->addCookie(cookie); } }
// // This function hands a KHttpCookie object over to the cookie jar. // // On return cookiePtr is set to 0. // void KCookieJar::addCookie(KHttpCookiePtr &cookiePtr) { QStringList domains; KHttpCookieList *cookieList = 0L; // We always need to do this to make sure that the // that cookies of type hostname == cookie-domainname // are properly removed and/or updated as necessary! extractDomains( cookiePtr->host(), domains ); for ( QStringList::ConstIterator it = domains.begin(); (it != domains.end() && !cookieList); ++it ) { QString key = (*it).isNull() ? L1("") : (*it); KHttpCookieList *list= m_cookieDomains[key]; if ( !list ) continue; removeDuplicateFromList(list, cookiePtr, false, true); } QString domain = stripDomain( cookiePtr ); QString key = domain.isNull() ? L1("") : domain; cookieList = m_cookieDomains[ key ]; if (!cookieList) { // Make a new cookie list cookieList = new KHttpCookieList(); cookieList->setAutoDelete(true); // All cookies whose domain is not already // known to us should be added with KCookieDunno. // KCookieDunno means that we use the global policy. cookieList->setAdvice( KCookieDunno ); m_cookieDomains.insert( domain, cookieList); // Update the list of domains m_domainList.append(domain); } // Add the cookie to the cookie list // The cookie list is sorted 'longest path first' if (!cookiePtr->isExpired(time(0))) { #ifdef MAX_COOKIE_LIMIT if (cookieList->count() >= MAX_COOKIES_PER_HOST) makeRoom(cookieList, cookiePtr); // Delete a cookie #endif cookieList->inSort( cookiePtr ); m_cookiesChanged = true; } else { delete cookiePtr; } cookiePtr = 0; }
void KCookieJar::eatCookiesForDomain(const QString &domain) { KHttpCookieList *cookieList = m_cookieDomains[domain]; if (!cookieList || cookieList->isEmpty()) return; cookieList->clear(); if (cookieList->getAdvice() == KCookieDunno) { // This deletes cookieList! m_cookieDomains.remove(domain); m_domainList.remove(domain); } m_cookiesChanged = true; }
// // This function gets the advice for all cookies originating from // _domain. // KCookieAdvice KCookieJar::getDomainAdvice(const QString &_domain) { KHttpCookieList *cookieList = m_cookieDomains[_domain]; KCookieAdvice advice; if (cookieList) { advice = cookieList->getAdvice(); } else { advice = KCookieDunno; } return advice; }
// // This function advices whether a single KHttpCookie object should // be added to the cookie jar. // KCookieAdvice KCookieJar::cookieAdvice(KHttpCookiePtr cookiePtr) { if (m_rejectCrossDomainCookies && cookiePtr->isCrossDomain()) return KCookieReject; QStringList domains; extractDomains(cookiePtr->host(), domains); // If the cookie specifies a domain, check whether it is valid. Otherwise, // accept the cookie anyways but remove the domain="" value to prevent // cross-site cookie injection. if (!cookiePtr->domain().isEmpty()) { if (!domains.contains(cookiePtr->domain()) && !cookiePtr->domain().endsWith("."+cookiePtr->host())) cookiePtr->fixDomain(QString::null); } if (m_autoAcceptSessionCookies && (cookiePtr->expireDate() == 0 || m_ignoreCookieExpirationDate)) return KCookieAccept; KCookieAdvice advice = KCookieDunno; bool isFQDN = true; // First is FQDN QStringList::Iterator it = domains.begin(); // Start with FQDN which first in the list. while( (advice == KCookieDunno) && (it != domains.end())) { QString domain = *it; // Check if a policy for the FQDN/domain is set. if ( domain[0] == '.' || isFQDN ) { isFQDN = false; KHttpCookieList *cookieList = m_cookieDomains[domain]; if (cookieList) advice = cookieList->getAdvice(); } domains.remove(it); it = domains.begin(); // Continue from begin of remaining list } if (advice == KCookieDunno) advice = m_globalAdvice; return advice; }
/** * Parses cookie_domstr and returns a linked list of KHttpCookie objects. * cookie_domstr should be a semicolon-delimited list of "name=value" * pairs. Any whitespace before "name" or around '=' is discarded. * If no cookies are found, 0 is returned. */ KHttpCookieList KCookieJar::makeDOMCookies(const QString &_url, const QCString &cookie_domstring, long windowId) { // A lot copied from above KHttpCookieList cookieList; KHttpCookiePtr lastCookie = 0; const char *cookieStr = cookie_domstring.data(); QString Name; QString Value; QString fqdn; QString path; if (!parseURL(_url, fqdn, path)) { // Error parsing _url return KHttpCookieList(); } // This time it's easy while(*cookieStr) { cookieStr = parseNameValue(cookieStr, Name, Value); // Host = FQDN // Default domain = "" // Default path = "" KHttpCookie *cookie = new KHttpCookie(fqdn, QString::null, QString::null, Name, Value ); if (windowId) cookie->mWindowIds.append(windowId); cookieList.append(cookie); lastCookie = cookie; if (*cookieStr != '\0') cookieStr++; // Skip ';' or '\n' } return cookieList; }
// // Eat a cookie out of the jar. // cookiePtr should be one of the cookies returned by getCookieList() // void KCookieJar::eatCookie(KHttpCookiePtr cookiePtr) { QString domain = stripDomain(cookiePtr); // We file the cookie under this domain. KHttpCookieList *cookieList = m_cookieDomains[domain]; if (cookieList) { // This deletes cookiePtr! if (cookieList->removeRef( cookiePtr )) m_cookiesChanged = true; if ((cookieList->isEmpty()) && (cookieList->getAdvice() == KCookieDunno)) { // This deletes cookieList! m_cookieDomains.remove(domain); m_domainList.remove(domain); } } }
void KCookieServer::addCookies(const QString &url, const QCString &cookieHeader, long windowId, bool useDOMFormat) { KHttpCookieList cookieList; if(useDOMFormat) cookieList = mCookieJar->makeDOMCookies(url, cookieHeader, windowId); else cookieList = mCookieJar->makeCookies(url, cookieHeader, windowId); checkCookies(&cookieList); for(KHttpCookiePtr cookie = cookieList.first(); cookie; cookie = cookieList.first()) mPendingCookies->append(cookieList.take()); if(!mAdvicePending) { mAdvicePending = true; while(!mPendingCookies->isEmpty()) { checkCookies(0); } mAdvicePending = false; } }
// // This function sets the advice for all cookies originating from // _domain. // void KCookieJar::setDomainAdvice(const QString &_domain, KCookieAdvice _advice) { QString domain(_domain); KHttpCookieList *cookieList = m_cookieDomains[domain]; if (cookieList) { if (cookieList->getAdvice() != _advice) { m_configChanged = true; // domain is already known cookieList->setAdvice( _advice); } if ((cookieList->isEmpty()) && (_advice == KCookieDunno)) { // This deletes cookieList! m_cookieDomains.remove(domain); m_domainList.remove(domain); } } else { // domain is not yet known if (_advice != KCookieDunno) { // We should create a domain entry m_configChanged = true; // Make a new cookie list cookieList = new KHttpCookieList(); cookieList->setAutoDelete(true); cookieList->setAdvice( _advice); m_cookieDomains.insert( domain, cookieList); // Update the list of domains m_domainList.append( domain); } } }
void KCookieJar::eatSessionCookies( const QString& fqdn, long windowId, bool isFQDN ) { KHttpCookieList* cookieList; if ( !isFQDN ) cookieList = m_cookieDomains[fqdn]; else { QString domain; stripDomain( fqdn, domain ); cookieList = m_cookieDomains[domain]; } if ( cookieList ) { KHttpCookiePtr cookie=cookieList->first(); for (; cookie != 0;) { if ((cookie->expireDate() != 0) && !m_ignoreCookieExpirationDate) { cookie = cookieList->next(); continue; } QValueList<long> &ids = cookie->windowIds(); if (!ids.remove(windowId) || !ids.isEmpty()) { cookie = cookieList->next(); continue; } KHttpCookiePtr old_cookie = cookie; cookie = cookieList->next(); cookieList->removeRef( old_cookie ); } } }
void KCookieServer::checkCookies(KHttpCookieList *cookieList) { KHttpCookieList *list; if(cookieList) list = cookieList; else list = mPendingCookies; KHttpCookiePtr cookie = list->first(); while(cookie) { kdDebug(7104) << "checkCookies: Asking cookie advice for " << cookie->host() << endl; KCookieAdvice advice = mCookieJar->cookieAdvice(cookie); switch(advice) { case KCookieAccept: list->take(); mCookieJar->addCookie(cookie); cookie = list->current(); break; case KCookieReject: list->take(); delete cookie; cookie = list->current(); break; default: cookie = list->next(); break; } } if(cookieList || list->isEmpty()) return; KHttpCookiePtr currentCookie = mPendingCookies->first(); KHttpCookieList currentList; currentList.append(currentCookie); QString currentHost = currentCookie->host(); cookie = mPendingCookies->next(); while(cookie) { if(cookie->host() == currentHost) { currentList.append(cookie); } cookie = mPendingCookies->next(); } KCookieWin *kw = new KCookieWin(0L, currentList, mCookieJar->preferredDefaultPolicy(), mCookieJar->showCookieDetails()); KCookieAdvice userAdvice = kw->advice(mCookieJar, currentCookie); delete kw; // Save the cookie config if it has changed mCookieJar->saveConfig(mConfig); // Apply the user's choice to all cookies that are currently // queued for this host. cookie = mPendingCookies->first(); while(cookie) { if(cookie->host() == currentHost) { switch(userAdvice) { case KCookieAccept: mPendingCookies->take(); mCookieJar->addCookie(cookie); cookie = mPendingCookies->current(); break; case KCookieReject: mPendingCookies->take(); delete cookie; cookie = mPendingCookies->current(); break; default: qWarning(__FILE__ ":%d Problem!", __LINE__); cookie = mPendingCookies->next(); break; } } else { cookie = mPendingCookies->next(); } } // Check if we can handle any request for(CookieRequest *request = mRequestList->first(); request;) { if(!cookiesPending(request->url)) { QCString replyType; QByteArray replyData; QString res = mCookieJar->findCookies(request->url, request->DOM, request->windowId); QDataStream stream2(replyData, IO_WriteOnly); stream2 << res; replyType = "QString"; request->client->endTransaction(request->transaction, replyType, replyData); CookieRequest *tmp = request; request = mRequestList->next(); mRequestList->removeRef(tmp); delete tmp; } else { request = mRequestList->next(); } } if(mCookieJar->changed()) saveCookieJar(); }
void KCookieServer::checkCookies(KHttpCookieList *cookieList, qlonglong windowId) { KHttpCookieList *list; if (cookieList) { list = cookieList; } else { list = mPendingCookies; } QMutableListIterator<KHttpCookie> cookieIterator(*list); while (cookieIterator.hasNext()) { KHttpCookie &cookie = cookieIterator.next(); const KCookieAdvice advice = mCookieJar->cookieAdvice(cookie); switch (advice) { case KCookieAccept: case KCookieAcceptForSession: mCookieJar->addCookie(cookie); cookieIterator.remove(); break; case KCookieReject: cookieIterator.remove(); break; case KCookieDunno: case KCookieAsk: break; } } if (cookieList || list->isEmpty()) { return; } // Collect all pending cookies with the same host as the first pending cookie const KHttpCookie ¤tCookie = mPendingCookies->first(); KHttpCookieList currentList; currentList.append(currentCookie); const QString currentHost = currentCookie.host(); QList<int> shownCookies; shownCookies << 0; for (int i = 1 /*first already done*/; i < mPendingCookies->count(); ++i) { const KHttpCookie &cookie = (*mPendingCookies)[i]; if (cookie.host() == currentHost) { currentList.append(cookie); shownCookies << i; } } //qDebug() << shownCookies; KCookieWin *kw = new KCookieWin(0L, currentList, mCookieJar->preferredDefaultPolicy(), mCookieJar->showCookieDetails()); if (windowId > 0) { KWindowSystem::setMainWindow(kw, windowId); } KCookieAdvice userAdvice = kw->advice(mCookieJar, currentCookie); delete kw; // Save the cookie config if it has changed mCookieJar->saveConfig(mConfig); // Apply the user's choice to all cookies that are currently // queued for this host (or just the first one, if the user asks for that). QMutableListIterator<KHttpCookie> cookieIterator2(*mPendingCookies); int pendingCookieIndex = -1; while (cookieIterator2.hasNext()) { ++pendingCookieIndex; KHttpCookie &cookie = cookieIterator2.next(); if (cookie.host() != currentHost) { continue; } if (mCookieJar->preferredDefaultPolicy() == KCookieJar::ApplyToShownCookiesOnly && !shownCookies.contains(pendingCookieIndex)) { // User chose "only those cookies", and this one was added while the dialog was up -> skip break; } switch (userAdvice) { case KCookieAccept: case KCookieAcceptForSession: // Store the user's choice for the cookie. // This is only used to check later if the cookie should expire // at the end of the session. The choice is not saved on disk. cookie.setUserSelectedAdvice(userAdvice); mCookieJar->addCookie(cookie); cookieIterator2.remove(); break; case KCookieReject: cookieIterator2.remove(); break; case KCookieDunno: case KCookieAsk: qCWarning(KIO_COOKIEJAR) << "userAdvice not accept or reject, this should never happen!"; break; } } // Check if we can handle any request QMutableListIterator<CookieRequest *> requestIterator(*mRequestList); while (requestIterator.hasNext()) { CookieRequest *request = requestIterator.next(); if (!cookiesPending(request->url)) { const QString res = mCookieJar->findCookies(request->url, request->DOM, request->windowId); QDBusConnection::sessionBus().send(request->reply.createReply(res)); delete request; requestIterator.remove(); } } saveCookieJar(); }
// // This function parses cookie_headers and returns a linked list of // KHttpCookie objects for all cookies found in cookie_headers. // If no cookies could be found 0 is returned. // // cookie_headers should be a concatenation of all lines of a HTTP-header // which start with "Set-Cookie". The lines should be separated by '\n's. // KHttpCookieList KCookieJar::makeCookies(const QString &_url, const QCString &cookie_headers, long windowId) { KHttpCookieList cookieList; KHttpCookieList cookieList2; KHttpCookiePtr lastCookie = 0; const char *cookieStr = cookie_headers.data(); QString Name; QString Value; QString fqdn; QString path; bool crossDomain = false; if (!parseURL(_url, fqdn, path)) { // Error parsing _url return KHttpCookieList(); } QString defaultPath; int i = path.findRev('/'); if (i > 0) defaultPath = path.left(i); // The hard stuff :) for(;;) { // check for "Set-Cookie" if (strncmp(cookieStr, "Cross-Domain\n", 13) == 0) { cookieStr += 13; crossDomain = true; } else if (strncasecmp(cookieStr, "Set-Cookie:", 11) == 0) { cookieStr = parseNameValue(cookieStr+11, Name, Value, true); // Host = FQDN // Default domain = "" // Default path according to rfc2109 KHttpCookie *cookie = new KHttpCookie(fqdn, L1(""), defaultPath, Name, Value); if (windowId) cookie->mWindowIds.append(windowId); cookie->mCrossDomain = crossDomain; // Insert cookie in chain cookieList.append(cookie); lastCookie = cookie; } else if (strncasecmp(cookieStr, "Set-Cookie2:", 12) == 0) { // Attempt to follow rfc2965 cookieStr = parseNameValue(cookieStr+12, Name, Value, true, true); // Host = FQDN // Default domain = "" // Default path according to rfc2965 KHttpCookie *cookie = new KHttpCookie(fqdn, L1(""), defaultPath, Name, Value); if (windowId) cookie->mWindowIds.append(windowId); cookie->mCrossDomain = crossDomain; // Insert cookie in chain cookieList2.append(cookie); lastCookie = cookie; } else { // This is not the start of a cookie header, skip till next line. while (*cookieStr && *cookieStr != '\n') cookieStr++; if (*cookieStr == '\n') cookieStr++; if (!*cookieStr) break; // End of cookie_headers else continue; // end of this header, continue with next. } while ((*cookieStr == ';') || (*cookieStr == ' ')) { cookieStr++; // Name-Value pair follows cookieStr = parseNameValue(cookieStr, Name, Value); QCString cName = Name.lower().latin1(); if (cName == "domain") { QString dom = Value.lower(); // RFC2965 3.2.2: If an explicitly specified value does not // start with a dot, the user agent supplies a leading dot if(dom.length() && dom[0] != '.') dom.prepend("."); // remove a trailing dot if(dom.length() > 2 && dom[dom.length()-1] == '.') dom = dom.left(dom.length()-1); if(dom.contains('.') > 1 || dom == ".local") lastCookie->mDomain = dom; } else if (cName == "max-age") { int max_age = Value.toInt(); if (max_age == 0) lastCookie->mExpireDate = 1; else lastCookie->mExpireDate = time(0)+max_age; } else if (cName == "expires") { // Parse brain-dead netscape cookie-format lastCookie->mExpireDate = KRFCDate::parseDate(Value); // Workaround for servers that send the expiration date in // 'Wed Sep 12 07:00:00 2007 GMT' format. See BR# 145244. if (lastCookie->mExpireDate == 0) lastCookie->mExpireDate = KRFCDate::parseDate(fixupDateTime(Value)); } else if (cName == "path") { if (Value.isEmpty()) lastCookie->mPath = QString::null; // Catch "" <> QString::null else lastCookie->mPath = KURL::decode_string(Value); lastCookie->mExplicitPath = true; } else if (cName == "version") { lastCookie->mProtocolVersion = Value.toInt(); } else if ((cName == "secure") || (cName.isEmpty() && Value.lower() == L1("secure"))) { lastCookie->mSecure = true; } else if ((cName == "httponly") || (cName.isEmpty() && Value.lower() == L1("httponly"))) { lastCookie->mHttpOnly = true; } } if (*cookieStr == '\0') break; // End of header // Skip ';' or '\n' cookieStr++; } // RFC2965 cookies come last so that they override netscape cookies. while( !cookieList2.isEmpty() && (lastCookie = cookieList2.take(0)) ) { removeDuplicateFromList(&cookieList, lastCookie, true); cookieList.append(lastCookie); } return cookieList; }
// // Looks for cookies in the cookie jar which are appropriate for _url. // Returned is a string containing all appropriate cookies in a format // which can be added to a HTTP-header without any additional processing. // QString KCookieJar::findCookies(const QString &_url, bool useDOMFormat, long windowId, KHttpCookieList *pendingCookies) { QString cookieStr; QStringList domains; QString fqdn; QString path; KHttpCookiePtr cookie; KCookieAdvice advice = m_globalAdvice; if (!parseURL(_url, fqdn, path)) return cookieStr; bool secureRequest = (_url.find( L1("https://"), 0, false) == 0 || _url.find( L1("webdavs://"), 0, false) == 0); // kdDebug(7104) << "findCookies: URL= " << _url << ", secure = " << secureRequest << endl; extractDomains(fqdn, domains); KHttpCookieList allCookies; for(QStringList::ConstIterator it = domains.begin(); true; ++it) { KHttpCookieList *cookieList; if (it == domains.end()) { cookieList = pendingCookies; // Add pending cookies pendingCookies = 0; if (!cookieList) break; } else { QString key = (*it).isNull() ? L1("") : (*it); cookieList = m_cookieDomains[key]; if (!cookieList) continue; // No cookies for this domain } if (cookieList->getAdvice() != KCookieDunno) advice = cookieList->getAdvice(); for ( cookie=cookieList->first(); cookie != 0; cookie=cookieList->next() ) { // If the we are setup to automatically accept all session cookies and to // treat all cookies as session cookies or the current cookie is a session // cookie, then send the cookie back regardless of either policy. if (advice == KCookieReject && !(m_autoAcceptSessionCookies && (m_ignoreCookieExpirationDate || cookie->expireDate() == 0))) continue; if (!cookie->match(fqdn, domains, path)) continue; if( cookie->isSecure() && !secureRequest ) continue; if( cookie->isHttpOnly() && useDOMFormat ) continue; // Do not send expired cookies. if ( cookie->isExpired (time(0)) ) { // Note there is no need to actually delete the cookie here // since the cookieserver will invoke ::saveCookieJar because // of the state change below. This will then do the job of // deleting the cookie for us. m_cookiesChanged = true; continue; } if (windowId && (cookie->windowIds().find(windowId) == cookie->windowIds().end())) { cookie->windowIds().append(windowId); } if (it == domains.end()) // Only needed when processing pending cookies removeDuplicateFromList(&allCookies, cookie); allCookies.append(cookie); } if (it == domains.end()) break; // Finished. } int cookieCount = 0; int protVersion=0; for ( cookie=allCookies.first(); cookie != 0; cookie=allCookies.next() ) { if (cookie->protocolVersion() > protVersion) protVersion = cookie->protocolVersion(); } for ( cookie=allCookies.first(); cookie != 0; cookie=allCookies.next() ) { if (useDOMFormat) { if (cookieCount > 0) cookieStr += L1("; "); cookieStr += cookie->cookieStr(true); } else { if (cookieCount == 0) { cookieStr += L1("Cookie: "); if (protVersion > 0) { QString version; version.sprintf("$Version=%d; ", protVersion); // Without quotes cookieStr += version; } } else { cookieStr += L1("; "); } cookieStr += cookie->cookieStr(false); } cookieCount++; } return cookieStr; }
// // Saves all cookies to the file '_filename'. // On succes 'true' is returned. // On failure 'false' is returned. bool KCookieJar::saveCookies(const QString &_filename) { KSaveFile saveFile(_filename, 0600); if (saveFile.status() != 0) return false; FILE *fStream = saveFile.fstream(); time_t curTime = time(0); fprintf(fStream, "# KDE Cookie File v2\n#\n"); fprintf(fStream, "%-20s %-20s %-12s %-10s %-4s %-20s %-4s %s\n", "# Host", "Domain", "Path", "Exp.date", "Prot", "Name", "Sec", "Value"); for ( QStringList::Iterator it=m_domainList.begin(); it != m_domainList.end(); it++ ) { const QString &domain = *it; bool domainPrinted = false; KHttpCookieList *cookieList = m_cookieDomains[domain]; KHttpCookiePtr cookie=cookieList->last(); for (; cookie != 0;) { if (cookie->isExpired(curTime)) { // Delete expired cookies KHttpCookiePtr old_cookie = cookie; cookie = cookieList->prev(); cookieList->removeRef( old_cookie ); } else if (cookie->expireDate() != 0 && !m_ignoreCookieExpirationDate) { if (!domainPrinted) { domainPrinted = true; fprintf(fStream, "[%s]\n", domain.local8Bit().data()); } // Store persistent cookies QString path = L1("\""); path += cookie->path(); path += '"'; QString domain = L1("\""); domain += cookie->domain(); domain += '"'; fprintf(fStream, "%-20s %-20s %-12s %10lu %3d %-20s %-4i %s\n", cookie->host().latin1(), domain.latin1(), path.latin1(), (unsigned long) cookie->expireDate(), cookie->protocolVersion(), cookie->name().isEmpty() ? cookie->value().latin1() : cookie->name().latin1(), (cookie->isSecure() ? 1 : 0) + (cookie->isHttpOnly() ? 2 : 0) + (cookie->hasExplicitPath() ? 4 : 0) + (cookie->name().isEmpty() ? 8 : 0), cookie->value().latin1()); cookie = cookieList->prev(); } else { // Skip session-only cookies cookie = cookieList->prev(); } } } return saveFile.close(); }