1/*
2 * Copyright (C) 2006 George Staikos <staikos@kde.org>
3 * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies)
4 *
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "CookieJarQt.h"
31
32#include "Cookie.h"
33#include "KURL.h"
34#include "NetworkingContext.h"
35#include "PlatformCookieJar.h"
36#include "ThirdPartyCookiesQt.h"
37#include <QDateTime>
38#include <QNetworkAccessManager>
39#include <QNetworkCookie>
40#include <QSqlQuery>
41#include <QStringList>
42#include <QVariant>
43#include <wtf/text/WTFString.h>
44
45namespace WebCore {
46
47static SharedCookieJarQt* s_sharedCookieJarQt = 0;
48
49void setCookiesFromDOM(const NetworkStorageSession& session, const KURL& firstParty, const KURL& url, const String& value)
50{
51    QNetworkCookieJar* jar = session.context() ? session.context()->networkAccessManager()->cookieJar() : SharedCookieJarQt::shared();
52    if (!jar)
53        return;
54
55    QUrl urlForCookies(url);
56    QUrl firstPartyUrl(firstParty);
57    if (!thirdPartyCookiePolicyPermits(session.context(), urlForCookies, firstPartyUrl))
58        return;
59
60    QList<QNetworkCookie> cookies = QNetworkCookie::parseCookies(QString(value).toLatin1());
61    QList<QNetworkCookie>::Iterator it = cookies.begin();
62    while (it != cookies.end()) {
63        if (it->isHttpOnly())
64            it = cookies.erase(it);
65        else
66            ++it;
67    }
68
69    jar->setCookiesFromUrl(cookies, urlForCookies);
70}
71
72String cookiesForDOM(const NetworkStorageSession& session, const KURL& firstParty, const KURL& url)
73{
74    QNetworkCookieJar* jar = session.context() ? session.context()->networkAccessManager()->cookieJar() : SharedCookieJarQt::shared();
75    if (!jar)
76        return String();
77
78    QUrl urlForCookies(url);
79    QUrl firstPartyUrl(firstParty);
80    if (!thirdPartyCookiePolicyPermits(session.context(), urlForCookies, firstPartyUrl))
81        return String();
82
83    QList<QNetworkCookie> cookies = jar->cookiesForUrl(urlForCookies);
84    if (cookies.isEmpty())
85        return String();
86
87    QStringList resultCookies;
88    foreach (const QNetworkCookie& networkCookie, cookies) {
89        if (networkCookie.isHttpOnly())
90            continue;
91        resultCookies.append(QString::fromLatin1(networkCookie.toRawForm(QNetworkCookie::NameAndValueOnly).constData()));
92    }
93
94    return resultCookies.join(QLatin1String("; "));
95}
96
97String cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& url)
98{
99    QNetworkCookieJar* jar = session.context() ? session.context()->networkAccessManager()->cookieJar() : SharedCookieJarQt::shared();
100    if (!jar)
101        return String();
102
103    QList<QNetworkCookie> cookies = jar->cookiesForUrl(QUrl(url));
104    if (cookies.isEmpty())
105        return String();
106
107    QStringList resultCookies;
108    foreach (QNetworkCookie networkCookie, cookies)
109        resultCookies.append(QString::fromLatin1(networkCookie.toRawForm(QNetworkCookie::NameAndValueOnly).constData()));
110
111    return resultCookies.join(QLatin1String("; "));
112}
113
114bool cookiesEnabled(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& /*url*/)
115{
116    QNetworkCookieJar* jar = session.context() ? session.context()->networkAccessManager()->cookieJar() : SharedCookieJarQt::shared();
117    return !!jar;
118}
119
120bool getRawCookies(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& /*url*/, Vector<Cookie>& rawCookies)
121{
122    // FIXME: Not yet implemented
123    rawCookies.clear();
124    return false; // return true when implemented
125}
126
127void deleteCookie(const NetworkStorageSession&, const KURL&, const String&)
128{
129    // FIXME: Not yet implemented
130}
131
132void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
133{
134    ASSERT_UNUSED(session, !session.context()); // Not yet implemented for cookie jars other than the shared one.
135    SharedCookieJarQt* jar = SharedCookieJarQt::shared();
136    if (jar)
137        jar->getHostnamesWithCookies(hostnames);
138}
139
140void deleteCookiesForHostname(const NetworkStorageSession& session, const String& hostname)
141{
142    ASSERT_UNUSED(session, !session.context()); // Not yet implemented for cookie jars other than the shared one.
143    SharedCookieJarQt* jar = SharedCookieJarQt::shared();
144    if (jar)
145        jar->deleteCookiesForHostname(hostname);
146}
147
148void deleteAllCookies(const NetworkStorageSession& session)
149{
150    ASSERT_UNUSED(session, !session.context()); // Not yet implemented for cookie jars other than the shared one.
151    SharedCookieJarQt* jar = SharedCookieJarQt::shared();
152    if (jar)
153        jar->deleteAllCookies();
154}
155
156SharedCookieJarQt* SharedCookieJarQt::shared()
157{
158    return s_sharedCookieJarQt;
159}
160
161SharedCookieJarQt* SharedCookieJarQt::create(const String& cookieStorageDirectory)
162{
163    if (!s_sharedCookieJarQt)
164        s_sharedCookieJarQt = new SharedCookieJarQt(cookieStorageDirectory);
165
166    return s_sharedCookieJarQt;
167}
168
169void SharedCookieJarQt::destroy()
170{
171    delete s_sharedCookieJarQt;
172    s_sharedCookieJarQt = 0;
173}
174
175void SharedCookieJarQt::getHostnamesWithCookies(HashSet<String>& hostnames)
176{
177    QList<QNetworkCookie> cookies = allCookies();
178    foreach (const QNetworkCookie& networkCookie, cookies)
179        hostnames.add(networkCookie.domain());
180}
181
182bool SharedCookieJarQt::deleteCookie(const QNetworkCookie& cookie)
183{
184    if (!QNetworkCookieJar::deleteCookie(cookie))
185        return false;
186
187    if (!m_database.isOpen())
188        return false;
189
190    QSqlQuery sqlQuery(m_database);
191    sqlQuery.prepare(QLatin1String("DELETE FROM cookies WHERE cookieId=:cookieIdvalue"));
192    sqlQuery.bindValue(QLatin1String(":cookieIdvalue"), cookie.domain().append(QLatin1String(cookie.name())));
193    sqlQuery.exec();
194
195    return true;
196}
197
198void SharedCookieJarQt::deleteCookiesForHostname(const String& hostname)
199{
200    if (!m_database.isOpen())
201        return;
202
203    QList<QNetworkCookie> cookies = allCookies();
204    QList<QNetworkCookie>::Iterator it = cookies.begin();
205    QList<QNetworkCookie>::Iterator end = cookies.end();
206    QSqlQuery sqlQuery(m_database);
207    sqlQuery.prepare(QLatin1String("DELETE FROM cookies WHERE cookieId=:cookieIdvalue"));
208    while (it != end) {
209        if (it->domain() == QString(hostname)) {
210            sqlQuery.bindValue(QLatin1String(":cookieIdvalue"), it->domain().append(QLatin1String(it->name())));
211            sqlQuery.exec();
212            it = cookies.erase(it);
213        } else
214            it++;
215    }
216    setAllCookies(cookies);
217}
218
219void SharedCookieJarQt::deleteAllCookies()
220{
221    if (!m_database.isOpen())
222        return;
223
224    QSqlQuery sqlQuery(m_database);
225    sqlQuery.prepare(QLatin1String("DELETE FROM cookies"));
226    sqlQuery.exec();
227    setAllCookies(QList<QNetworkCookie>());
228}
229
230SharedCookieJarQt::SharedCookieJarQt(const String& cookieStorageDirectory)
231{
232    m_database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"));
233    const QString cookieStoragePath = cookieStorageDirectory;
234    const QString dataBaseName = cookieStoragePath + QLatin1String("/cookies.db");
235    m_database.setDatabaseName(dataBaseName);
236    ensureDatabaseTable();
237    loadCookies();
238}
239
240SharedCookieJarQt::~SharedCookieJarQt()
241{
242    m_database.close();
243}
244
245bool SharedCookieJarQt::setCookiesFromUrl(const QList<QNetworkCookie>& cookieList, const QUrl& url)
246{
247    if (!QNetworkCookieJar::setCookiesFromUrl(cookieList, url))
248        return false;
249
250    if (!m_database.isOpen())
251        return false;
252
253    QSqlQuery sqlQuery(m_database);
254    sqlQuery.prepare(QLatin1String("INSERT OR REPLACE INTO cookies (cookieId, cookie) VALUES (:cookieIdvalue, :cookievalue)"));
255    QVariantList cookiesIds;
256    QVariantList cookiesValues;
257    foreach (const QNetworkCookie &cookie, cookiesForUrl(url)) {
258        if (cookie.isSessionCookie())
259            continue;
260        cookiesIds.append(cookie.domain().append(QLatin1String(cookie.name())));
261        cookiesValues.append(cookie.toRawForm());
262    }
263    sqlQuery.bindValue(QLatin1String(":cookieIdvalue"), cookiesIds);
264    sqlQuery.bindValue(QLatin1String(":cookievalue"), cookiesValues);
265    sqlQuery.execBatch();
266    return true;
267}
268
269void SharedCookieJarQt::ensureDatabaseTable()
270{
271    if (!m_database.open()) {
272        qWarning("Can't open cookie database");
273        return;
274    }
275    m_database.exec(QLatin1String("PRAGMA synchronous=OFF"));
276
277    QSqlQuery sqlQuery(m_database);
278    sqlQuery.prepare(QLatin1String("CREATE TABLE IF NOT EXISTS cookies (cookieId VARCHAR PRIMARY KEY, cookie BLOB);"));
279    sqlQuery.exec();
280}
281
282void SharedCookieJarQt::loadCookies()
283{
284    if (!m_database.isOpen())
285        return;
286
287    QList<QNetworkCookie> cookies;
288    QSqlQuery sqlQuery(m_database);
289    sqlQuery.prepare(QLatin1String("SELECT cookie FROM cookies"));
290    sqlQuery.exec();
291    while (sqlQuery.next())
292        cookies.append(QNetworkCookie::parseCookies(sqlQuery.value(0).toByteArray()));
293    setAllCookies(cookies);
294}
295
296#include "moc_CookieJarQt.cpp"
297
298}
299
300// vim: ts=4 sw=4 et
301