1/*
2 * Copyright (C) 2012 Igalia S.L.
3 * Copyright (C) 2012 Intel Corporation
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22
23#include "UnitTestUtils/EWK2UnitTestBase.h"
24#include "UnitTestUtils/EWK2UnitTestServer.h"
25#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28
29using namespace EWK2UnitTest;
30using namespace WTF;
31
32extern EWK2UnitTestEnvironment* environment;
33
34static const char FIRST_PARTY_DOMAIN[] = "127.0.0.1";
35static const char THIRD_PARTY_DOMAIN[] = "localhost";
36static const char INDEX_HTML_STRING[] =
37    "<html><body>"
38    " <p>EFLWebKit2 Cookie Manager test</p>"
39    " <img src='http://localhost:%u/image.png' width=5 height=5></img>"
40    "</body></html>";
41
42class EWK2CookieManagerTest : public EWK2UnitTestBase {
43public:
44    static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
45    {
46        if (message->method != SOUP_METHOD_GET) {
47            soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
48            return;
49        }
50
51        soup_message_set_status(message, SOUP_STATUS_OK);
52        if (!strcmp(path, "/index.html")) {
53            Eina_Strbuf* buffer = eina_strbuf_new();
54            eina_strbuf_append_printf(buffer, INDEX_HTML_STRING, soup_server_get_port(server));
55            soup_message_headers_replace(message->response_headers, "Set-Cookie", "foo=bar; Max-Age=60");
56            soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, eina_strbuf_string_steal(buffer), eina_strbuf_length_get(buffer));
57            eina_strbuf_free(buffer);
58        } else if (!strcmp(path, "/image.png"))
59            soup_message_headers_replace(message->response_headers, "Set-Cookie", "baz=qux; Max-Age=60");
60        else
61            soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
62
63        soup_message_body_complete(message->response_body);
64    }
65
66    static void getAcceptPolicyCallback(Ewk_Cookie_Accept_Policy policy, void* event_info)
67    {
68        Ewk_Cookie_Accept_Policy* ret = static_cast<Ewk_Cookie_Accept_Policy*>(event_info);
69        *ret = policy;
70        ecore_main_loop_quit();
71    }
72
73    static void getHostnamesWithCookiesCallback(Eina_List* hostnames, void* event_info)
74    {
75        Eina_List** ret = static_cast<Eina_List**>(event_info);
76        Eina_List* l;
77        void* data;
78        EINA_LIST_FOREACH(hostnames, l, data)
79            *ret = eina_list_append(*ret, eina_stringshare_ref(static_cast<char*>(data)));
80        ecore_main_loop_quit();
81    }
82
83    static int compareHostNames(const void* hostName1, const void* hostName2)
84    {
85        return strcmp(static_cast<const char*>(hostName1), static_cast<const char*>(hostName2));
86    }
87
88    static void onCookiesChanged(void *eventInfo)
89    {
90        bool* cookiesChanged = static_cast<bool*>(eventInfo);
91        *cookiesChanged = true;
92    }
93
94protected:
95    Ewk_Cookie_Accept_Policy getAcceptPolicy(Ewk_Cookie_Manager* manager)
96    {
97        Ewk_Cookie_Accept_Policy policy = EWK_COOKIE_ACCEPT_POLICY_ALWAYS;
98        ewk_cookie_manager_accept_policy_async_get(manager, getAcceptPolicyCallback, &policy);
99        ecore_main_loop_begin();
100        return policy;
101    }
102
103    Eina_List* getHostnamesWithCookies(Ewk_Cookie_Manager* manager)
104    {
105        Eina_List* ret = 0;
106        ewk_cookie_manager_hostnames_with_cookies_async_get(manager, getHostnamesWithCookiesCallback, &ret);
107        ecore_main_loop_begin();
108        return ret;
109    }
110
111    void freeHostNames(Eina_List* hostnames)
112    {
113        void* data;
114        EINA_LIST_FREE(hostnames, data)
115            eina_stringshare_del(static_cast<char*>(data));
116    }
117
118    int countHostnamesWithCookies(Ewk_Cookie_Manager* manager)
119    {
120        Eina_List* hostnames = getHostnamesWithCookies(manager);
121        int count = eina_list_count(hostnames);
122        freeHostNames(hostnames);
123        return count;
124    }
125};
126
127TEST_F(EWK2CookieManagerTest, ewk_cookie_manager_accept_policy)
128{
129    std::unique_ptr<EWK2UnitTestServer> httpServer = std::make_unique<EWK2UnitTestServer>();
130    httpServer->run(serverCallback);
131
132    Ewk_Cookie_Manager* cookieManager = ewk_context_cookie_manager_get(ewk_view_context_get(webView()));
133    ASSERT_TRUE(cookieManager);
134
135    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
136
137    // Default policy is EWK_COOKIE_ACCEPT_POLICY_NO_THIRD_PARTY.
138    ASSERT_EQ(EWK_COOKIE_ACCEPT_POLICY_NO_THIRD_PARTY, getAcceptPolicy(cookieManager));
139
140    Eina_List* hostnames = getHostnamesWithCookies(cookieManager);
141    ASSERT_EQ(1, eina_list_count(hostnames));
142    ASSERT_STREQ(FIRST_PARTY_DOMAIN, static_cast<char*>(eina_list_nth(hostnames, 0)));
143    freeHostNames(hostnames);
144    ewk_cookie_manager_cookies_clear(cookieManager);
145
146    // Change policy to EWK_COOKIE_ACCEPT_POLICY_ALWAYS
147    ewk_cookie_manager_accept_policy_set(cookieManager, EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
148    ASSERT_EQ(EWK_COOKIE_ACCEPT_POLICY_ALWAYS, getAcceptPolicy(cookieManager));
149    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
150
151    hostnames = getHostnamesWithCookies(cookieManager);
152    ASSERT_EQ(2, eina_list_count(hostnames));
153    hostnames = eina_list_sort(hostnames, eina_list_count(hostnames), compareHostNames);
154    ASSERT_STREQ(FIRST_PARTY_DOMAIN, static_cast<char*>(eina_list_nth(hostnames, 0)));
155    ASSERT_STREQ(THIRD_PARTY_DOMAIN, static_cast<char*>(eina_list_nth(hostnames, 1)));
156    freeHostNames(hostnames);
157    ewk_cookie_manager_cookies_clear(cookieManager);
158
159    // Change policy to EWK_COOKIE_ACCEPT_POLICY_NEVER
160    ewk_cookie_manager_accept_policy_set(cookieManager, EWK_COOKIE_ACCEPT_POLICY_NEVER);
161    ASSERT_EQ(EWK_COOKIE_ACCEPT_POLICY_NEVER, getAcceptPolicy(cookieManager));
162    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
163    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
164}
165
166TEST_F(EWK2CookieManagerTest, ewk_cookie_manager_changes_watch)
167{
168    std::unique_ptr<EWK2UnitTestServer> httpServer = std::make_unique<EWK2UnitTestServer>();
169    httpServer->run(serverCallback);
170
171    Ewk_Cookie_Manager* cookieManager = ewk_context_cookie_manager_get(ewk_view_context_get(webView()));
172    ASSERT_TRUE(cookieManager);
173
174    // Load default test page to guarantee that WebProcess or NetworkProcess is launched.
175    ASSERT_TRUE(loadUrlSync(environment->defaultTestPageUrl()));
176
177    ewk_cookie_manager_accept_policy_set(cookieManager, EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
178    ASSERT_EQ(EWK_COOKIE_ACCEPT_POLICY_ALWAYS, getAcceptPolicy(cookieManager));
179
180    // Watch for changes
181    bool cookiesChanged = false;
182    ewk_cookie_manager_changes_watch(cookieManager, onCookiesChanged, &cookiesChanged);
183
184    // Check for cookie changes notifications
185    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
186
187    ASSERT_TRUE(waitUntilTrue(cookiesChanged));
188
189    cookiesChanged = false;
190    ewk_cookie_manager_cookies_clear(cookieManager);
191    ASSERT_TRUE(waitUntilTrue(cookiesChanged));
192
193    // Stop watching for notifications
194    ewk_cookie_manager_changes_watch(cookieManager, 0, 0);
195    cookiesChanged = false;
196    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
197    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
198    ASSERT_FALSE(cookiesChanged);
199
200    // Watch again for notifications
201    ewk_cookie_manager_changes_watch(cookieManager, onCookiesChanged, &cookiesChanged);
202
203    // Make sure we don't get notifications when loading setting an existing persistent storage
204    char storageDirectory[] = "/tmp/ewk2_cookie_manager-XXXXXX";
205    ASSERT_TRUE(mkdtemp(storageDirectory));
206    char textStorage1[64];
207    snprintf(textStorage1, sizeof(textStorage1), "%s/txt-cookie1", storageDirectory);
208    char textStorage2[64];
209    snprintf(textStorage2, sizeof(textStorage2), "%s/txt-cookie2", storageDirectory);
210
211    ewk_cookie_manager_persistent_storage_set(cookieManager, textStorage1, EWK_COOKIE_PERSISTENT_STORAGE_TEXT);
212    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
213    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
214
215    cookiesChanged = false;
216    ewk_cookie_manager_persistent_storage_set(cookieManager, textStorage2, EWK_COOKIE_PERSISTENT_STORAGE_TEXT);
217    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
218
219    ewk_cookie_manager_persistent_storage_set(cookieManager, textStorage1, EWK_COOKIE_PERSISTENT_STORAGE_TEXT);
220    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
221
222    ASSERT_FALSE(cookiesChanged);
223
224    // Final clean up.
225    ewk_cookie_manager_changes_watch(cookieManager, 0, 0);
226    unlink(textStorage1);
227    unlink(textStorage2);
228    rmdir(storageDirectory);
229}
230
231TEST_F(EWK2CookieManagerTest, ewk_cookie_manager_cookies_delete)
232{
233    std::unique_ptr<EWK2UnitTestServer> httpServer = std::make_unique<EWK2UnitTestServer>();
234    httpServer->run(serverCallback);
235
236    Ewk_Cookie_Manager* cookieManager = ewk_context_cookie_manager_get(ewk_view_context_get(webView()));
237    ASSERT_TRUE(cookieManager);
238
239    // Load default test page to guarantee that WebProcess or NetworkProcess is launched.
240    ASSERT_TRUE(loadUrlSync(environment->defaultTestPageUrl()));
241
242    ewk_cookie_manager_accept_policy_set(cookieManager, EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
243    ASSERT_EQ(EWK_COOKIE_ACCEPT_POLICY_ALWAYS, getAcceptPolicy(cookieManager));
244
245    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
246    Eina_List* hostnames = getHostnamesWithCookies(cookieManager);
247    ASSERT_EQ(2, eina_list_count(hostnames));
248    freeHostNames(hostnames);
249
250    // Delete first party cookie
251    ewk_cookie_manager_hostname_cookies_clear(cookieManager, FIRST_PARTY_DOMAIN);
252    hostnames = getHostnamesWithCookies(cookieManager);
253    ASSERT_EQ(1, eina_list_count(hostnames));
254    ASSERT_STREQ(THIRD_PARTY_DOMAIN, static_cast<char*>(eina_list_nth(hostnames, 0)));
255    freeHostNames(hostnames);
256
257    // Delete third party cookie
258    ewk_cookie_manager_hostname_cookies_clear(cookieManager, THIRD_PARTY_DOMAIN);
259    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
260
261    // Get all cookies again
262    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
263    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
264
265    // Clear all cookies
266    ewk_cookie_manager_cookies_clear(cookieManager);
267    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
268}
269
270TEST_F(EWK2CookieManagerTest, DISABLED_ewk_cookie_manager_permanent_storage)
271{
272    std::unique_ptr<EWK2UnitTestServer> httpServer = std::make_unique<EWK2UnitTestServer>();
273    httpServer->run(serverCallback);
274
275    // Generate unique names for cookie storages.
276    char storageDirectory[] = "/tmp/ewk2_cookie_manager-XXXXXX";
277    ASSERT_TRUE(mkdtemp(storageDirectory));
278    char textStorage[64];
279    snprintf(textStorage, sizeof(textStorage), "%s/txt-cookie", storageDirectory);
280    char sqliteStorage[64];
281    snprintf(sqliteStorage, sizeof(sqliteStorage), "%s/sqlite-cookie", storageDirectory);
282
283    Ewk_Cookie_Manager* cookieManager = ewk_context_cookie_manager_get(ewk_view_context_get(webView()));
284    ASSERT_TRUE(cookieManager);
285
286    // Load default test page to guarantee that WebProcess or NetworkProcess is launched.
287    ASSERT_TRUE(loadUrlSync(environment->defaultTestPageUrl()));
288
289    ewk_cookie_manager_accept_policy_set(cookieManager, EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
290    ASSERT_EQ(EWK_COOKIE_ACCEPT_POLICY_ALWAYS, getAcceptPolicy(cookieManager));
291
292    // Text storage using a new file.
293    ewk_cookie_manager_persistent_storage_set(cookieManager, textStorage, EWK_COOKIE_PERSISTENT_STORAGE_TEXT);
294    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
295
296    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
297    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
298
299    // SQLite storage using a new file.
300    ewk_cookie_manager_persistent_storage_set(cookieManager, sqliteStorage, EWK_COOKIE_PERSISTENT_STORAGE_SQLITE);
301    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
302
303    ASSERT_TRUE(loadUrlSync(httpServer->getURLForPath("/index.html").data()));
304    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
305
306    // Text storage using an existing file.
307    ewk_cookie_manager_persistent_storage_set(cookieManager, textStorage, EWK_COOKIE_PERSISTENT_STORAGE_TEXT);
308    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
309    ewk_cookie_manager_cookies_clear(cookieManager);
310    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
311
312    // SQLite storage with an existing file.
313    ewk_cookie_manager_persistent_storage_set(cookieManager, sqliteStorage, EWK_COOKIE_PERSISTENT_STORAGE_SQLITE);
314    ASSERT_EQ(2, countHostnamesWithCookies(cookieManager));
315    ewk_cookie_manager_cookies_clear(cookieManager);
316    ASSERT_EQ(0, countHostnamesWithCookies(cookieManager));
317
318    // Final clean up.
319    unlink(textStorage);
320    unlink(sqliteStorage);
321    rmdir(storageDirectory);
322}
323