1/* 2 * Copyright (C) 2012 Igalia S.L. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2,1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 */ 19 20#include "config.h" 21#include "WebKitCookieManager.h" 22 23#include "APIString.h" 24#include "SoupCookiePersistentStorageType.h" 25#include "WebCookieManagerProxy.h" 26#include "WebKitCookieManagerPrivate.h" 27#include "WebKitEnumTypes.h" 28#include <wtf/gobject/GRefPtr.h> 29#include <wtf/text/CString.h> 30 31using namespace WebKit; 32 33/** 34 * SECTION: WebKitCookieManager 35 * @Short_description: Defines how to handle cookies in a #WebKitWebContext 36 * @Title: WebKitCookieManager 37 * 38 * The #WebKitCookieManager defines how to handle cookies in a 39 * #WebKitWebContext. Get it from the context with 40 * webkit_web_context_get_cookie_manager(), and use it to set where to 41 * store cookies, with webkit_cookie_manager_set_persistent_storage(), 42 * to get the list of domains with cookies, with 43 * webkit_cookie_manager_get_domains_with_cookies(), or to set the 44 * acceptance policy, with webkit_cookie_manager_get_accept_policy() 45 * (among other actions). 46 * 47 */ 48 49enum { 50 CHANGED, 51 52 LAST_SIGNAL 53}; 54 55struct _WebKitCookieManagerPrivate { 56 ~_WebKitCookieManagerPrivate() 57 { 58 webCookieManager->stopObservingCookieChanges(); 59 } 60 61 RefPtr<WebCookieManagerProxy> webCookieManager; 62}; 63 64static guint signals[LAST_SIGNAL] = { 0, }; 65 66WEBKIT_DEFINE_TYPE(WebKitCookieManager, webkit_cookie_manager, G_TYPE_OBJECT) 67 68static inline SoupCookiePersistentStorageType toSoupCookiePersistentStorageType(WebKitCookiePersistentStorage kitStorage) 69{ 70 switch (kitStorage) { 71 case WEBKIT_COOKIE_PERSISTENT_STORAGE_TEXT: 72 return SoupCookiePersistentStorageText; 73 case WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE: 74 return SoupCookiePersistentStorageSQLite; 75 default: 76 ASSERT_NOT_REACHED(); 77 return SoupCookiePersistentStorageText; 78 } 79} 80 81static inline WebKitCookieAcceptPolicy toWebKitCookieAcceptPolicy(HTTPCookieAcceptPolicy httpPolicy) 82{ 83 switch (httpPolicy) { 84 case HTTPCookieAcceptPolicyAlways: 85 return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS; 86 case HTTPCookieAcceptPolicyNever: 87 return WEBKIT_COOKIE_POLICY_ACCEPT_NEVER; 88 case HTTPCookieAcceptPolicyOnlyFromMainDocumentDomain: 89 return WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY; 90 default: 91 ASSERT_NOT_REACHED(); 92 return WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS; 93 } 94} 95 96static inline HTTPCookieAcceptPolicy toHTTPCookieAcceptPolicy(WebKitCookieAcceptPolicy kitPolicy) 97{ 98 switch (kitPolicy) { 99 case WEBKIT_COOKIE_POLICY_ACCEPT_ALWAYS: 100 return HTTPCookieAcceptPolicyAlways; 101 case WEBKIT_COOKIE_POLICY_ACCEPT_NEVER: 102 return HTTPCookieAcceptPolicyNever; 103 case WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY: 104 return HTTPCookieAcceptPolicyOnlyFromMainDocumentDomain; 105 default: 106 ASSERT_NOT_REACHED(); 107 return HTTPCookieAcceptPolicyAlways; 108 } 109} 110 111static void webkit_cookie_manager_class_init(WebKitCookieManagerClass* findClass) 112{ 113 GObjectClass* gObjectClass = G_OBJECT_CLASS(findClass); 114 115 /** 116 * WebKitCookieManager::changed: 117 * @cookie_manager: the #WebKitCookieManager 118 * 119 * This signal is emitted when cookies are added, removed or modified. 120 */ 121 signals[CHANGED] = 122 g_signal_new("changed", 123 G_TYPE_FROM_CLASS(gObjectClass), 124 G_SIGNAL_RUN_LAST, 125 0, 0, 0, 126 g_cclosure_marshal_VOID__VOID, 127 G_TYPE_NONE, 0); 128} 129 130static void cookiesDidChange(WKCookieManagerRef, const void* clientInfo) 131{ 132 g_signal_emit(WEBKIT_COOKIE_MANAGER(clientInfo), signals[CHANGED], 0); 133} 134 135WebKitCookieManager* webkitCookieManagerCreate(WebCookieManagerProxy* webCookieManager) 136{ 137 WebKitCookieManager* manager = WEBKIT_COOKIE_MANAGER(g_object_new(WEBKIT_TYPE_COOKIE_MANAGER, NULL)); 138 manager->priv->webCookieManager = webCookieManager; 139 140 WKCookieManagerClientV0 wkCookieManagerClient = { 141 { 142 0, // version 143 manager, // clientInfo 144 }, 145 cookiesDidChange 146 }; 147 WKCookieManagerSetClient(toAPI(webCookieManager), &wkCookieManagerClient.base); 148 manager->priv->webCookieManager->startObservingCookieChanges(); 149 150 return manager; 151} 152 153/** 154 * webkit_cookie_manager_set_persistent_storage: 155 * @cookie_manager: a #WebKitCookieManager 156 * @filename: the filename to read to/write from 157 * @storage: a #WebKitCookiePersistentStorage 158 * 159 * Set the @filename where non-session cookies are stored persistently using 160 * @storage as the format to read/write the cookies. 161 * Cookies are initially read from @filename to create an initial set of cookies. 162 * Then, non-session cookies will be written to @filename when the WebKitCookieManager::changed 163 * signal is emitted. 164 * By default, @cookie_manager doesn't store the cookies persistenly, so you need to call this 165 * method to keep cookies saved across sessions. 166 */ 167void webkit_cookie_manager_set_persistent_storage(WebKitCookieManager* manager, const char* filename, WebKitCookiePersistentStorage storage) 168{ 169 g_return_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager)); 170 g_return_if_fail(filename); 171 172 manager->priv->webCookieManager->stopObservingCookieChanges(); 173 manager->priv->webCookieManager->setCookiePersistentStorage(String::fromUTF8(filename), toSoupCookiePersistentStorageType(storage)); 174 manager->priv->webCookieManager->startObservingCookieChanges(); 175} 176 177/** 178 * webkit_cookie_manager_set_accept_policy: 179 * @cookie_manager: a #WebKitCookieManager 180 * @policy: a #WebKitCookieAcceptPolicy 181 * 182 * Set the cookie acceptance policy of @cookie_manager as @policy. 183 */ 184void webkit_cookie_manager_set_accept_policy(WebKitCookieManager* manager, WebKitCookieAcceptPolicy policy) 185{ 186 g_return_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager)); 187 188 manager->priv->webCookieManager->setHTTPCookieAcceptPolicy(toHTTPCookieAcceptPolicy(policy)); 189} 190 191static void webkitCookieManagerGetAcceptPolicyCallback(WKHTTPCookieAcceptPolicy policy, WKErrorRef, void* context) 192{ 193 GRefPtr<GTask> task = adoptGRef(G_TASK(context)); 194 g_task_return_int(task.get(), toWebKitCookieAcceptPolicy(toHTTPCookieAcceptPolicy(policy))); 195} 196 197/** 198 * webkit_cookie_manager_get_accept_policy: 199 * @cookie_manager: a #WebKitCookieManager 200 * @cancellable: (allow-none): a #GCancellable or %NULL to ignore 201 * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied 202 * @user_data: (closure): the data to pass to callback function 203 * 204 * Asynchronously get the cookie acceptance policy of @cookie_manager. 205 * 206 * When the operation is finished, @callback will be called. You can then call 207 * webkit_cookie_manager_get_accept_policy_finish() to get the result of the operation. 208 */ 209void webkit_cookie_manager_get_accept_policy(WebKitCookieManager* manager, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) 210{ 211 g_return_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager)); 212 213 GTask* task = g_task_new(manager, cancellable, callback, userData); 214 manager->priv->webCookieManager->getHTTPCookieAcceptPolicy(toGenericCallbackFunction<WKHTTPCookieAcceptPolicy, HTTPCookieAcceptPolicy>(task, webkitCookieManagerGetAcceptPolicyCallback)); 215} 216 217/** 218 * webkit_cookie_manager_get_accept_policy_finish: 219 * @cookie_manager: a #WebKitCookieManager 220 * @result: a #GAsyncResult 221 * @error: return location for error or %NULL to ignore 222 * 223 * Finish an asynchronous operation started with webkit_cookie_manager_get_accept_policy(). 224 * 225 * Returns: the cookie acceptance policy of @cookie_manager as a #WebKitCookieAcceptPolicy. 226 */ 227WebKitCookieAcceptPolicy webkit_cookie_manager_get_accept_policy_finish(WebKitCookieManager* manager, GAsyncResult* result, GError** error) 228{ 229 g_return_val_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager), WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY); 230 g_return_val_if_fail(g_task_is_valid(result, manager), WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY); 231 232 gssize returnValue = g_task_propagate_int(G_TASK(result), error); 233 return returnValue == -1 ? WEBKIT_COOKIE_POLICY_ACCEPT_NO_THIRD_PARTY : static_cast<WebKitCookieAcceptPolicy>(returnValue); 234} 235 236static void webkitCookieManagerGetDomainsWithCookiesCallback(WKArrayRef wkDomains, WKErrorRef, void* context) 237{ 238 GRefPtr<GTask> task = adoptGRef(G_TASK(context)); 239 if (g_task_return_error_if_cancelled(task.get())) 240 return; 241 242 API::Array* domains = toImpl(wkDomains); 243 GPtrArray* returnValue = g_ptr_array_sized_new(domains->size()); 244 for (size_t i = 0; i < domains->size(); ++i) { 245 API::String* domainString = static_cast<API::String*>(domains->at(i)); 246 String domain = domainString->string(); 247 if (domain.isEmpty()) 248 continue; 249 g_ptr_array_add(returnValue, g_strdup(domain.utf8().data())); 250 } 251 g_ptr_array_add(returnValue, 0); 252 g_task_return_pointer(task.get(), g_ptr_array_free(returnValue, FALSE), reinterpret_cast<GDestroyNotify>(g_strfreev)); 253} 254 255/** 256 * webkit_cookie_manager_get_domains_with_cookies: 257 * @cookie_manager: a #WebKitCookieManager 258 * @cancellable: (allow-none): a #GCancellable or %NULL to ignore 259 * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied 260 * @user_data: (closure): the data to pass to callback function 261 * 262 * Asynchronously get the list of domains for which @cookie_manager contains cookies. 263 * 264 * When the operation is finished, @callback will be called. You can then call 265 * webkit_cookie_manager_get_domains_with_cookies_finish() to get the result of the operation. 266 */ 267void webkit_cookie_manager_get_domains_with_cookies(WebKitCookieManager* manager, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) 268{ 269 g_return_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager)); 270 271 GTask* task = g_task_new(manager, cancellable, callback, userData); 272 manager->priv->webCookieManager->getHostnamesWithCookies(toGenericCallbackFunction(task, webkitCookieManagerGetDomainsWithCookiesCallback)); 273} 274 275/** 276 * webkit_cookie_manager_get_domains_with_cookies_finish: 277 * @cookie_manager: a #WebKitCookieManager 278 * @result: a #GAsyncResult 279 * @error: return location for error or %NULL to ignore 280 * 281 * Finish an asynchronous operation started with webkit_cookie_manager_get_domains_with_cookies(). 282 * The return value is a %NULL terminated list of strings which should 283 * be released with g_strfreev(). 284 * 285 * Returns: (transfer full) (array zero-terminated=1): A %NULL terminated array of domain names 286 * or %NULL in case of error. 287 */ 288gchar** webkit_cookie_manager_get_domains_with_cookies_finish(WebKitCookieManager* manager, GAsyncResult* result, GError** error) 289{ 290 g_return_val_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager), 0); 291 g_return_val_if_fail(g_task_is_valid(result, manager), 0); 292 293 return reinterpret_cast<char**>(g_task_propagate_pointer(G_TASK(result), error)); 294} 295 296/** 297 * webkit_cookie_manager_delete_cookies_for_domain: 298 * @cookie_manager: a #WebKitCookieManager 299 * @domain: a domain name 300 * 301 * Remove all cookies of @cookie_manager for the given @domain. 302 */ 303void webkit_cookie_manager_delete_cookies_for_domain(WebKitCookieManager* manager, const gchar* domain) 304{ 305 g_return_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager)); 306 g_return_if_fail(domain); 307 308 manager->priv->webCookieManager->deleteCookiesForHostname(String::fromUTF8(domain)); 309} 310 311/** 312 * webkit_cookie_manager_delete_all_cookies: 313 * @cookie_manager: a #WebKitCookieManager 314 * 315 * Delete all cookies of @cookie_manager 316 */ 317void webkit_cookie_manager_delete_all_cookies(WebKitCookieManager* manager) 318{ 319 g_return_if_fail(WEBKIT_IS_COOKIE_MANAGER(manager)); 320 321 manager->priv->webCookieManager->deleteAllCookies(); 322} 323