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