Remove extern "C" wrapping other includes
[platform/upstream/libsoup.git] / tests / cookies-test.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2010 Igalia S.L.
4  */
5
6 #include "test-utils.h"
7
8 SoupServer *server;
9 SoupURI *first_party_uri, *third_party_uri;
10 const char *first_party = "http://127.0.0.1/";
11 const char *third_party = "http://localhost/";
12
13 static void
14 server_callback (SoupServer *server, SoupMessage *msg,
15                  const char *path, GHashTable *query,
16                  SoupClientContext *context, gpointer data)
17 {
18         if (g_str_equal (path, "/index.html")) {
19                 soup_message_headers_replace (msg->response_headers,
20                                               "Set-Cookie",
21                                               "foo=bar");
22         } else if (g_str_equal (path, "/foo.jpg")) {
23                 soup_message_headers_replace (msg->response_headers,
24                                               "Set-Cookie",
25                                               "baz=qux");
26         } else if (soup_message_headers_get_one (msg->request_headers,
27                                                  "Echo-Set-Cookie")) {
28                 soup_message_headers_replace (msg->response_headers,
29                                               "Set-Cookie",
30                                               soup_message_headers_get_one (msg->request_headers,
31                                                                             "Echo-Set-Cookie"));
32         }
33
34         soup_message_set_status (msg, SOUP_STATUS_OK);
35 }
36
37 typedef struct {
38         SoupCookieJarAcceptPolicy policy;
39         gboolean try_third_party_again;
40         int n_cookies;
41 } CookiesForPolicy;
42
43 static const CookiesForPolicy validResults[] = {
44         { SOUP_COOKIE_JAR_ACCEPT_ALWAYS, FALSE, 2 },
45         { SOUP_COOKIE_JAR_ACCEPT_NEVER, FALSE, 0 },
46         { SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY, FALSE, 1 },
47         { SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, FALSE, 1 },
48         { SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY, TRUE, 2 }
49 };
50
51 static void
52 do_cookies_accept_policy_test (void)
53 {
54         SoupSession *session;
55         SoupMessage *msg;
56         SoupURI *uri;
57         SoupCookieJar *jar;
58         GSList *l, *p;
59         int i;
60
61         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
62         soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
63         jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));
64
65         for (i = 0; i < G_N_ELEMENTS (validResults); i++) {
66                 soup_cookie_jar_set_accept_policy (jar, validResults[i].policy);
67
68                 /* We can't use two servers due to limitations in
69                  * test_server, so let's swap first and third party here
70                  * to simulate a cookie coming from a third party.
71                  */
72                 uri = soup_uri_new_with_base (first_party_uri, "/foo.jpg");
73                 msg = soup_message_new_from_uri ("GET", uri);
74                 soup_message_set_first_party (msg, third_party_uri);
75                 soup_session_send_message (session, msg);
76                 soup_uri_free (uri);
77                 g_object_unref (msg);
78
79                 uri = soup_uri_new_with_base (first_party_uri, "/index.html");
80                 msg = soup_message_new_from_uri ("GET", uri);
81                 soup_message_set_first_party (msg, first_party_uri);
82                 soup_session_send_message (session, msg);
83                 soup_uri_free (uri);
84                 g_object_unref (msg);
85
86                 if (validResults[i].try_third_party_again) {
87                         uri = soup_uri_new_with_base (first_party_uri, "/foo.jpg");
88                         msg = soup_message_new_from_uri ("GET", uri);
89                         soup_message_set_first_party (msg, third_party_uri);
90                         soup_session_send_message (session, msg);
91                         soup_uri_free (uri);
92                         g_object_unref (msg);
93                 }
94
95                 l = soup_cookie_jar_all_cookies (jar);
96                 g_assert_cmpint (g_slist_length (l), ==, validResults[i].n_cookies);
97
98                 for (p = l; p; p = p->next) {
99                         soup_cookie_jar_delete_cookie (jar, p->data);
100                         soup_cookie_free (p->data);
101                 }
102
103                 g_slist_free (l);
104         }
105
106         soup_test_session_abort_unref (session);
107 }
108
109 static void
110 do_cookies_subdomain_policy_test (void)
111 {
112         SoupCookieJar *jar;
113         GSList *cookies;
114         SoupURI *uri1;
115         SoupURI *uri2;
116         SoupURI *uri3;
117
118         g_test_bug ("792130");
119
120         /* Only the base domain should be considered when deciding
121          * whether a cookie is a third-party cookie.
122          */
123         uri1 = soup_uri_new ("https://www.gnome.org");
124         uri2 = soup_uri_new ("https://foundation.gnome.org");
125         uri3 = soup_uri_new ("https://www.gnome.org.");
126
127         /* We can't check subdomains with a test server running on
128          * localhost, so we'll just check the cookie jar API itself.
129          */
130
131         /* Cookie should be accepted. One cookie in the jar. */
132         jar = soup_cookie_jar_new ();
133         soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY);
134         soup_cookie_jar_set_cookie_with_first_party (jar, uri1, uri2, "1=foo");
135         cookies = soup_cookie_jar_all_cookies (jar);
136         g_assert_cmpint (g_slist_length (cookies), ==, 1);
137         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
138
139         /* Cookie should be accepted. Two cookies in the jar. */
140         soup_cookie_jar_set_cookie_with_first_party (jar, uri2, uri1, "2=foo");
141         cookies = soup_cookie_jar_all_cookies (jar);
142         g_assert_cmpint (g_slist_length (cookies), ==, 2);
143         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
144
145         /* Third-party cookie should be rejected, so there are still
146          * only two cookies in the jar.
147          */
148         soup_cookie_jar_set_cookie_with_first_party (jar, third_party_uri, uri1, "3=foo");
149         cookies = soup_cookie_jar_all_cookies (jar);
150         g_assert_cmpint (g_slist_length (cookies), ==, 2);
151         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
152
153         /* Now, we allow the "third-party" to set one cookie as the
154          * first party. Three cookies in the jar.
155          */
156         soup_cookie_jar_set_cookie_with_first_party (jar, third_party_uri, third_party_uri, "4=foo");
157         cookies = soup_cookie_jar_all_cookies (jar);
158         g_assert_cmpint (g_slist_length (cookies), ==, 3);
159         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
160
161         /* Third-party cookie should now be allowed by grandfathering, though
162          * it was blocked before on the same URL. Four cookies in the jar.
163          */
164         soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY);
165         soup_cookie_jar_set_cookie_with_first_party (jar, third_party_uri, uri1, "5=foo");
166         cookies = soup_cookie_jar_all_cookies (jar);
167         g_assert_cmpint (g_slist_length (cookies), ==, 4);
168         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
169
170         /* Now some Domain attribute tests.*/
171         soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_ALWAYS);
172
173         /* The cookie must be rejected if the Domain is not an appropriate
174          * match for the URI. Still four cookies in the jar.
175          */
176         soup_cookie_jar_set_cookie (jar, uri1, "6=foo; Domain=gitlab.gnome.org");
177         cookies = soup_cookie_jar_all_cookies (jar);
178         g_assert_cmpint (g_slist_length (cookies), ==, 4);
179         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
180
181         /* Now the Domain is an appropriate match. Five cookies in the jar. */
182         soup_cookie_jar_set_cookie (jar, uri1, "7=foo; Domain=gnome.org");
183         cookies = soup_cookie_jar_all_cookies (jar);
184         g_assert_cmpint (g_slist_length (cookies), ==, 5);
185         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
186
187         /* A leading dot in the domain property should not affect things.
188          * This cookie should be accepted. Six cookies in the jar.
189          */
190         soup_cookie_jar_set_cookie (jar, uri1, "8=foo; Domain=.www.gnome.org");
191         cookies = soup_cookie_jar_all_cookies (jar);
192         g_assert_cmpint (g_slist_length (cookies), ==, 6);
193         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
194
195         /* The cookie must be rejected if the Domain ends in a trailing dot
196          * but the uri doesn't.
197          */
198         soup_cookie_jar_set_cookie (jar, uri1, "9=foo; Domain=www.gnome.org.");
199         cookies = soup_cookie_jar_all_cookies (jar);
200         g_assert_cmpint (g_slist_length (cookies), ==, 6);
201         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
202
203         /* The cookie should be accepted if both Domain and URI end with a trailing
204          * dot and they are a match. Seven cookies in the jar.
205          */
206         soup_cookie_jar_set_cookie (jar, uri3, "10=foo; Domain=gnome.org.");
207         cookies = soup_cookie_jar_all_cookies (jar);
208         g_assert_cmpint (g_slist_length (cookies), ==, 7);
209         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
210
211         /* The cookie should be rejected if URI has trailing dot but Domain doesn't.
212          * Seven cookies in the jar.
213          */
214         soup_cookie_jar_set_cookie (jar, uri3, "11=foo; Domain=gnome.org");
215         cookies = soup_cookie_jar_all_cookies (jar);
216         g_assert_cmpint (g_slist_length (cookies), ==, 7);
217         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
218
219         /* It should not be possible to set a cookie for a TLD. Still seven
220          * cookies in the jar.
221          */
222         soup_cookie_jar_set_cookie (jar, uri1, "12=foo; Domain=.org");
223         cookies = soup_cookie_jar_all_cookies (jar);
224         g_assert_cmpint (g_slist_length (cookies), ==, 7);
225         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
226
227         /* It should still not be possible to set a cookie for a TLD, even if
228          * we are tricksy and have a trailing dot. Still only seven cookies.
229          */
230         soup_cookie_jar_set_cookie (jar, uri3, "13=foo; Domain=.org.");
231         cookies = soup_cookie_jar_all_cookies (jar);
232         g_assert_cmpint (g_slist_length (cookies), ==, 7);
233         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
234
235         soup_uri_free (uri1);
236         soup_uri_free (uri2);
237         soup_uri_free (uri3);
238         g_object_unref (jar);
239 }
240
241 static void
242 do_cookies_strict_secure_test (void)
243 {
244         SoupCookieJar *jar;
245         GSList *cookies;
246         SoupURI *insecure_uri;
247         SoupURI *secure_uri;
248
249         insecure_uri = soup_uri_new ("http://gnome.org");
250         secure_uri = soup_uri_new ("https://gnome.org");
251         jar = soup_cookie_jar_new ();
252
253         /* Set a cookie from secure origin */
254         soup_cookie_jar_set_cookie (jar, secure_uri, "1=foo; secure");
255         cookies = soup_cookie_jar_all_cookies (jar);
256         g_assert_cmpint (g_slist_length (cookies), ==, 1);
257         g_assert_cmpstr (soup_cookie_get_value(cookies->data), ==, "foo");
258         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
259
260         /* Do not allow an insecure origin to overwrite a secure cookie */
261         soup_cookie_jar_set_cookie (jar, insecure_uri, "1=bar");
262         cookies = soup_cookie_jar_all_cookies (jar);
263         g_assert_cmpint (g_slist_length (cookies), ==, 1);
264         g_assert_cmpstr (soup_cookie_get_value(cookies->data), ==, "foo");
265         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
266
267         /* Secure can only be set by from secure origin */
268         soup_cookie_jar_set_cookie (jar, insecure_uri, "2=foo; secure");
269         cookies = soup_cookie_jar_all_cookies (jar);
270         g_assert_cmpint (g_slist_length (cookies), ==, 1);
271         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
272
273         /* But we can make one for another path */
274         soup_cookie_jar_set_cookie (jar, insecure_uri, "1=foo; path=/foo");
275         cookies = soup_cookie_jar_all_cookies (jar);
276         g_assert_cmpint (g_slist_length (cookies), ==, 2);
277         g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
278
279         soup_uri_free (insecure_uri);
280         soup_uri_free (secure_uri);
281         g_object_unref (jar);
282 }
283
284 /* FIXME: moar tests! */
285 static void
286 do_cookies_parsing_test (void)
287 {
288         SoupSession *session;
289         SoupMessage *msg;
290         SoupCookieJar *jar;
291         GSList *cookies, *iter;
292         SoupCookie *cookie;
293         gboolean got1, got2, got3;
294
295         g_test_bug ("678753");
296
297         session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
298         soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
299         jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));
300
301         /* "httponly" is case-insensitive, and its value (if any) is ignored */
302         msg = soup_message_new_from_uri ("GET", first_party_uri);
303         soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
304                                      "one=1; httponly; max-age=100");
305         soup_session_send_message (session, msg);
306         g_object_unref (msg);
307
308         msg = soup_message_new_from_uri ("GET", first_party_uri);
309         soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
310                                      "two=2; HttpOnly; max-age=100; SameSite=Invalid");
311         soup_session_send_message (session, msg);
312         g_object_unref (msg);
313
314         msg = soup_message_new_from_uri ("GET", first_party_uri);
315         soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
316                                      "three=3; httpONLY=Wednesday; max-age=100; SameSite=Lax");
317         soup_session_send_message (session, msg);
318         g_object_unref (msg);
319
320         cookies = soup_cookie_jar_get_cookie_list (jar, first_party_uri, TRUE);
321         got1 = got2 = got3 = FALSE;
322
323         for (iter = cookies; iter; iter = iter->next) {
324                 cookie = iter->data;
325
326                 if (!strcmp (soup_cookie_get_name (cookie), "one")) {
327                         got1 = TRUE;
328                         g_assert_true (soup_cookie_get_http_only (cookie));
329                         g_assert_true (soup_cookie_get_expires (cookie) != NULL);
330                 } else if (!strcmp (soup_cookie_get_name (cookie), "two")) {
331                         got2 = TRUE;
332                         g_assert_true (soup_cookie_get_http_only (cookie));
333                         g_assert_true (soup_cookie_get_expires (cookie) != NULL);
334                         g_assert_cmpint (soup_cookie_get_same_site_policy (cookie), ==, SOUP_SAME_SITE_POLICY_NONE);
335                 } else if (!strcmp (soup_cookie_get_name (cookie), "three")) {
336                         got3 = TRUE;
337                         g_assert_true (soup_cookie_get_http_only (cookie));
338                         g_assert_true (soup_cookie_get_expires (cookie) != NULL);
339                         g_assert_cmpint (soup_cookie_get_same_site_policy (cookie), ==, SOUP_SAME_SITE_POLICY_LAX);
340                 } else {
341                         soup_test_assert (FALSE, "got unexpected cookie '%s'",
342                                           soup_cookie_get_name (cookie));
343                 }
344
345                 soup_cookie_free (cookie);
346         }
347         g_slist_free (cookies);
348
349         g_assert_true (got1);
350         g_assert_true (got2);
351         g_assert_true (got3);
352
353         soup_test_session_abort_unref (session);
354 }       
355
356 static void
357 do_cookies_parsing_nopath_nullorigin (void)
358 {
359         SoupCookie *cookie = soup_cookie_parse ("NAME=Value", NULL);
360         g_assert_nonnull (cookie);
361         g_assert_cmpstr ("/", ==, soup_cookie_get_path (cookie));
362         soup_cookie_free (cookie);
363 }
364
365 static void
366 do_get_cookies_empty_host_test (void)
367 {
368         SoupCookieJar *jar;
369         SoupURI *uri;
370         char *cookies;
371
372         jar = soup_cookie_jar_new ();
373         uri = soup_uri_new ("file:///whatever.html");
374
375         cookies = soup_cookie_jar_get_cookies (jar, uri, FALSE);
376
377         g_assert_null (cookies);
378
379         g_object_unref (jar);
380         soup_uri_free (uri);
381 }
382
383 static void
384 send_callback (GObject *source_object,
385                GAsyncResult *res,
386                GMainLoop *loop)
387 {
388         g_main_loop_quit (loop);
389 }
390
391 static void
392 do_remove_feature_test (void)
393 {
394         SoupSession *session;
395         SoupMessage *msg;
396         SoupURI *uri;
397         GMainLoop *loop;
398
399         session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
400         soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
401         uri = soup_uri_new_with_base (first_party_uri, "/index.html");
402         msg = soup_message_new_from_uri ("GET", uri);
403         soup_message_set_first_party (msg, first_party_uri);
404
405         loop = g_main_loop_new (NULL, TRUE);
406         soup_session_send_async (session, msg, NULL, (GAsyncReadyCallback)send_callback, loop);
407         soup_session_remove_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
408
409         g_main_loop_run(loop);
410
411         g_main_loop_unref (loop);
412         g_object_unref (msg);
413         soup_uri_free (uri);
414 }
415
416 int
417 main (int argc, char **argv)
418 {
419         SoupURI *server_uri;
420         int ret;
421
422         test_init (argc, argv, NULL);
423
424         server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
425         soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
426         server_uri = soup_test_server_get_uri (server, "http", NULL);
427
428         first_party_uri = soup_uri_new (first_party);
429         third_party_uri = soup_uri_new (third_party);
430         soup_uri_set_port (first_party_uri, server_uri->port);
431         soup_uri_set_port (third_party_uri, server_uri->port);
432
433         g_test_add_func ("/cookies/accept-policy", do_cookies_accept_policy_test);
434         g_test_add_func ("/cookies/accept-policy-subdomains", do_cookies_subdomain_policy_test);
435         g_test_add_func ("/cookies/parsing", do_cookies_parsing_test);
436         g_test_add_func ("/cookies/parsing/no-path-null-origin", do_cookies_parsing_nopath_nullorigin);
437         g_test_add_func ("/cookies/get-cookies/empty-host", do_get_cookies_empty_host_test);
438         g_test_add_func ("/cookies/remove-feature", do_remove_feature_test);
439         g_test_add_func ("/cookies/secure-cookies", do_cookies_strict_secure_test);
440
441         ret = g_test_run ();
442
443         soup_uri_free (first_party_uri);
444         soup_uri_free (third_party_uri);
445         soup_uri_free (server_uri);
446         soup_test_server_quit_unref (server);
447
448         test_cleanup ();
449         return ret;
450 }