Soup-2.4.gir: add missing introspection data from Vala bindings
[platform/upstream/libsoup.git] / libsoup / soup-cookie-jar.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-cookie-jar.c
4  *
5  * Copyright (C) 2008 Red Hat, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdio.h>
13 #include <string.h>
14
15 #include "soup-cookie.h"
16 #include "soup-cookie-jar.h"
17 #include "soup-date.h"
18 #include "soup-enum-types.h"
19 #include "soup-marshal.h"
20 #include "soup-message.h"
21 #include "soup-session-feature.h"
22 #include "soup-uri.h"
23
24 /**
25  * SECTION:soup-cookie-jar
26  * @short_description: Automatic cookie handling for #SoupSession
27  *
28  * A #SoupCookieJar stores #SoupCookie<!-- -->s and arrange for them
29  * to be sent with the appropriate #SoupMessage<!-- -->s.
30  * #SoupCookieJar implements #SoupSessionFeature, so you can add a
31  * cookie jar to a session with soup_session_add_feature() or
32  * soup_session_add_feature_by_type().
33  *
34  * Note that the base #SoupCookieJar class does not support any form
35  * of long-term cookie persistence.
36  **/
37
38 static void soup_cookie_jar_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
39 static void request_queued (SoupSessionFeature *feature, SoupSession *session,
40                             SoupMessage *msg);
41 static void request_started (SoupSessionFeature *feature, SoupSession *session,
42                              SoupMessage *msg, SoupSocket *socket);
43 static void request_unqueued (SoupSessionFeature *feature, SoupSession *session,
44                               SoupMessage *msg);
45
46 G_DEFINE_TYPE_WITH_CODE (SoupCookieJar, soup_cookie_jar, G_TYPE_OBJECT,
47                          G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
48                                                 soup_cookie_jar_session_feature_init))
49
50 enum {
51         CHANGED,
52         LAST_SIGNAL
53 };
54
55 static guint signals[LAST_SIGNAL] = { 0 };
56
57 enum {
58         PROP_0,
59
60         PROP_READ_ONLY,
61         PROP_ACCEPT_POLICY,
62
63         LAST_PROP
64 };
65
66 typedef struct {
67         gboolean constructed, read_only;
68         GHashTable *domains, *serials;
69         guint serial;
70         SoupCookieJarAcceptPolicy accept_policy;
71 } SoupCookieJarPrivate;
72 #define SOUP_COOKIE_JAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR, SoupCookieJarPrivate))
73
74 static void set_property (GObject *object, guint prop_id,
75                           const GValue *value, GParamSpec *pspec);
76 static void get_property (GObject *object, guint prop_id,
77                           GValue *value, GParamSpec *pspec);
78
79 static void
80 soup_cookie_jar_init (SoupCookieJar *jar)
81 {
82         SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
83
84         priv->domains = g_hash_table_new_full (soup_str_case_hash,
85                                                soup_str_case_equal,
86                                                g_free, NULL);
87         priv->serials = g_hash_table_new (NULL, NULL);
88         priv->accept_policy = SOUP_COOKIE_JAR_ACCEPT_ALWAYS;
89 }
90
91 static void
92 constructed (GObject *object)
93 {
94         SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (object);
95
96         priv->constructed = TRUE;
97 }
98
99 static void
100 finalize (GObject *object)
101 {
102         SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (object);
103         GHashTableIter iter;
104         gpointer key, value;
105
106         g_hash_table_iter_init (&iter, priv->domains);
107         while (g_hash_table_iter_next (&iter, &key, &value))
108                 soup_cookies_free (value);
109         g_hash_table_destroy (priv->domains);
110         g_hash_table_destroy (priv->serials);
111
112         G_OBJECT_CLASS (soup_cookie_jar_parent_class)->finalize (object);
113 }
114
115 static void
116 soup_cookie_jar_class_init (SoupCookieJarClass *jar_class)
117 {
118         GObjectClass *object_class = G_OBJECT_CLASS (jar_class);
119
120         g_type_class_add_private (jar_class, sizeof (SoupCookieJarPrivate));
121
122         object_class->constructed = constructed;
123         object_class->finalize = finalize;
124         object_class->set_property = set_property;
125         object_class->get_property = get_property;
126
127         /**
128          * SoupCookieJar::changed
129          * @jar: the #SoupCookieJar
130          * @old_cookie: the old #SoupCookie value
131          * @new_cookie: the new #SoupCookie value
132          *
133          * Emitted when @jar changes. If a cookie has been added,
134          * @new_cookie will contain the newly-added cookie and
135          * @old_cookie will be %NULL. If a cookie has been deleted,
136          * @old_cookie will contain the to-be-deleted cookie and
137          * @new_cookie will be %NULL. If a cookie has been changed,
138          * @old_cookie will contain its old value, and @new_cookie its
139          * new value.
140          **/
141         signals[CHANGED] =
142                 g_signal_new ("changed",
143                               G_OBJECT_CLASS_TYPE (object_class),
144                               G_SIGNAL_RUN_FIRST,
145                               G_STRUCT_OFFSET (SoupCookieJarClass, changed),
146                               NULL, NULL,
147                               soup_marshal_NONE__BOXED_BOXED,
148                               G_TYPE_NONE, 2, 
149                               SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE,
150                               SOUP_TYPE_COOKIE | G_SIGNAL_TYPE_STATIC_SCOPE);
151
152         /**
153          * SOUP_COOKIE_JAR_READ_ONLY:
154          *
155          * Alias for the #SoupCookieJar:read-only property. (Whether
156          * or not the cookie jar is read-only.)
157          **/
158         g_object_class_install_property (
159                 object_class, PROP_READ_ONLY,
160                 g_param_spec_boolean (SOUP_COOKIE_JAR_READ_ONLY,
161                                       "Read-only",
162                                       "Whether or not the cookie jar is read-only",
163                                       FALSE,
164                                       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
165
166         /**
167          * SOUP_COOKIE_JAR_ACCEPT_POLICY:
168          *
169          * Alias for the #SoupCookieJar:accept-policy property.
170          *
171          * Since: 2.30
172          */
173         /**
174          * SoupCookieJar:accept-policy:
175          *
176          * The policy the jar should follow to accept or reject cookies
177          *
178          * Since: 2.30
179          */
180         g_object_class_install_property (
181                 object_class, PROP_ACCEPT_POLICY,
182                 g_param_spec_enum (SOUP_COOKIE_JAR_ACCEPT_POLICY,
183                                    "Accept-policy",
184                                    "The policy the jar should follow to accept or reject cookies",
185                                    SOUP_TYPE_COOKIE_JAR_ACCEPT_POLICY,
186                                    SOUP_COOKIE_JAR_ACCEPT_ALWAYS,
187                                    G_PARAM_READWRITE));
188 }
189
190 static void
191 soup_cookie_jar_session_feature_init (SoupSessionFeatureInterface *feature_interface,
192                                       gpointer interface_data)
193 {
194         feature_interface->request_queued = request_queued;
195         feature_interface->request_started = request_started;
196         feature_interface->request_unqueued = request_unqueued;
197 }
198
199 static void
200 set_property (GObject *object, guint prop_id,
201               const GValue *value, GParamSpec *pspec)
202 {
203         SoupCookieJarPrivate *priv =
204                 SOUP_COOKIE_JAR_GET_PRIVATE (object);
205
206         switch (prop_id) {
207         case PROP_READ_ONLY:
208                 priv->read_only = g_value_get_boolean (value);
209                 break;
210         case PROP_ACCEPT_POLICY:
211                 priv->accept_policy = g_value_get_enum (value);
212                 break;
213         default:
214                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215                 break;
216         }
217 }
218
219 static void
220 get_property (GObject *object, guint prop_id,
221               GValue *value, GParamSpec *pspec)
222 {
223         SoupCookieJarPrivate *priv =
224                 SOUP_COOKIE_JAR_GET_PRIVATE (object);
225
226         switch (prop_id) {
227         case PROP_READ_ONLY:
228                 g_value_set_boolean (value, priv->read_only);
229                 break;
230         case PROP_ACCEPT_POLICY:
231                 g_value_set_enum (value, priv->accept_policy);
232                 break;
233         default:
234                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235                 break;
236         }
237 }
238
239 /**
240  * soup_cookie_jar_new:
241  *
242  * Creates a new #SoupCookieJar. The base #SoupCookieJar class does
243  * not support persistent storage of cookies; use a subclass for that.
244  *
245  * Returns: a new #SoupCookieJar
246  *
247  * Since: 2.24
248  **/
249 SoupCookieJar *
250 soup_cookie_jar_new (void) 
251 {
252         return g_object_new (SOUP_TYPE_COOKIE_JAR, NULL);
253 }
254
255 void
256 soup_cookie_jar_save (SoupCookieJar *jar)
257 {
258         /* Does nothing, obsolete */
259 }
260
261 static void
262 soup_cookie_jar_changed (SoupCookieJar *jar,
263                          SoupCookie *old, SoupCookie *new)
264 {
265         SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
266
267         if (old && old != new)
268                 g_hash_table_remove (priv->serials, old);
269         if (new) {
270                 priv->serial++;
271                 g_hash_table_insert (priv->serials, new, GUINT_TO_POINTER (priv->serial));
272         }
273
274         if (priv->read_only || !priv->constructed)
275                 return;
276
277         g_signal_emit (jar, signals[CHANGED], 0, old, new);
278 }
279
280 static int
281 compare_cookies (gconstpointer a, gconstpointer b, gpointer jar)
282 {
283         SoupCookie *ca = (SoupCookie *)a;
284         SoupCookie *cb = (SoupCookie *)b;
285         SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
286         int alen, blen;
287         guint aserial, bserial;
288
289         /* "Cookies with longer path fields are listed before cookies
290          * with shorter path field."
291          */
292         alen = ca->path ? strlen (ca->path) : 0;
293         blen = cb->path ? strlen (cb->path) : 0;
294         if (alen != blen)
295                 return blen - alen;
296
297         /* "Among cookies that have equal length path fields, cookies
298          * with earlier creation dates are listed before cookies with
299          * later creation dates."
300          */
301         aserial = GPOINTER_TO_UINT (g_hash_table_lookup (priv->serials, ca));
302         bserial = GPOINTER_TO_UINT (g_hash_table_lookup (priv->serials, cb));
303         return aserial - bserial;
304 }
305
306 /**
307  * soup_cookie_jar_get_cookies:
308  * @jar: a #SoupCookieJar
309  * @uri: a #SoupURI
310  * @for_http: whether or not the return value is being passed directly
311  * to an HTTP operation
312  *
313  * Retrieves (in Cookie-header form) the list of cookies that would
314  * be sent with a request to @uri.
315  *
316  * If @for_http is %TRUE, the return value will include cookies marked
317  * "HttpOnly" (that is, cookies that the server wishes to keep hidden
318  * from client-side scripting operations such as the JavaScript
319  * document.cookies property). Since #SoupCookieJar sets the Cookie
320  * header itself when making the actual HTTP request, you should
321  * almost certainly be setting @for_http to %FALSE if you are calling
322  * this.
323  *
324  * Return value: the cookies, in string form, or %NULL if there are no
325  * cookies for @uri.
326  *
327  * Since: 2.24
328  **/
329 char *
330 soup_cookie_jar_get_cookies (SoupCookieJar *jar, SoupURI *uri,
331                              gboolean for_http)
332 {
333         SoupCookieJarPrivate *priv;
334         GSList *cookies, *domain_cookies;
335         char *domain, *cur, *next_domain, *result;
336         GSList *new_head, *cookies_to_remove = NULL, *p;
337
338         g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
339         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
340         g_return_val_if_fail (uri != NULL, NULL);
341
342         if (!uri->host)
343                 return NULL;
344
345         /* The logic here is a little weird, but the plan is that if
346          * uri->host is "www.foo.com", we will end up looking up
347          * cookies for ".www.foo.com", "www.foo.com", ".foo.com", and
348          * ".com", in that order. (Logic stolen from Mozilla.)
349          */
350         cookies = NULL;
351         domain = cur = g_strdup_printf (".%s", uri->host);
352         next_domain = domain + 1;
353         do {
354                 new_head = domain_cookies = g_hash_table_lookup (priv->domains, cur);
355                 while (domain_cookies) {
356                         GSList *next = domain_cookies->next;
357                         SoupCookie *cookie = domain_cookies->data;
358
359                         if (cookie->expires && soup_date_is_past (cookie->expires)) {
360                                 cookies_to_remove = g_slist_append (cookies_to_remove,
361                                                                     cookie);
362                                 new_head = g_slist_delete_link (new_head, domain_cookies);
363                                 g_hash_table_insert (priv->domains,
364                                                      g_strdup (cur),
365                                                      new_head);
366                         } else if (soup_cookie_applies_to_uri (cookie, uri) &&
367                                    (for_http || !cookie->http_only))
368                                 cookies = g_slist_append (cookies, cookie);
369
370                         domain_cookies = next;
371                 }
372                 cur = next_domain;
373                 if (cur)
374                         next_domain = strchr (cur + 1, '.');
375         } while (cur);
376         g_free (domain);
377
378         for (p = cookies_to_remove; p; p = p->next) {
379                 SoupCookie *cookie = p->data;
380
381                 soup_cookie_jar_changed (jar, cookie, NULL);
382                 soup_cookie_free (cookie);
383         }
384         g_slist_free (cookies_to_remove);
385
386         if (cookies) {
387                 cookies = g_slist_sort_with_data (cookies, compare_cookies, jar);
388                 result = soup_cookies_to_cookie_header (cookies);
389                 g_slist_free (cookies);
390
391                 if (!*result) {
392                         g_free (result);
393                         result = NULL;
394                 }
395                 return result;
396         } else
397                 return NULL;
398 }
399
400 /**
401  * soup_cookie_jar_add_cookie:
402  * @jar: a #SoupCookieJar
403  * @cookie: a #SoupCookie
404  *
405  * Adds @cookie to @jar, emitting the 'changed' signal if we are modifying
406  * an existing cookie or adding a valid new cookie ('valid' means
407  * that the cookie's expire date is not in the past).
408  *
409  * @cookie will be 'stolen' by the jar, so don't free it afterwards.
410  *
411  * Since: 2.26
412  **/
413 void
414 soup_cookie_jar_add_cookie (SoupCookieJar *jar, SoupCookie *cookie)
415 {
416         SoupCookieJarPrivate *priv;
417         GSList *old_cookies, *oc, *last = NULL;
418         SoupCookie *old_cookie;
419
420         g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
421         g_return_if_fail (cookie != NULL);
422
423         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
424         old_cookies = g_hash_table_lookup (priv->domains, cookie->domain);
425         for (oc = old_cookies; oc; oc = oc->next) {
426                 old_cookie = oc->data;
427                 if (!strcmp (cookie->name, old_cookie->name) &&
428                     !g_strcmp0 (cookie->path, old_cookie->path)) {
429                         if (cookie->expires && soup_date_is_past (cookie->expires)) {
430                                 /* The new cookie has an expired date,
431                                  * this is the way the the server has
432                                  * of telling us that we have to
433                                  * remove the cookie.
434                                  */
435                                 old_cookies = g_slist_delete_link (old_cookies, oc);
436                                 g_hash_table_insert (priv->domains,
437                                                      g_strdup (cookie->domain),
438                                                      old_cookies);
439                                 soup_cookie_jar_changed (jar, old_cookie, NULL);
440                                 soup_cookie_free (old_cookie);
441                                 soup_cookie_free (cookie);
442                         } else {
443                                 oc->data = cookie;
444                                 soup_cookie_jar_changed (jar, old_cookie, cookie);
445                                 soup_cookie_free (old_cookie);
446                         }
447
448                         return;
449                 }
450                 last = oc;
451         }
452
453         /* The new cookie is... a new cookie */
454         if (cookie->expires && soup_date_is_past (cookie->expires)) {
455                 soup_cookie_free (cookie);
456                 return;
457         }
458
459         if (last)
460                 last->next = g_slist_append (NULL, cookie);
461         else {
462                 old_cookies = g_slist_append (NULL, cookie);
463                 g_hash_table_insert (priv->domains, g_strdup (cookie->domain),
464                                      old_cookies);
465         }
466
467         soup_cookie_jar_changed (jar, NULL, cookie);
468 }
469
470 /**
471  * soup_cookie_jar_set_cookie:
472  * @jar: a #SoupCookieJar
473  * @uri: the URI setting the cookie
474  * @cookie: the stringified cookie to set
475  *
476  * Adds @cookie to @jar, exactly as though it had appeared in a
477  * Set-Cookie header returned from a request to @uri.
478  *
479  * Keep in mind that if the #SoupCookieJarAcceptPolicy
480  * %SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY is set you'll need to use
481  * soup_cookie_jar_set_cookie_with_first_party(), otherwise the jar
482  * will have no way of knowing if the cookie is being set by a third
483  * party or not.
484  *
485  * Since: 2.24
486  **/
487 void
488 soup_cookie_jar_set_cookie (SoupCookieJar *jar, SoupURI *uri,
489                             const char *cookie)
490 {
491         SoupCookie *soup_cookie;
492         SoupCookieJarPrivate *priv;
493
494         g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
495         g_return_if_fail (uri != NULL);
496         g_return_if_fail (cookie != NULL);
497
498         if (!uri->host)
499                 return;
500
501         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
502         if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
503                 return;
504
505         g_return_if_fail (priv->accept_policy != SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY);
506
507         soup_cookie = soup_cookie_parse (cookie, uri);
508         if (soup_cookie) {
509                 /* will steal or free soup_cookie */
510                 soup_cookie_jar_add_cookie (jar, soup_cookie);
511         }
512 }
513
514 /**
515  * soup_cookie_jar_set_cookie_with_first_party:
516  * @jar: a #SoupCookieJar
517  * @uri: the URI setting the cookie
518  * @first_party: the URI for the main document
519  * @cookie: the stringified cookie to set
520  *
521  * Adds @cookie to @jar, exactly as though it had appeared in a
522  * Set-Cookie header returned from a request to @uri. @first_party
523  * will be used to reject cookies coming from third party resources in
524  * case such a security policy is set in the @jar.
525  *
526  * Since: 2.30
527  **/
528 void
529 soup_cookie_jar_set_cookie_with_first_party (SoupCookieJar *jar,
530                                              SoupURI *uri,
531                                              SoupURI *first_party,
532                                              const char *cookie)
533 {
534         SoupCookie *soup_cookie;
535         SoupCookieJarPrivate *priv;
536
537         g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
538         g_return_if_fail (uri != NULL);
539         g_return_if_fail (first_party != NULL);
540         g_return_if_fail (cookie != NULL);
541
542         if (!uri->host)
543                 return;
544
545         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
546         if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
547                 return;
548
549         soup_cookie = soup_cookie_parse (cookie, uri);
550         if (soup_cookie) {
551                 if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS ||
552                     soup_cookie_domain_matches (soup_cookie, first_party->host)) {
553                         /* will steal or free soup_cookie */
554                         soup_cookie_jar_add_cookie (jar, soup_cookie);
555                 } else {
556                         soup_cookie_free (soup_cookie);
557                 }
558         }
559 }
560
561 static void
562 process_set_cookie_header (SoupMessage *msg, gpointer user_data)
563 {
564         SoupCookieJar *jar = user_data;
565         SoupCookieJarPrivate *priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
566         GSList *new_cookies, *nc;
567
568         if (priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NEVER)
569                 return;
570
571         new_cookies = soup_cookies_from_response (msg);
572         for (nc = new_cookies; nc; nc = nc->next) {
573                 SoupURI *first_party = soup_message_get_first_party (msg);
574                 
575                 if ((priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY &&
576                      first_party != NULL && first_party->host &&
577                      soup_cookie_domain_matches (nc->data, first_party->host)) ||
578                     priv->accept_policy == SOUP_COOKIE_JAR_ACCEPT_ALWAYS)
579                         soup_cookie_jar_add_cookie (jar, nc->data);
580                 else
581                         soup_cookie_free (nc->data);
582         }
583         g_slist_free (new_cookies);
584 }
585
586 static void
587 request_queued (SoupSessionFeature *feature, SoupSession *session,
588                 SoupMessage *msg)
589 {
590         soup_message_add_header_handler (msg, "got-headers",
591                                          "Set-Cookie",
592                                          G_CALLBACK (process_set_cookie_header),
593                                          feature);
594 }
595
596 static void
597 request_started (SoupSessionFeature *feature, SoupSession *session,
598                  SoupMessage *msg, SoupSocket *socket)
599 {
600         SoupCookieJar *jar = SOUP_COOKIE_JAR (feature);
601         char *cookies;
602
603         cookies = soup_cookie_jar_get_cookies (jar, soup_message_get_uri (msg), TRUE);
604         if (cookies) {
605                 soup_message_headers_replace (msg->request_headers,
606                                               "Cookie", cookies);
607                 g_free (cookies);
608         } else
609                 soup_message_headers_remove (msg->request_headers, "Cookie");
610 }
611
612 static void
613 request_unqueued (SoupSessionFeature *feature, SoupSession *session,
614                   SoupMessage *msg)
615 {
616         g_signal_handlers_disconnect_by_func (msg, process_set_cookie_header, feature);
617 }
618
619 /**
620  * soup_cookie_jar_all_cookies:
621  * @jar: a #SoupCookieJar
622  *
623  * Constructs a #GSList with every cookie inside the @jar.
624  * The cookies in the list are a copy of the original, so
625  * you have to free them when you are done with them.
626  *
627  * Return value: (transfer full) (element-type Soup.Cookie): a #GSList
628  * with all the cookies in the @jar.
629  *
630  * Since: 2.26
631  **/
632 GSList *
633 soup_cookie_jar_all_cookies (SoupCookieJar *jar)
634 {
635         SoupCookieJarPrivate *priv;
636         GHashTableIter iter;
637         GSList *l = NULL;
638         gpointer key, value;
639
640         g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), NULL);
641
642         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
643
644         g_hash_table_iter_init (&iter, priv->domains);
645
646         while (g_hash_table_iter_next (&iter, &key, &value)) {
647                 GSList *p, *cookies = value;
648                 for (p = cookies; p; p = p->next)
649                         l = g_slist_prepend (l, soup_cookie_copy (p->data));
650         }
651
652         return l;
653 }
654
655 /**
656  * soup_cookie_jar_delete_cookie:
657  * @jar: a #SoupCookieJar
658  * @cookie: a #SoupCookie
659  *
660  * Deletes @cookie from @jar, emitting the 'changed' signal.
661  *
662  * Since: 2.26
663  **/
664 void
665 soup_cookie_jar_delete_cookie (SoupCookieJar *jar,
666                                SoupCookie    *cookie)
667 {
668         SoupCookieJarPrivate *priv;
669         GSList *cookies, *p;
670         char *domain;
671
672         g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
673         g_return_if_fail (cookie != NULL);
674
675         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
676
677         domain = g_strdup (cookie->domain);
678
679         cookies = g_hash_table_lookup (priv->domains, domain);
680         if (cookies == NULL)
681                 return;
682
683         for (p = cookies; p; p = p->next ) {
684                 SoupCookie *c = (SoupCookie*)p->data;
685                 if (soup_cookie_equal (cookie, c)) {
686                         cookies = g_slist_delete_link (cookies, p);
687                         g_hash_table_insert (priv->domains,
688                                              domain,
689                                              cookies);
690                         soup_cookie_jar_changed (jar, c, NULL);
691                         soup_cookie_free (c);
692                         return;
693                 }
694         }
695 }
696
697 /**
698  * SoupCookieJarAcceptPolicy:
699  * @SOUP_COOKIE_JAR_ACCEPT_ALWAYS: accept all cookies unconditionally.
700  * @SOUP_COOKIE_JAR_ACCEPT_NEVER: reject all cookies unconditionally.
701  * @SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY: accept all cookies set by
702  * the main document loaded in the application using libsoup. An
703  * example of the most common case, web browsers, would be: If
704  * http://www.example.com is the page loaded, accept all cookies set
705  * by example.com, but if a resource from http://www.third-party.com
706  * is loaded from that page reject any cookie that it could try to
707  * set. For libsoup to be able to tell apart first party cookies from
708  * the rest, the application must call soup_message_set_first_party()
709  * on each outgoing #SoupMessage, setting the #SoupURI of the main
710  * document. If no first party is set in a message when this policy is
711  * in effect, cookies will be assumed to be third party by default.
712  *
713  * Since: 2.30
714  */
715
716 /**
717  * soup_cookie_jar_get_accept_policy:
718  * @jar: a #SoupCookieJar
719  * 
720  * Returns: the #SoupCookieJarAcceptPolicy set in the @jar
721  *
722  * Since: 2.30
723  **/
724 SoupCookieJarAcceptPolicy
725 soup_cookie_jar_get_accept_policy (SoupCookieJar *jar)
726 {
727         SoupCookieJarPrivate *priv;
728
729         g_return_val_if_fail (SOUP_IS_COOKIE_JAR (jar), SOUP_COOKIE_JAR_ACCEPT_ALWAYS);
730
731         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
732         return priv->accept_policy;
733 }
734
735 /**
736  * soup_cookie_jar_set_accept_policy:
737  * @jar: a #SoupCookieJar
738  * @policy: a #SoupCookieJarAcceptPolicy
739  * 
740  * Sets @policy as the cookie acceptance policy for @jar.
741  *
742  * Since: 2.30
743  **/
744 void
745 soup_cookie_jar_set_accept_policy (SoupCookieJar *jar,
746                                    SoupCookieJarAcceptPolicy policy)
747 {
748         SoupCookieJarPrivate *priv;
749
750         g_return_if_fail (SOUP_IS_COOKIE_JAR (jar));
751
752         priv = SOUP_COOKIE_JAR_GET_PRIVATE (jar);
753
754         if (priv->accept_policy != policy) {
755                 priv->accept_policy = policy;
756                 g_object_notify (G_OBJECT (jar), SOUP_COOKIE_JAR_ACCEPT_POLICY);
757         }
758 }