1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
5 * Copyright (C) 2007 Red Hat, Inc.
15 #include "soup-cookie.h"
20 * @short_description: HTTP Cookies
21 * @see_also: #SoupMessage, #SoupCookieJar
23 * #SoupCookie implements HTTP cookies, as described by <ulink
24 * url="http://tools.ietf.org/html/rfc6265.txt">RFC 6265</ulink>.
26 * To have a #SoupSession handle cookies for your appliction
27 * automatically, use a #SoupCookieJar.
32 * @name: the cookie name
33 * @value: the cookie value
34 * @domain: the "domain" attribute, or else the hostname that the
36 * @path: the "path" attribute, or %NULL
37 * @expires: the cookie expiration time, or %NULL for a session cookie
38 * @secure: %TRUE if the cookie should only be tranferred over SSL
39 * @http_only: %TRUE if the cookie should not be exposed to scripts
43 * @name and @value will be set for all cookies. If the cookie is
44 * generated from a string that appears to have no name, then @name
45 * will be the empty string.
47 * @domain and @path give the host or domain, and path within that
48 * host/domain, to restrict this cookie to. If @domain starts with
49 * ".", that indicates a domain (which matches the string after the
50 * ".", or any hostname that has @domain as a suffix). Otherwise, it
51 * is a hostname and must match exactly.
53 * @expires will be non-%NULL if the cookie uses either the original
54 * "expires" attribute, or the newer "max-age" attribute. If @expires
55 * is %NULL, it indicates that neither "expires" nor "max-age" was
56 * specified, and the cookie expires at the end of the session.
58 * If @http_only is set, the cookie should not be exposed to untrusted
59 * code (eg, javascript), so as to minimize the danger posed by
60 * cross-site scripting attacks.
65 G_DEFINE_BOXED_TYPE (SoupCookie, soup_cookie, soup_cookie_copy, soup_cookie_free)
69 * @cookie: a #SoupCookie
73 * Return value: a copy of @cookie
78 soup_cookie_copy (SoupCookie *cookie)
80 SoupCookie *copy = g_slice_new0 (SoupCookie);
82 copy->name = g_strdup (cookie->name);
83 copy->value = g_strdup (cookie->value);
84 copy->domain = g_strdup (cookie->domain);
85 copy->path = g_strdup (cookie->path);
87 copy->expires = soup_date_copy(cookie->expires);
88 copy->secure = cookie->secure;
89 copy->http_only = cookie->http_only;
95 * soup_cookie_domain_matches:
96 * @cookie: a #SoupCookie
99 * Checks if the @cookie's domain and @host match in the sense that
100 * @cookie should be sent when making a request to @host, or that
101 * @cookie should be accepted when receiving a response from @host.
103 * Return value: %TRUE if the domains match, %FALSE otherwise
108 soup_cookie_domain_matches (SoupCookie *cookie, const char *host)
114 g_return_val_if_fail (cookie != NULL, FALSE);
115 g_return_val_if_fail (host != NULL, FALSE);
117 domain = cookie->domain;
119 if (!g_ascii_strcasecmp (domain, host))
123 if (!g_ascii_strcasecmp (domain + 1, host))
125 dlen = strlen (domain);
126 while ((match = strstr (host, domain))) {
134 static inline const char *
135 skip_lws (const char *s)
137 while (g_ascii_isspace (*s))
142 static inline const char *
143 unskip_lws (const char *s, const char *start)
145 while (s > start && g_ascii_isspace (*(s - 1)))
150 #define is_attr_ender(ch) ((ch) < ' ' || (ch) == ';' || (ch) == ',' || (ch) == '=')
151 #define is_value_ender(ch) ((ch) < ' ' || (ch) == ';')
154 parse_value (const char **val_p, gboolean copy)
156 const char *start, *end, *p;
162 start = skip_lws (p);
163 for (p = start; !is_value_ender (*p); p++)
165 end = unskip_lws (p, start);
168 value = g_strndup (start, end - start);
177 parse_date (const char **val_p)
182 value = parse_value (val_p, TRUE);
183 date = soup_date_new_from_string (value);
189 parse_one_cookie (const char *header, SoupURI *origin)
191 const char *start, *end, *p;
195 g_return_val_if_fail (origin == NULL || origin->host, NULL);
197 cookie = g_slice_new0 (SoupCookie);
200 start = skip_lws (header);
201 for (p = start; !is_attr_ender (*p); p++)
204 end = unskip_lws (p, start);
205 cookie->name = g_strndup (start, end - start);
207 /* No NAME; Set cookie->name to "" and then rewind to
208 * re-parse the string as a VALUE.
210 cookie->name = g_strdup ("");
214 /* Parse the VALUE */
215 cookie->value = parse_value (&p, TRUE);
217 /* Parse attributes */
219 start = skip_lws (p + 1);
220 for (p = start; !is_attr_ender (*p); p++)
222 end = unskip_lws (p, start);
224 has_value = (*p == '=');
225 #define MATCH_NAME(name) ((end - start == strlen (name)) && !g_ascii_strncasecmp (start, name, end - start))
227 if (MATCH_NAME ("domain") && has_value) {
228 cookie->domain = parse_value (&p, TRUE);
229 if (!*cookie->domain) {
230 g_free (cookie->domain);
231 cookie->domain = NULL;
233 } else if (MATCH_NAME ("expires") && has_value) {
234 cookie->expires = parse_date (&p);
235 } else if (MATCH_NAME ("httponly")) {
236 cookie->http_only = TRUE;
238 parse_value (&p, FALSE);
239 } else if (MATCH_NAME ("max-age") && has_value) {
240 char *max_age_str = parse_value (&p, TRUE), *mae;
241 long max_age = strtol (max_age_str, &mae, 10);
245 soup_cookie_set_max_age (cookie, max_age);
247 g_free (max_age_str);
248 } else if (MATCH_NAME ("path") && has_value) {
249 cookie->path = parse_value (&p, TRUE);
250 if (*cookie->path != '/') {
251 g_free (cookie->path);
254 } else if (MATCH_NAME ("secure")) {
255 cookie->secure = TRUE;
257 parse_value (&p, FALSE);
259 /* Ignore unknown attributes, but we still have
260 * to skip over the value.
263 parse_value (&p, FALSE);
267 if (cookie->domain) {
268 /* Domain must have at least one '.' (not counting an
269 * initial one. (We check this now, rather than
270 * bailing out sooner, because we don't want to force
271 * any cookies after this one in the Set-Cookie header
274 if (!strchr (cookie->domain + 1, '.')) {
275 soup_cookie_free (cookie);
279 /* If the domain string isn't an IP addr, and doesn't
280 * start with a '.', prepend one.
282 if (!g_hostname_is_ip_address (cookie->domain) &&
283 cookie->domain[0] != '.') {
284 char *tmp = g_strdup_printf (".%s", cookie->domain);
285 g_free (cookie->domain);
286 cookie->domain = tmp;
291 /* Sanity-check domain */
292 if (cookie->domain) {
293 if (!soup_cookie_domain_matches (cookie, origin->host)) {
294 soup_cookie_free (cookie);
298 cookie->domain = g_strdup (origin->host);
300 /* The original cookie spec didn't say that pages
301 * could only set cookies for paths they were under.
302 * RFC 2109 adds that requirement, but some sites
303 * depend on the old behavior
304 * (https://bugzilla.mozilla.org/show_bug.cgi?id=156725#c20).
305 * So we don't check the path.
311 slash = strrchr (origin->path, '/');
312 if (!slash || slash == origin->path)
313 cookie->path = g_strdup ("/");
315 cookie->path = g_strndup (origin->path,
316 slash - origin->path);
325 cookie_new_internal (const char *name, const char *value,
326 const char *domain, const char *path,
331 cookie = g_slice_new0 (SoupCookie);
332 cookie->name = g_strdup (name);
333 cookie->value = g_strdup (value);
334 cookie->domain = g_strdup (domain);
335 cookie->path = g_strdup (path);
336 soup_cookie_set_max_age (cookie, max_age);
344 * @value: cookie value
345 * @domain: cookie domain or hostname
346 * @path: cookie path, or %NULL
347 * @max_age: max age of the cookie, or -1 for a session cookie
349 * Creates a new #SoupCookie with the given attributes. (Use
350 * soup_cookie_set_secure() and soup_cookie_set_http_only() if you
351 * need to set those attributes on the returned cookie.)
353 * If @domain starts with ".", that indicates a domain (which matches
354 * the string after the ".", or any hostname that has @domain as a
355 * suffix). Otherwise, it is a hostname and must match exactly.
357 * @max_age is used to set the "expires" attribute on the cookie; pass
358 * -1 to not include the attribute (indicating that the cookie expires
359 * with the current session), 0 for an already-expired cookie, or a
360 * lifetime in seconds. You can use the constants
361 * %SOUP_COOKIE_MAX_AGE_ONE_HOUR, %SOUP_COOKIE_MAX_AGE_ONE_DAY,
362 * %SOUP_COOKIE_MAX_AGE_ONE_WEEK and %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or
363 * multiples thereof) to calculate this value. (If you really care
364 * about setting the exact time that the cookie will expire, use
365 * soup_cookie_set_expires().)
367 * Return value: a new #SoupCookie.
372 soup_cookie_new (const char *name, const char *value,
373 const char *domain, const char *path,
376 g_return_val_if_fail (name != NULL, NULL);
377 g_return_val_if_fail (value != NULL, NULL);
379 /* We ought to return if domain is NULL too, but this used to
380 * do be incorrectly documented as legal, and it wouldn't
381 * break anything as long as you called
382 * soup_cookie_set_domain() immediately after. So we warn but
383 * don't return, to discourage that behavior but not actually
384 * break anyone doing it.
386 g_warn_if_fail (domain != NULL);
388 return cookie_new_internal (name, value, domain, path, max_age);
393 * @header: a cookie string (eg, the value of a Set-Cookie header)
394 * @origin: origin of the cookie, or %NULL
396 * Parses @header and returns a #SoupCookie. (If @header contains
397 * multiple cookies, only the first one will be parsed.)
399 * If @header does not have "path" or "domain" attributes, they will
400 * be defaulted from @origin. If @origin is %NULL, path will default
401 * to "/", but domain will be left as %NULL. Note that this is not a
402 * valid state for a #SoupCookie, and you will need to fill in some
403 * appropriate string for the domain if you want to actually make use
406 * Return value: a new #SoupCookie, or %NULL if it could not be
407 * parsed, or contained an illegal "domain" attribute for a cookie
408 * originating from @origin.
413 soup_cookie_parse (const char *cookie, SoupURI *origin)
415 return parse_one_cookie (cookie, origin);
419 * soup_cookie_get_name:
420 * @cookie: a #SoupCookie
422 * Gets @cookie's name
424 * Return value: @cookie's name
429 soup_cookie_get_name (SoupCookie *cookie)
435 * soup_cookie_set_name:
436 * @cookie: a #SoupCookie
437 * @name: the new name
439 * Sets @cookie's name to @name
444 soup_cookie_set_name (SoupCookie *cookie, const char *name)
446 g_free (cookie->name);
447 cookie->name = g_strdup (name);
451 * soup_cookie_get_value:
452 * @cookie: a #SoupCookie
454 * Gets @cookie's value
456 * Return value: @cookie's value
461 soup_cookie_get_value (SoupCookie *cookie)
463 return cookie->value;
467 * soup_cookie_set_value:
468 * @cookie: a #SoupCookie
469 * @value: the new value
471 * Sets @cookie's value to @value
476 soup_cookie_set_value (SoupCookie *cookie, const char *value)
478 g_free (cookie->value);
479 cookie->value = g_strdup (value);
483 * soup_cookie_get_domain:
484 * @cookie: a #SoupCookie
486 * Gets @cookie's domain
488 * Return value: @cookie's domain
493 soup_cookie_get_domain (SoupCookie *cookie)
495 return cookie->domain;
499 * soup_cookie_set_domain:
500 * @cookie: a #SoupCookie
501 * @domain: the new domain
503 * Sets @cookie's domain to @domain
508 soup_cookie_set_domain (SoupCookie *cookie, const char *domain)
510 g_free (cookie->domain);
511 cookie->domain = g_strdup (domain);
515 * soup_cookie_get_path:
516 * @cookie: a #SoupCookie
518 * Gets @cookie's path
520 * Return value: @cookie's path
525 soup_cookie_get_path (SoupCookie *cookie)
531 * soup_cookie_set_path:
532 * @cookie: a #SoupCookie
533 * @path: the new path
535 * Sets @cookie's path to @path
540 soup_cookie_set_path (SoupCookie *cookie, const char *path)
542 g_free (cookie->path);
543 cookie->path = g_strdup (path);
547 * soup_cookie_set_max_age:
548 * @cookie: a #SoupCookie
549 * @max_age: the new max age
551 * Sets @cookie's max age to @max_age. If @max_age is -1, the cookie
552 * is a session cookie, and will expire at the end of the client's
553 * session. Otherwise, it is the number of seconds until the cookie
554 * expires. You can use the constants %SOUP_COOKIE_MAX_AGE_ONE_HOUR,
555 * %SOUP_COOKIE_MAX_AGE_ONE_DAY, %SOUP_COOKIE_MAX_AGE_ONE_WEEK and
556 * %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or multiples thereof) to calculate
557 * this value. (A value of 0 indicates that the cookie should be
558 * considered already-expired.)
560 * (This sets the same property as soup_cookie_set_expires().)
565 soup_cookie_set_max_age (SoupCookie *cookie, int max_age)
568 soup_date_free (cookie->expires);
571 cookie->expires = NULL;
572 else if (max_age == 0) {
573 /* Use a date way in the past, to protect against
576 cookie->expires = soup_date_new (1970, 1, 1, 0, 0, 0);
578 cookie->expires = soup_date_new_from_now (max_age);
582 * SOUP_COOKIE_MAX_AGE_ONE_HOUR:
584 * A constant corresponding to 1 hour, for use with soup_cookie_new()
585 * and soup_cookie_set_max_age().
590 * SOUP_COOKIE_MAX_AGE_ONE_DAY:
592 * A constant corresponding to 1 day, for use with soup_cookie_new()
593 * and soup_cookie_set_max_age().
598 * SOUP_COOKIE_MAX_AGE_ONE_WEEK:
600 * A constant corresponding to 1 week, for use with soup_cookie_new()
601 * and soup_cookie_set_max_age().
606 * SOUP_COOKIE_MAX_AGE_ONE_YEAR:
608 * A constant corresponding to 1 year, for use with soup_cookie_new()
609 * and soup_cookie_set_max_age().
615 * soup_cookie_get_expires:
616 * @cookie: a #SoupCookie
618 * Gets @cookie's expiration time
620 * Return value: (transfer none): @cookie's expiration time, which is
621 * owned by @cookie and should not be modified or freed.
626 soup_cookie_get_expires (SoupCookie *cookie)
628 return cookie->expires;
632 * soup_cookie_set_expires:
633 * @cookie: a #SoupCookie
634 * @expires: the new expiration time, or %NULL
636 * Sets @cookie's expiration time to @expires. If @expires is %NULL,
637 * @cookie will be a session cookie and will expire at the end of the
640 * (This sets the same property as soup_cookie_set_max_age().)
645 soup_cookie_set_expires (SoupCookie *cookie, SoupDate *expires)
648 soup_date_free (cookie->expires);
651 cookie->expires = soup_date_copy (expires);
653 cookie->expires = NULL;
657 * soup_cookie_get_secure:
658 * @cookie: a #SoupCookie
660 * Gets @cookie's secure attribute
662 * Return value: @cookie's secure attribute
667 soup_cookie_get_secure (SoupCookie *cookie)
669 return cookie->secure;
673 * soup_cookie_set_secure:
674 * @cookie: a #SoupCookie
675 * @secure: the new value for the secure attribute
677 * Sets @cookie's secure attribute to @secure. If %TRUE, @cookie will
678 * only be transmitted from the client to the server over secure
679 * (https) connections.
684 soup_cookie_set_secure (SoupCookie *cookie, gboolean secure)
686 cookie->secure = secure;
690 * soup_cookie_get_http_only:
691 * @cookie: a #SoupCookie
693 * Gets @cookie's HttpOnly attribute
695 * Return value: @cookie's HttpOnly attribute
700 soup_cookie_get_http_only (SoupCookie *cookie)
702 return cookie->http_only;
706 * soup_cookie_set_http_only:
707 * @cookie: a #SoupCookie
708 * @http_only: the new value for the HttpOnly attribute
710 * Sets @cookie's HttpOnly attribute to @http_only. If %TRUE, @cookie
711 * will be marked as "http only", meaning it should not be exposed to
712 * web page scripts or other untrusted code.
717 soup_cookie_set_http_only (SoupCookie *cookie, gboolean http_only)
719 cookie->http_only = http_only;
723 serialize_cookie (SoupCookie *cookie, GString *header, gboolean set_cookie)
725 if (!*cookie->name && !*cookie->value)
730 g_string_append (header, ", ");
732 g_string_append (header, "; ");
735 if (set_cookie || *cookie->name) {
736 g_string_append (header, cookie->name);
737 g_string_append (header, "=");
739 g_string_append (header, cookie->value);
743 if (cookie->expires) {
746 g_string_append (header, "; expires=");
747 timestamp = soup_date_to_string (cookie->expires,
749 g_string_append (header, timestamp);
753 g_string_append (header, "; path=");
754 g_string_append (header, cookie->path);
756 if (cookie->domain) {
757 g_string_append (header, "; domain=");
758 g_string_append (header, cookie->domain);
761 g_string_append (header, "; secure");
762 if (cookie->http_only)
763 g_string_append (header, "; HttpOnly");
767 * soup_cookie_to_set_cookie_header:
768 * @cookie: a #SoupCookie
770 * Serializes @cookie in the format used by the Set-Cookie header
771 * (ie, for sending a cookie from a #SoupServer to a client).
773 * Return value: the header
778 soup_cookie_to_set_cookie_header (SoupCookie *cookie)
780 GString *header = g_string_new (NULL);
782 serialize_cookie (cookie, header, TRUE);
783 return g_string_free (header, FALSE);
787 * soup_cookie_to_cookie_header:
788 * @cookie: a #SoupCookie
790 * Serializes @cookie in the format used by the Cookie header (ie, for
791 * returning a cookie from a #SoupSession to a server).
793 * Return value: the header
798 soup_cookie_to_cookie_header (SoupCookie *cookie)
800 GString *header = g_string_new (NULL);
802 serialize_cookie (cookie, header, FALSE);
803 return g_string_free (header, FALSE);
808 * @cookie: a #SoupCookie
815 soup_cookie_free (SoupCookie *cookie)
817 g_return_if_fail (cookie != NULL);
819 g_free (cookie->name);
820 g_free (cookie->value);
821 g_free (cookie->domain);
822 g_free (cookie->path);
823 g_clear_pointer (&cookie->expires, soup_date_free);
825 g_slice_free (SoupCookie, cookie);
829 * soup_cookies_from_response:
830 * @msg: a #SoupMessage containing a "Set-Cookie" response header
832 * Parses @msg's Set-Cookie response headers and returns a #GSList of
833 * #SoupCookie<!-- -->s. Cookies that do not specify "path" or
834 * "domain" attributes will have their values defaulted from @msg.
836 * Return value: (element-type SoupCookie) (transfer full): a #GSList
837 * of #SoupCookie<!-- -->s, which can be freed with
838 * soup_cookies_free().
843 soup_cookies_from_response (SoupMessage *msg)
846 const char *name, *value;
848 GSList *cookies = NULL;
849 SoupMessageHeadersIter iter;
851 origin = soup_message_get_uri (msg);
853 /* We have to use soup_message_headers_iter rather than
854 * soup_message_headers_get_list() since Set-Cookie isn't
855 * properly mergeable/unmergeable.
857 soup_message_headers_iter_init (&iter, msg->response_headers);
858 while (soup_message_headers_iter_next (&iter, &name, &value)) {
859 if (g_ascii_strcasecmp (name, "Set-Cookie") != 0)
862 cookie = parse_one_cookie (value, origin);
864 cookies = g_slist_prepend (cookies, cookie);
866 return g_slist_reverse (cookies);
870 * soup_cookies_from_request:
871 * @msg: a #SoupMessage containing a "Cookie" request header
873 * Parses @msg's Cookie request header and returns a #GSList of
874 * #SoupCookie<!-- -->s. As the "Cookie" header, unlike "Set-Cookie",
875 * only contains cookie names and values, none of the other
876 * #SoupCookie fields will be filled in. (Thus, you can't generally
877 * pass a cookie returned from this method directly to
878 * soup_cookies_to_response().)
880 * Return value: (element-type SoupCookie) (transfer full): a #GSList
881 * of #SoupCookie<!-- -->s, which can be freed with
882 * soup_cookies_free().
887 soup_cookies_from_request (SoupMessage *msg)
890 GSList *cookies = NULL;
893 gpointer name, value;
896 header = soup_message_headers_get_one (msg->request_headers, "Cookie");
900 params = soup_header_parse_semi_param_list (header);
901 g_hash_table_iter_init (&iter, params);
902 while (g_hash_table_iter_next (&iter, &name, &value)) {
904 cookie = cookie_new_internal (name, value,
906 cookies = g_slist_prepend (cookies, cookie);
909 soup_header_free_param_list (params);
911 return g_slist_reverse (cookies);
915 * soup_cookies_to_response:
916 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
917 * @msg: a #SoupMessage
919 * Appends a "Set-Cookie" response header to @msg for each cookie in
920 * @cookies. (This is in addition to any other "Set-Cookie" headers
921 * @msg may already have.)
926 soup_cookies_to_response (GSList *cookies, SoupMessage *msg)
930 header = g_string_new (NULL);
932 serialize_cookie (cookies->data, header, TRUE);
933 soup_message_headers_append (msg->response_headers,
934 "Set-Cookie", header->str);
935 g_string_truncate (header, 0);
936 cookies = cookies->next;
938 g_string_free (header, TRUE);
942 * soup_cookies_to_request:
943 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
944 * @msg: a #SoupMessage
946 * Adds the name and value of each cookie in @cookies to @msg's
947 * "Cookie" request. (If @msg already has a "Cookie" request header,
948 * these cookies will be appended to the cookies already present. Be
949 * careful that you do not append the same cookies twice, eg, when
950 * requeuing a message.)
955 soup_cookies_to_request (GSList *cookies, SoupMessage *msg)
959 header = g_string_new (soup_message_headers_get_one (msg->request_headers,
962 serialize_cookie (cookies->data, header, FALSE);
963 cookies = cookies->next;
965 soup_message_headers_replace (msg->request_headers,
966 "Cookie", header->str);
967 g_string_free (header, TRUE);
971 * soup_cookies_free: (skip)
972 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
979 soup_cookies_free (GSList *cookies)
981 g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
985 * soup_cookies_to_cookie_header:
986 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
988 * Serializes a #GSList of #SoupCookie into a string suitable for
989 * setting as the value of the "Cookie" header.
991 * Return value: the serialization of @cookies
996 soup_cookies_to_cookie_header (GSList *cookies)
1000 g_return_val_if_fail (cookies != NULL, NULL);
1002 str = g_string_new (NULL);
1004 serialize_cookie (cookies->data, str, FALSE);
1005 cookies = cookies->next;
1008 return g_string_free (str, FALSE);
1012 * soup_cookie_applies_to_uri:
1013 * @cookie: a #SoupCookie
1016 * Tests if @cookie should be sent to @uri.
1018 * (At the moment, this does not check that @cookie's domain matches
1019 * @uri, because it assumes that the caller has already done that.
1020 * But don't rely on that; it may change in the future.)
1022 * Return value: %TRUE if @cookie should be sent to @uri, %FALSE if
1028 soup_cookie_applies_to_uri (SoupCookie *cookie, SoupURI *uri)
1032 if (cookie->secure && uri->scheme != SOUP_URI_SCHEME_HTTPS)
1035 if (cookie->expires && soup_date_is_past (cookie->expires))
1038 /* uri->path is required to be non-NULL */
1039 g_return_val_if_fail (uri->path != NULL, FALSE);
1041 plen = strlen (cookie->path);
1044 if (strncmp (cookie->path, uri->path, plen) != 0)
1046 if (cookie->path[plen - 1] != '/' &&
1047 uri->path[plen] && uri->path[plen] != '/')
1054 * soup_cookie_equal:
1055 * @cookie1: a #SoupCookie
1056 * @cookie2: a #SoupCookie
1058 * Tests if @cookie1 and @cookie2 are equal.
1060 * Note that currently, this does not check that the cookie domains
1061 * match. This may change in the future.
1063 * Return value: whether the cookies are equal.
1068 soup_cookie_equal (SoupCookie *cookie1, SoupCookie *cookie2)
1070 g_return_val_if_fail (cookie1, FALSE);
1071 g_return_val_if_fail (cookie2, FALSE);
1073 return (!strcmp (cookie1->name, cookie2->name) &&
1074 !strcmp (cookie1->value, cookie2->value) &&
1075 !strcmp (cookie1->path, cookie2->path));