1/*
2 *  Copyright (C) 2008 Xan Lopez <xan@gnome.org>
3 *  Copyright (C) 2009 Igalia S.L.
4 *  Copyright (C) 2008 Apple Inc. All rights reserved.
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19 */
20
21#include "config.h"
22#include "CookieJarSoup.h"
23
24#include "Cookie.h"
25#include "GOwnPtrSoup.h"
26#include "KURL.h"
27#include "NetworkingContext.h"
28#include "PlatformCookieJar.h"
29#include "ResourceHandle.h"
30#include <wtf/gobject/GRefPtr.h>
31#include <wtf/text/CString.h>
32
33namespace WebCore {
34
35static SoupCookieJar* cookieJarForSession(const NetworkStorageSession& session)
36{
37    if (!session.soupSession())
38        return soupCookieJar();
39    return SOUP_COOKIE_JAR(soup_session_get_feature(session.soupSession(), SOUP_TYPE_COOKIE_JAR));
40}
41
42static GRefPtr<SoupCookieJar>& defaultCookieJar()
43{
44    DEFINE_STATIC_LOCAL(GRefPtr<SoupCookieJar>, cookieJar, ());
45    return cookieJar;
46}
47
48SoupCookieJar* soupCookieJar()
49{
50    if (GRefPtr<SoupCookieJar>& jar = defaultCookieJar())
51        return jar.get();
52
53    SoupCookieJar* jar = soup_cookie_jar_new();
54    soup_cookie_jar_set_accept_policy(jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY);
55    setSoupCookieJar(jar);
56    return jar;
57}
58
59void setSoupCookieJar(SoupCookieJar* jar)
60{
61    defaultCookieJar() = jar;
62}
63
64static inline bool httpOnlyCookieExists(const GSList* cookies, const gchar* name, const gchar* path)
65{
66    for (const GSList* iter = cookies; iter; iter = g_slist_next(iter)) {
67        SoupCookie* cookie = static_cast<SoupCookie*>(iter->data);
68        if (!strcmp(soup_cookie_get_name(cookie), name)
69            && !g_strcmp0(soup_cookie_get_path(cookie), path)) {
70            if (soup_cookie_get_http_only(cookie))
71                return true;
72            break;
73        }
74    }
75    return false;
76}
77
78void setCookiesFromDOM(const NetworkStorageSession& session, const KURL& firstParty, const KURL& url, const String& value)
79{
80    SoupCookieJar* jar = cookieJarForSession(session);
81    if (!jar)
82        return;
83
84    GOwnPtr<SoupURI> origin(soup_uri_new(url.string().utf8().data()));
85    GOwnPtr<SoupURI> firstPartyURI(soup_uri_new(firstParty.string().utf8().data()));
86
87    // Get existing cookies for this origin.
88    GSList* existingCookies = soup_cookie_jar_get_cookie_list(jar, origin.get(), TRUE);
89
90    Vector<String> cookies;
91    value.split('\n', cookies);
92    const size_t cookiesCount = cookies.size();
93    for (size_t i = 0; i < cookiesCount; ++i) {
94        GOwnPtr<SoupCookie> cookie(soup_cookie_parse(cookies[i].utf8().data(), origin.get()));
95        if (!cookie)
96            continue;
97
98        // Make sure the cookie is not httpOnly since such cookies should not be set from JavaScript.
99        if (soup_cookie_get_http_only(cookie.get()))
100            continue;
101
102        // Make sure we do not overwrite httpOnly cookies from JavaScript.
103        if (httpOnlyCookieExists(existingCookies, soup_cookie_get_name(cookie.get()), soup_cookie_get_path(cookie.get())))
104            continue;
105
106        soup_cookie_jar_add_cookie_with_first_party(jar, firstPartyURI.get(), cookie.release());
107    }
108
109    soup_cookies_free(existingCookies);
110}
111
112static String cookiesForSession(const NetworkStorageSession& session, const KURL& url, bool forHTTPHeader)
113{
114    SoupCookieJar* jar = cookieJarForSession(session);
115    if (!jar)
116        return String();
117
118    GOwnPtr<SoupURI> uri(soup_uri_new(url.string().utf8().data()));
119    GOwnPtr<char> cookies(soup_cookie_jar_get_cookies(jar, uri.get(), forHTTPHeader));
120    return String::fromUTF8(cookies.get());
121}
122
123String cookiesForDOM(const NetworkStorageSession& session, const KURL&, const KURL& url)
124{
125    return cookiesForSession(session, url, false);
126}
127
128String cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& url)
129{
130    return cookiesForSession(session, url, true);
131}
132
133bool cookiesEnabled(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& /*url*/)
134{
135    return !!cookieJarForSession(session);
136}
137
138bool getRawCookies(const NetworkStorageSession& session, const KURL& /*firstParty*/, const KURL& url, Vector<Cookie>& rawCookies)
139{
140    rawCookies.clear();
141    SoupCookieJar* jar = cookieJarForSession(session);
142    if (!jar)
143        return false;
144
145    GOwnPtr<SoupURI> uri(soup_uri_new(url.string().utf8().data()));
146    GOwnPtr<GSList> cookies(soup_cookie_jar_get_cookie_list(jar, uri.get(), TRUE));
147    if (!cookies)
148        return false;
149
150    for (GSList* iter = cookies.get(); iter; iter = g_slist_next(iter)) {
151        SoupCookie* cookie = static_cast<SoupCookie*>(iter->data);
152        rawCookies.append(Cookie(String::fromUTF8(cookie->name), String::fromUTF8(cookie->value), String::fromUTF8(cookie->domain),
153            String::fromUTF8(cookie->path), cookie->expires ? static_cast<double>(soup_date_to_time_t(cookie->expires)) * 1000 : 0,
154            cookie->http_only, cookie->secure, !cookie->expires));
155        soup_cookie_free(cookie);
156    }
157
158    return true;
159}
160
161void deleteCookie(const NetworkStorageSession& session, const KURL& url, const String& name)
162{
163    SoupCookieJar* jar = cookieJarForSession(session);
164    if (!jar)
165        return;
166
167    GOwnPtr<SoupURI> uri(soup_uri_new(url.string().utf8().data()));
168    GOwnPtr<GSList> cookies(soup_cookie_jar_get_cookie_list(jar, uri.get(), TRUE));
169    if (!cookies)
170        return;
171
172    CString cookieName = name.utf8();
173    bool wasDeleted = false;
174    for (GSList* iter = cookies.get(); iter; iter = g_slist_next(iter)) {
175        SoupCookie* cookie = static_cast<SoupCookie*>(iter->data);
176        if (!wasDeleted && cookieName == cookie->name) {
177            soup_cookie_jar_delete_cookie(jar, cookie);
178            wasDeleted = true;
179        }
180        soup_cookie_free(cookie);
181    }
182}
183
184void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
185{
186    SoupCookieJar* cookieJar = cookieJarForSession(session);
187    GOwnPtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar));
188    for (GSList* item = cookies.get(); item; item = g_slist_next(item)) {
189        SoupCookie* cookie = static_cast<SoupCookie*>(item->data);
190        if (cookie->domain)
191            hostnames.add(String::fromUTF8(cookie->domain));
192        soup_cookie_free(cookie);
193    }
194}
195
196void deleteCookiesForHostname(const NetworkStorageSession& session, const String& hostname)
197{
198    CString hostNameString = hostname.utf8();
199    SoupCookieJar* cookieJar = cookieJarForSession(session);
200    GOwnPtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar));
201    for (GSList* item = cookies.get(); item; item = g_slist_next(item)) {
202        SoupCookie* cookie = static_cast<SoupCookie*>(item->data);
203        if (soup_cookie_domain_matches(cookie, hostNameString.data()))
204            soup_cookie_jar_delete_cookie(cookieJar, cookie);
205        soup_cookie_free(cookie);
206    }
207}
208
209void deleteAllCookies(const NetworkStorageSession& session)
210{
211    SoupCookieJar* cookieJar = cookieJarForSession(session);
212    GOwnPtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar));
213    for (GSList* item = cookies.get(); item; item = g_slist_next(item)) {
214        SoupCookie* cookie = static_cast<SoupCookie*>(item->data);
215        soup_cookie_jar_delete_cookie(cookieJar, cookie);
216        soup_cookie_free(cookie);
217    }
218}
219
220}
221