source: trunk/src/network/access/qnetworkcookiejar.cpp@ 846

Last change on this file since 846 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qnetworkcookiejar.h"
43#include "qnetworkcookiejartlds_p.h"
44#include "qnetworkcookiejar_p.h"
45
46#include "QtNetwork/qnetworkcookie.h"
47#include "QtCore/qurl.h"
48#include "QtCore/qdatetime.h"
49
50QT_BEGIN_NAMESPACE
51
52/*!
53 \class QNetworkCookieJar
54 \brief The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects
55 \since 4.4
56
57 Cookies are small bits of information that stateless protocols
58 like HTTP use to maintain some persistent information across
59 requests.
60
61 A cookie is set by a remote server when it replies to a request
62 and it expects the same cookie to be sent back when further
63 requests are sent.
64
65 The cookie jar is the object that holds all cookies set in
66 previous requests. Web browsers save their cookie jars to disk in
67 order to conserve permanent cookies across invocations of the
68 application.
69
70 QNetworkCookieJar does not implement permanent storage: it only
71 keeps the cookies in memory. Once the QNetworkCookieJar object is
72 deleted, all cookies it held will be discarded as well. If you
73 want to save the cookies, you should derive from this class and
74 implement the saving to disk to your own storage format.
75
76 This class implements only the basic security recommended by the
77 cookie specifications and does not implement any cookie acceptance
78 policy (it accepts all cookies set by any requests). In order to
79 override those rules, you should reimplement the
80 cookiesForUrl() and setCookiesFromUrl() virtual
81 functions. They are called by QNetworkReply and
82 QNetworkAccessManager when they detect new cookies and when they
83 require cookies.
84
85 \sa QNetworkCookie, QNetworkAccessManager, QNetworkReply,
86 QNetworkRequest, QNetworkAccessManager::setCookieJar()
87*/
88
89/*!
90 Creates a QNetworkCookieJar object and sets the parent object to
91 be \a parent.
92
93 The cookie jar is initialized to empty.
94*/
95QNetworkCookieJar::QNetworkCookieJar(QObject *parent)
96 : QObject(*new QNetworkCookieJarPrivate, parent)
97{
98}
99
100/*!
101 Destroys this cookie jar object and discards all cookies stored in
102 it. Cookies are not saved to disk in the QNetworkCookieJar default
103 implementation.
104
105 If you need to save the cookies to disk, you have to derive from
106 QNetworkCookieJar and save the cookies to disk yourself.
107*/
108QNetworkCookieJar::~QNetworkCookieJar()
109{
110}
111
112/*!
113 Returns all cookies stored in this cookie jar. This function is
114 suitable for derived classes to save cookies to disk, as well as
115 to implement cookie expiration and other policies.
116
117 \sa setAllCookies(), cookiesForUrl()
118*/
119QList<QNetworkCookie> QNetworkCookieJar::allCookies() const
120{
121 return d_func()->allCookies;
122}
123
124/*!
125 Sets the internal list of cookies held by this cookie jar to be \a
126 cookieList. This function is suitable for derived classes to
127 implement loading cookies from permanent storage, or their own
128 cookie acceptance policies by reimplementing
129 setCookiesFromUrl().
130
131 \sa allCookies(), setCookiesFromUrl()
132*/
133void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
134{
135 Q_D(QNetworkCookieJar);
136 d->allCookies = cookieList;
137}
138
139static inline bool isParentPath(QString path, QString reference)
140{
141 if (!path.endsWith(QLatin1Char('/')))
142 path += QLatin1Char('/');
143 if (!reference.endsWith(QLatin1Char('/')))
144 reference += QLatin1Char('/');
145 return path.startsWith(reference);
146}
147
148static inline bool isParentDomain(QString domain, QString reference)
149{
150 if (!reference.startsWith(QLatin1Char('.')))
151 return domain == reference;
152
153 return domain.endsWith(reference) || domain == reference.mid(1);
154}
155
156/*!
157 Adds the cookies in the list \a cookieList to this cookie
158 jar. Default values for path and domain are taken from the \a
159 url object.
160
161 Returns true if one or more cookies are set for \a url,
162 otherwise false.
163
164 If a cookie already exists in the cookie jar, it will be
165 overridden by those in \a cookieList.
166
167 The default QNetworkCookieJar class implements only a very basic
168 security policy (it makes sure that the cookies' domain and path
169 match the reply's). To enhance the security policy with your own
170 algorithms, override setCookiesFromUrl().
171
172 Also, QNetworkCookieJar does not have a maximum cookie jar
173 size. Reimplement this function to discard older cookies to create
174 room for new ones.
175
176 \sa cookiesForUrl(), QNetworkAccessManager::setCookieJar()
177*/
178bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
179 const QUrl &url)
180{
181 Q_D(QNetworkCookieJar);
182 QString defaultDomain = url.host();
183 QString pathAndFileName = url.path();
184 QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
185 if (defaultPath.isEmpty())
186 defaultPath = QLatin1Char('/');
187
188 int added = 0;
189 QDateTime now = QDateTime::currentDateTime();
190 foreach (QNetworkCookie cookie, cookieList) {
191 bool isDeletion = !cookie.isSessionCookie() &&
192 cookie.expirationDate() < now;
193
194 // validate the cookie & set the defaults if unset
195 if (cookie.path().isEmpty())
196 cookie.setPath(defaultPath);
197 // don't do path checking. See http://bugreports.qt.nokia.com/browse/QTBUG-5815
198// else if (!isParentPath(pathAndFileName, cookie.path())) {
199// continue; // not accepted
200// }
201 if (cookie.domain().isEmpty()) {
202 cookie.setDomain(defaultDomain);
203 } else {
204 // Ensure the domain starts with a dot if its field was not empty
205 // in the HTTP header. There are some servers that forget the
206 // leading dot and this is actually forbidden according to RFC 2109,
207 // but all browsers accept it anyway so we do that as well.
208 if (!cookie.domain().startsWith(QLatin1Char('.')))
209 cookie.setDomain(QLatin1Char('.') + cookie.domain());
210
211 QString domain = cookie.domain();
212 if (!(isParentDomain(domain, defaultDomain)
213 || isParentDomain(defaultDomain, domain)))
214 continue; // not accepted
215
216 // the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
217 // redundant; the "leading dot" rule has been relaxed anyway, see above
218 // we remove the leading dot for this check
219 if (QNetworkCookieJarPrivate::isEffectiveTLD(domain.remove(0, 1)))
220 continue; // not accepted
221 }
222
223 QList<QNetworkCookie>::Iterator it = d->allCookies.begin(),
224 end = d->allCookies.end();
225 for ( ; it != end; ++it)
226 // does this cookie already exist?
227 if (cookie.name() == it->name() &&
228 cookie.domain() == it->domain() &&
229 cookie.path() == it->path()) {
230 // found a match
231 d->allCookies.erase(it);
232 break;
233 }
234
235 // did not find a match
236 if (!isDeletion) {
237 d->allCookies += cookie;
238 ++added;
239 }
240 }
241 return (added > 0);
242}
243
244/*!
245 Returns the cookies to be added to when a request is sent to
246 \a url. This function is called by the default
247 QNetworkAccessManager::createRequest(), which adds the
248 cookies returned by this function to the request being sent.
249
250 If more than one cookie with the same name is found, but with
251 differing paths, the one with longer path is returned before the
252 one with shorter path. In other words, this function returns
253 cookies sorted decreasingly by path length.
254
255 The default QNetworkCookieJar class implements only a very basic
256 security policy (it makes sure that the cookies' domain and path
257 match the reply's). To enhance the security policy with your own
258 algorithms, override cookiesForUrl().
259
260 \sa setCookiesFromUrl(), QNetworkAccessManager::setCookieJar()
261*/
262QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
263{
264// \b Warning! This is only a dumb implementation!
265// It does NOT follow all of the recommendations from
266// http://wp.netscape.com/newsref/std/cookie_spec.html
267// It does not implement a very good cross-domain verification yet.
268
269 Q_D(const QNetworkCookieJar);
270 QDateTime now = QDateTime::currentDateTime();
271 QList<QNetworkCookie> result;
272 bool isEncrypted = url.scheme().toLower() == QLatin1String("https");
273
274 // scan our cookies for something that matches
275 QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
276 end = d->allCookies.constEnd();
277 for ( ; it != end; ++it) {
278 if (!isParentDomain(url.host(), it->domain()))
279 continue;
280 if (!isParentPath(url.path(), it->path()))
281 continue;
282 if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
283 continue;
284 if ((*it).isSecure() && !isEncrypted)
285 continue;
286
287 // insert this cookie into result, sorted by path
288 QList<QNetworkCookie>::Iterator insertIt = result.begin();
289 while (insertIt != result.end()) {
290 if (insertIt->path().length() < it->path().length()) {
291 // insert here
292 insertIt = result.insert(insertIt, *it);
293 break;
294 } else {
295 ++insertIt;
296 }
297 }
298
299 // this is the shortest path yet, just append
300 if (insertIt == result.end())
301 result += *it;
302 }
303
304 return result;
305}
306
307bool QNetworkCookieJarPrivate::isEffectiveTLD(const QString &domain)
308{
309 // for domain 'foo.bar.com':
310 // 1. return if TLD table contains 'foo.bar.com'
311 if (containsTLDEntry(domain))
312 return true;
313
314 if (domain.contains(QLatin1Char('.'))) {
315 int count = domain.size() - domain.indexOf(QLatin1Char('.'));
316 QString wildCardDomain;
317 wildCardDomain.reserve(count + 1);
318 wildCardDomain.append(QLatin1Char('*'));
319 wildCardDomain.append(domain.right(count));
320 // 2. if table contains '*.bar.com',
321 // test if table contains '!foo.bar.com'
322 if (containsTLDEntry(wildCardDomain)) {
323 QString exceptionDomain;
324 exceptionDomain.reserve(domain.size() + 1);
325 exceptionDomain.append(QLatin1Char('!'));
326 exceptionDomain.append(domain);
327 return (! containsTLDEntry(exceptionDomain));
328 }
329 }
330 return false;
331}
332
333bool QNetworkCookieJarPrivate::containsTLDEntry(const QString &entry)
334{
335 int index = qHash(entry) % tldCount;
336 int currentDomainIndex = tldIndices[index];
337 while (currentDomainIndex < tldIndices[index+1]) {
338 QString currentEntry = QString::fromUtf8(tldData + currentDomainIndex);
339 if (currentEntry == entry)
340 return true;
341 currentDomainIndex += qstrlen(tldData + currentDomainIndex) + 1; // +1 for the ending \0
342 }
343 return false;
344}
345
346QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.