d6f928abb9b1a8e87b0098399d9e30231cfc188c
[WebKit-https.git] / Source / WebCore / platform / network / soup / CookieJarSoup.cpp
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
23 #if USE(SOUP)
24
25 #include "Cookie.h"
26 #include "CookieRequestHeaderFieldProxy.h"
27 #include "CookiesStrategy.h"
28 #include "GUniquePtrSoup.h"
29 #include "NetworkStorageSession.h"
30 #include "NetworkingContext.h"
31 #include "PlatformCookieJar.h"
32 #include "SoupNetworkSession.h"
33 #include "URL.h"
34 #include <wtf/DateMath.h>
35 #include <wtf/glib/GRefPtr.h>
36 #include <wtf/text/CString.h>
37
38 namespace WebCore {
39
40 static inline bool httpOnlyCookieExists(const GSList* cookies, const gchar* name, const gchar* path)
41 {
42     for (const GSList* iter = cookies; iter; iter = g_slist_next(iter)) {
43         SoupCookie* cookie = static_cast<SoupCookie*>(iter->data);
44         if (!strcmp(soup_cookie_get_name(cookie), name)
45             && !g_strcmp0(soup_cookie_get_path(cookie), path)) {
46             if (soup_cookie_get_http_only(cookie))
47                 return true;
48             break;
49         }
50     }
51     return false;
52 }
53
54 void setCookiesFromDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, const String& value)
55 {
56     UNUSED_PARAM(frameID);
57     UNUSED_PARAM(pageID);
58     SoupCookieJar* jar = session.cookieStorage();
59
60     GUniquePtr<SoupURI> origin = url.createSoupURI();
61     GUniquePtr<SoupURI> firstPartyURI = firstParty.createSoupURI();
62
63     // Get existing cookies for this origin.
64     GSList* existingCookies = soup_cookie_jar_get_cookie_list(jar, origin.get(), TRUE);
65
66     Vector<String> cookies;
67     value.split('\n', cookies);
68     const size_t cookiesCount = cookies.size();
69     for (size_t i = 0; i < cookiesCount; ++i) {
70         GUniquePtr<SoupCookie> cookie(soup_cookie_parse(cookies[i].utf8().data(), origin.get()));
71         if (!cookie)
72             continue;
73
74         // Make sure the cookie is not httpOnly since such cookies should not be set from JavaScript.
75         if (soup_cookie_get_http_only(cookie.get()))
76             continue;
77
78         // Make sure we do not overwrite httpOnly cookies from JavaScript.
79         if (httpOnlyCookieExists(existingCookies, soup_cookie_get_name(cookie.get()), soup_cookie_get_path(cookie.get())))
80             continue;
81
82         soup_cookie_jar_add_cookie_with_first_party(jar, firstPartyURI.get(), cookie.release());
83     }
84
85     soup_cookies_free(existingCookies);
86 }
87
88 static std::pair<String, bool> cookiesForSession(const NetworkStorageSession& session, const URL& url, bool forHTTPHeader, IncludeSecureCookies includeSecureCookies)
89 {
90     GUniquePtr<SoupURI> uri = url.createSoupURI();
91     GSList* cookies = soup_cookie_jar_get_cookie_list(session.cookieStorage(), uri.get(), forHTTPHeader);
92     bool didAccessSecureCookies = false;
93
94     // libsoup should omit secure cookies itself if the protocol is not https.
95     if (url.protocolIs("https")) {
96         GSList* item = cookies;
97         while (item) {
98             auto cookie = static_cast<SoupCookie*>(item->data);
99             if (soup_cookie_get_secure(cookie)) {
100                 didAccessSecureCookies = true;
101                 if (includeSecureCookies == IncludeSecureCookies::No) {
102                     GSList* next = item->next;
103                     soup_cookie_free(static_cast<SoupCookie*>(item->data));
104                     cookies = g_slist_remove_link(cookies, item);
105                     item = next;
106                     continue;
107                 }
108             }
109             item = item->next;
110         }
111     }
112
113     if (!cookies)
114         return { { }, false };
115
116     GUniquePtr<char> cookieHeader(soup_cookies_to_cookie_header(cookies));
117     soup_cookies_free(cookies);
118
119     return { String::fromUTF8(cookieHeader.get()), didAccessSecureCookies };
120 }
121
122 std::pair<String, bool> cookiesForDOM(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
123 {
124     UNUSED_PARAM(firstParty);
125     UNUSED_PARAM(frameID);
126     UNUSED_PARAM(pageID);
127     return cookiesForSession(session, url, false, includeSecureCookies);
128 }
129
130 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, IncludeSecureCookies includeSecureCookies)
131 {
132     UNUSED_PARAM(firstParty);
133     UNUSED_PARAM(frameID);
134     UNUSED_PARAM(pageID);
135     // Secure cookies will still only be included if url's protocol is https.
136     return cookiesForSession(session, url, true, includeSecureCookies);
137 }
138
139 std::pair<String, bool> cookieRequestHeaderFieldValue(const NetworkStorageSession& session, const CookieRequestHeaderFieldProxy& headerFieldProxy)
140 {
141     return cookieRequestHeaderFieldValue(session, headerFieldProxy.firstParty, headerFieldProxy.url, headerFieldProxy.frameID, headerFieldProxy.pageID, headerFieldProxy.includeSecureCookies);
142 }
143
144 bool cookiesEnabled(const NetworkStorageSession& session)
145 {
146     auto policy = soup_cookie_jar_get_accept_policy(session.cookieStorage());
147     return policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS || policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY;
148 }
149
150 bool getRawCookies(const NetworkStorageSession& session, const URL& firstParty, const SameSiteInfo&, const URL& url, std::optional<uint64_t> frameID, std::optional<uint64_t> pageID, Vector<Cookie>& rawCookies)
151 {
152     UNUSED_PARAM(firstParty);
153     UNUSED_PARAM(frameID);
154     UNUSED_PARAM(pageID);
155     rawCookies.clear();
156     GUniquePtr<SoupURI> uri = url.createSoupURI();
157     GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(session.cookieStorage(), uri.get(), TRUE));
158     if (!cookies)
159         return false;
160
161     for (GSList* iter = cookies.get(); iter; iter = g_slist_next(iter)) {
162         SoupCookie* cookie = static_cast<SoupCookie*>(iter->data);
163         rawCookies.append(Cookie(String::fromUTF8(cookie->name), String::fromUTF8(cookie->value), String::fromUTF8(cookie->domain),
164             String::fromUTF8(cookie->path), 0, cookie->expires ? static_cast<double>(soup_date_to_time_t(cookie->expires)) * 1000 : 0,
165             cookie->http_only, cookie->secure, !cookie->expires, String(), URL(), Vector<uint16_t>{ }));
166         soup_cookie_free(cookie);
167     }
168
169     return true;
170 }
171
172 void deleteCookie(const NetworkStorageSession& session, const URL& url, const String& name)
173 {
174     SoupCookieJar* jar = session.cookieStorage();
175
176     GUniquePtr<SoupURI> uri = url.createSoupURI();
177     GUniquePtr<GSList> cookies(soup_cookie_jar_get_cookie_list(jar, uri.get(), TRUE));
178     if (!cookies)
179         return;
180
181     CString cookieName = name.utf8();
182     bool wasDeleted = false;
183     for (GSList* iter = cookies.get(); iter; iter = g_slist_next(iter)) {
184         SoupCookie* cookie = static_cast<SoupCookie*>(iter->data);
185         if (!wasDeleted && cookieName == cookie->name) {
186             soup_cookie_jar_delete_cookie(jar, cookie);
187             wasDeleted = true;
188         }
189         soup_cookie_free(cookie);
190     }
191 }
192
193 void getHostnamesWithCookies(const NetworkStorageSession& session, HashSet<String>& hostnames)
194 {
195     GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(session.cookieStorage()));
196     for (GSList* item = cookies.get(); item; item = g_slist_next(item)) {
197         SoupCookie* cookie = static_cast<SoupCookie*>(item->data);
198         if (cookie->domain)
199             hostnames.add(String::fromUTF8(cookie->domain));
200         soup_cookie_free(cookie);
201     }
202 }
203
204 void deleteCookiesForHostnames(const NetworkStorageSession& session, const Vector<String>& hostnames)
205 {
206     SoupCookieJar* cookieJar = session.cookieStorage();
207
208     for (const auto& hostname : hostnames) {
209         CString hostNameString = hostname.utf8();
210
211         GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar));
212         for (GSList* item = cookies.get(); item; item = g_slist_next(item)) {
213             SoupCookie* cookie = static_cast<SoupCookie*>(item->data);
214             if (soup_cookie_domain_matches(cookie, hostNameString.data()))
215                 soup_cookie_jar_delete_cookie(cookieJar, cookie);
216             soup_cookie_free(cookie);
217         }
218     }
219 }
220
221 void deleteAllCookies(const NetworkStorageSession& session)
222 {
223     SoupCookieJar* cookieJar = session.cookieStorage();
224     GUniquePtr<GSList> cookies(soup_cookie_jar_all_cookies(cookieJar));
225     for (GSList* item = cookies.get(); item; item = g_slist_next(item)) {
226         SoupCookie* cookie = static_cast<SoupCookie*>(item->data);
227         soup_cookie_jar_delete_cookie(cookieJar, cookie);
228         soup_cookie_free(cookie);
229     }
230 }
231
232 void deleteAllCookiesModifiedSince(const NetworkStorageSession& session, WallTime timestamp)
233 {
234     // FIXME: Add support for deleting cookies modified since the given timestamp. It should probably be added to libsoup.
235     if (timestamp == WallTime::fromRawSeconds(0))
236         deleteAllCookies(session);
237     else
238         g_warning("Deleting cookies modified since a given time span is not supported yet");
239 }
240
241 }
242
243 #endif