1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qnetworkcookiejar.h"
43 #include "qnetworkcookiejar_p.h"
45 #include "QtNetwork/qnetworkcookie.h"
46 #include "QtCore/qurl.h"
47 #include "QtCore/qdatetime.h"
48 #include "private/qtldurl_p.h"
53 \class QNetworkCookieJar
57 \brief The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects
59 Cookies are small bits of information that stateless protocols
60 like HTTP use to maintain some persistent information across
63 A cookie is set by a remote server when it replies to a request
64 and it expects the same cookie to be sent back when further
67 The cookie jar is the object that holds all cookies set in
68 previous requests. Web browsers save their cookie jars to disk in
69 order to conserve permanent cookies across invocations of the
72 QNetworkCookieJar does not implement permanent storage: it only
73 keeps the cookies in memory. Once the QNetworkCookieJar object is
74 deleted, all cookies it held will be discarded as well. If you
75 want to save the cookies, you should derive from this class and
76 implement the saving to disk to your own storage format.
78 This class implements only the basic security recommended by the
79 cookie specifications and does not implement any cookie acceptance
80 policy (it accepts all cookies set by any requests). In order to
81 override those rules, you should reimplement the
82 cookiesForUrl() and setCookiesFromUrl() virtual
83 functions. They are called by QNetworkReply and
84 QNetworkAccessManager when they detect new cookies and when they
87 \sa QNetworkCookie, QNetworkAccessManager, QNetworkReply,
88 QNetworkRequest, QNetworkAccessManager::setCookieJar()
92 Creates a QNetworkCookieJar object and sets the parent object to
95 The cookie jar is initialized to empty.
97 QNetworkCookieJar::QNetworkCookieJar(QObject *parent)
98 : QObject(*new QNetworkCookieJarPrivate, parent)
103 Destroys this cookie jar object and discards all cookies stored in
104 it. Cookies are not saved to disk in the QNetworkCookieJar default
107 If you need to save the cookies to disk, you have to derive from
108 QNetworkCookieJar and save the cookies to disk yourself.
110 QNetworkCookieJar::~QNetworkCookieJar()
115 Returns all cookies stored in this cookie jar. This function is
116 suitable for derived classes to save cookies to disk, as well as
117 to implement cookie expiration and other policies.
119 \sa setAllCookies(), cookiesForUrl()
121 QList<QNetworkCookie> QNetworkCookieJar::allCookies() const
123 return d_func()->allCookies;
127 Sets the internal list of cookies held by this cookie jar to be \a
128 cookieList. This function is suitable for derived classes to
129 implement loading cookies from permanent storage, or their own
130 cookie acceptance policies by reimplementing
133 \sa allCookies(), setCookiesFromUrl()
135 void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
137 Q_D(QNetworkCookieJar);
138 d->allCookies = cookieList;
141 static inline bool isParentPath(QString path, QString reference)
143 if (!path.endsWith(QLatin1Char('/')))
144 path += QLatin1Char('/');
145 if (!reference.endsWith(QLatin1Char('/')))
146 reference += QLatin1Char('/');
147 return path.startsWith(reference);
150 static inline bool isParentDomain(QString domain, QString reference)
152 if (!reference.startsWith(QLatin1Char('.')))
153 return domain == reference;
155 return domain.endsWith(reference) || domain == reference.mid(1);
159 Adds the cookies in the list \a cookieList to this cookie
160 jar. Before being inserted cookies are normalized.
162 Returns true if one or more cookies are set for \a url,
165 If a cookie already exists in the cookie jar, it will be
166 overridden by those in \a cookieList.
168 The default QNetworkCookieJar class implements only a very basic
169 security policy (it makes sure that the cookies' domain and path
170 match the reply's). To enhance the security policy with your own
171 algorithms, override setCookiesFromUrl().
173 Also, QNetworkCookieJar does not have a maximum cookie jar
174 size. Reimplement this function to discard older cookies to create
177 \sa cookiesForUrl(), QNetworkAccessManager::setCookieJar(), QNetworkCookie::normalize()
179 bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
183 foreach (QNetworkCookie cookie, cookieList) {
184 cookie.normalize(url);
185 if (validateCookie(cookie, url) && insertCookie(cookie))
192 Returns the cookies to be added to when a request is sent to
193 \a url. This function is called by the default
194 QNetworkAccessManager::createRequest(), which adds the
195 cookies returned by this function to the request being sent.
197 If more than one cookie with the same name is found, but with
198 differing paths, the one with longer path is returned before the
199 one with shorter path. In other words, this function returns
200 cookies sorted decreasingly by path length.
202 The default QNetworkCookieJar class implements only a very basic
203 security policy (it makes sure that the cookies' domain and path
204 match the reply's). To enhance the security policy with your own
205 algorithms, override cookiesForUrl().
207 \sa setCookiesFromUrl(), QNetworkAccessManager::setCookieJar()
209 QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
211 // \b Warning! This is only a dumb implementation!
212 // It does NOT follow all of the recommendations from
213 // http://wp.netscape.com/newsref/std/cookie_spec.html
214 // It does not implement a very good cross-domain verification yet.
216 Q_D(const QNetworkCookieJar);
217 QDateTime now = QDateTime::currentDateTime();
218 QList<QNetworkCookie> result;
219 bool isEncrypted = url.scheme().toLower() == QLatin1String("https");
221 // scan our cookies for something that matches
222 QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
223 end = d->allCookies.constEnd();
224 for ( ; it != end; ++it) {
225 if (!isParentDomain(url.host(), it->domain()))
227 if (!isParentPath(url.path(), it->path()))
229 if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
231 if ((*it).isSecure() && !isEncrypted)
234 // insert this cookie into result, sorted by path
235 QList<QNetworkCookie>::Iterator insertIt = result.begin();
236 while (insertIt != result.end()) {
237 if (insertIt->path().length() < it->path().length()) {
239 insertIt = result.insert(insertIt, *it);
246 // this is the shortest path yet, just append
247 if (insertIt == result.end())
256 Adds \a cookie to this cookie jar.
258 Returns true if \a cookie was added, false otherwise.
260 If a cookie with the same identifier already exists in the
261 cookie jar, it will be overridden.
263 bool QNetworkCookieJar::insertCookie(const QNetworkCookie &cookie)
265 Q_D(QNetworkCookieJar);
266 QDateTime now = QDateTime::currentDateTime();
267 bool isDeletion = !cookie.isSessionCookie() &&
268 cookie.expirationDate() < now;
270 deleteCookie(cookie);
273 d->allCookies += cookie;
281 If a cookie with the same identifier as \a cookie exists in this cookie jar
282 it will be updated. This function uses insertCookie().
284 Returns true if \a cookie was updated, false if no cookie in the jar matches
285 the identifier of \a cookie.
287 \sa QNetworkCookie::hasSameIdentifier()
289 bool QNetworkCookieJar::updateCookie(const QNetworkCookie &cookie)
291 if (deleteCookie(cookie))
292 return insertCookie(cookie);
298 Deletes from cookie jar the cookie found to have the same identifier as \a cookie.
300 Returns true if a cookie was deleted, false otherwise.
302 \sa QNetworkCookie::hasSameIdentifier()
304 bool QNetworkCookieJar::deleteCookie(const QNetworkCookie &cookie)
306 Q_D(QNetworkCookieJar);
307 QList<QNetworkCookie>::Iterator it;
308 for (it = d->allCookies.begin(); it != d->allCookies.end(); it++)
309 if (it->hasSameIdentifier(cookie)) {
310 d->allCookies.erase(it);
318 Returns true if the domain and path of \a cookie are valid, false otherwise.
319 The \a url parameter is used to determine if the domain specified in the cookie
322 bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl &url) const
324 QString domain = cookie.domain();
325 if (!(isParentDomain(domain, url.host()) || isParentDomain(url.host(), domain)))
326 return false; // not accepted
328 // the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
329 // redundant; the "leading dot" rule has been relaxed anyway, see QNetworkCookie::normalize()
330 // we remove the leading dot for this check if it's present
331 if (qIsEffectiveTLD(domain.startsWith('.') ? domain.remove(0, 1) : domain))
332 return false; // not accepted