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"
16 #include "soup-date.h"
17 #include "soup-headers.h"
18 #include "soup-message.h"
19 #include "soup-message-headers.h"
24 * @short_description: HTTP Cookies
25 * @see_also: #SoupMessage
27 * #SoupCookie implements HTTP cookies, primarily as described by
29 * url="http://wp.netscape.com/newsref/std/cookie_spec.html">the
30 * original Netscape cookie specification</ulink>, but with slight
31 * modifications based on <ulink
32 * url="http://www.ietf.org/rfc/rfc2109.txt">RFC 2109</ulink>, <ulink
33 * url="http://msdn2.microsoft.com/en-us/library/ms533046.aspx">Microsoft's
34 * HttpOnly extension attribute</ulink>, and observed real-world usage
35 * (and, in particular, based on what Firefox does).
37 * To have a #SoupSession handle cookies for your appliction
38 * automatically, use a #SoupCookieJar.
43 * @name: the cookie name
44 * @value: the cookie value
45 * @domain: the "domain" attribute, or else the hostname that the
47 * @path: the "path" attribute, or %NULL
48 * @expires: the cookie expiration time, or %NULL for a session cookie
49 * @secure: %TRUE if the cookie should only be tranferred over SSL
50 * @http_only: %TRUE if the cookie should not be exposed to scripts
54 * @name and @value will be set for all cookies. If the cookie is
55 * generated from a string that appears to have no name, then @name
56 * will be the empty string.
58 * @domain and @path give the host or domain, and path within that
59 * host/domain, to restrict this cookie to. If @domain starts with
60 * ".", that indicates a domain (which matches the string after the
61 * ".", or any hostname that has @domain as a suffix). Otherwise, it
62 * is a hostname and must match exactly.
64 * @expires will be non-%NULL if the cookie uses either the original
65 * "expires" attribute, or the "max-age" attribute specified in RFC
66 * 2109. If @expires is %NULL, it indicates that neither "expires" nor
67 * "max-age" was specified, and the cookie expires at the end of the
70 * If @http_only is set, the cookie should not be exposed to untrusted
71 * code (eg, javascript), so as to minimize the danger posed by
72 * cross-site scripting attacks.
78 soup_cookie_get_type (void)
80 static volatile gsize type_volatile = 0;
82 if (g_once_init_enter (&type_volatile)) {
83 GType type = g_boxed_type_register_static (
84 g_intern_static_string ("SoupCookie"),
85 (GBoxedCopyFunc) soup_cookie_copy,
86 (GBoxedFreeFunc) soup_cookie_free);
87 g_once_init_leave (&type_volatile, type);
94 * @cookie: a #SoupCookie
98 * Return value: a copy of @cookie
103 soup_cookie_copy (SoupCookie *cookie)
105 SoupCookie *copy = g_slice_new0 (SoupCookie);
107 copy->name = g_strdup (cookie->name);
108 copy->value = g_strdup (cookie->value);
109 copy->domain = g_strdup (cookie->domain);
110 copy->path = g_strdup (cookie->path);
112 copy->expires = soup_date_copy(cookie->expires);
113 copy->secure = cookie->secure;
114 copy->http_only = cookie->http_only;
120 * soup_cookie_domain_matches:
121 * @cookie: a #SoupCookie
124 * Checks if the @cookie's domain and @host match in the sense that
125 * @cookie should be sent when making a request to @host, or that
126 * @cookie should be accepted when receiving a response from @host.
128 * Return value: %TRUE if the domains match, %FALSE otherwise
133 soup_cookie_domain_matches (SoupCookie *cookie, const char *host)
139 g_return_val_if_fail (cookie != NULL, FALSE);
140 g_return_val_if_fail (host != NULL, FALSE);
142 domain = cookie->domain;
144 if (!g_ascii_strcasecmp (domain, host))
148 if (!g_ascii_strcasecmp (domain + 1, host))
150 dlen = strlen (domain);
151 while ((match = strstr (host, domain))) {
159 static inline const char *
160 skip_lws (const char *s)
162 while (g_ascii_isspace (*s))
167 static inline const char *
168 unskip_lws (const char *s, const char *start)
170 while (s > start && g_ascii_isspace (*(s - 1)))
175 #define is_attr_ender(ch) ((ch) < ' ' || (ch) == ';' || (ch) == ',' || (ch) == '=')
176 #define is_value_ender(ch) ((ch) < ' ' || (ch) == ';')
179 parse_value (const char **val_p)
181 const char *start, *end, *p;
187 start = skip_lws (p);
188 for (p = start; !is_value_ender (*p); p++)
190 end = unskip_lws (p, start);
191 value = g_strndup (start, end - start);
198 parse_date (const char **val_p)
203 value = parse_value (val_p);
204 date = soup_date_new_from_string (value);
210 parse_one_cookie (const char *header, SoupURI *origin)
212 const char *start, *end, *p;
216 g_return_val_if_fail (origin == NULL || origin->host, NULL);
218 cookie = g_slice_new0 (SoupCookie);
221 start = skip_lws (header);
222 for (p = start; !is_attr_ender (*p); p++)
225 end = unskip_lws (p, start);
226 cookie->name = g_strndup (start, end - start);
228 /* No NAME; Set cookie->name to "" and then rewind to
229 * re-parse the string as a VALUE.
231 cookie->name = g_strdup ("");
235 /* Parse the VALUE */
236 cookie->value = parse_value (&p);
238 /* Parse attributes */
240 start = skip_lws (p + 1);
241 for (p = start; !is_attr_ender (*p); p++)
243 end = unskip_lws (p, start);
245 has_value = (*p == '=');
246 #define MATCH_NAME(name) ((end - start == strlen (name)) && !g_ascii_strncasecmp (start, name, end - start))
248 if (MATCH_NAME ("domain") && has_value) {
249 cookie->domain = parse_value (&p);
250 if (!*cookie->domain) {
251 g_free (cookie->domain);
252 cookie->domain = NULL;
254 } else if (MATCH_NAME ("expires") && has_value) {
255 cookie->expires = parse_date (&p);
256 } else if (MATCH_NAME ("httponly")) {
257 cookie->http_only = TRUE;
258 } else if (MATCH_NAME ("max-age") && has_value) {
259 char *max_age_str = parse_value (&p), *mae;
260 long max_age = strtol (max_age_str, &mae, 10);
264 soup_cookie_set_max_age (cookie, max_age);
266 g_free (max_age_str);
267 } else if (MATCH_NAME ("path") && has_value) {
268 cookie->path = parse_value (&p);
269 if (*cookie->path != '/') {
270 g_free (cookie->path);
273 } else if (MATCH_NAME ("secure")) {
274 cookie->secure = TRUE;
276 /* Ignore unknown attributes, but we still have
277 * to skip over the value.
280 g_free (parse_value (&p));
284 if (cookie->domain) {
285 /* Domain must have at least one '.' (not counting an
286 * initial one. (We check this now, rather than
287 * bailing out sooner, because we don't want to force
288 * any cookies after this one in the Set-Cookie header
291 if (!strchr (cookie->domain + 1, '.')) {
292 soup_cookie_free (cookie);
296 /* If the domain string isn't an IP addr, and doesn't
297 * start with a '.', prepend one.
299 if (!g_hostname_is_ip_address (cookie->domain) &&
300 cookie->domain[0] != '.') {
301 char *tmp = g_strdup_printf (".%s", cookie->domain);
302 g_free (cookie->domain);
303 cookie->domain = tmp;
308 /* Sanity-check domain */
309 if (cookie->domain) {
310 if (!soup_cookie_domain_matches (cookie, origin->host)) {
311 soup_cookie_free (cookie);
315 cookie->domain = g_strdup (origin->host);
317 /* The original cookie spec didn't say that pages
318 * could only set cookies for paths they were under.
319 * RFC 2109 adds that requirement, but some sites
320 * depend on the old behavior
321 * (https://bugzilla.mozilla.org/show_bug.cgi?id=156725#c20).
322 * So we don't check the path.
328 slash = strrchr (origin->path, '/');
329 if (!slash || slash == origin->path)
330 cookie->path = g_strdup ("/");
332 cookie->path = g_strndup (origin->path,
333 slash - origin->path);
342 cookie_new_internal (const char *name, const char *value,
343 const char *domain, const char *path,
348 cookie = g_slice_new0 (SoupCookie);
349 cookie->name = g_strdup (name);
350 cookie->value = g_strdup (value);
351 cookie->domain = g_strdup (domain);
352 cookie->path = g_strdup (path);
353 soup_cookie_set_max_age (cookie, max_age);
361 * @value: cookie value
362 * @domain: cookie domain or hostname
363 * @path: cookie path, or %NULL
364 * @max_age: max age of the cookie, or -1 for a session cookie
366 * Creates a new #SoupCookie with the given attributes. (Use
367 * soup_cookie_set_secure() and soup_cookie_set_http_only() if you
368 * need to set those attributes on the returned cookie.)
370 * @max_age is used to set the "expires" attribute on the cookie; pass
371 * -1 to not include the attribute (indicating that the cookie expires
372 * with the current session), 0 for an already-expired cookie, or a
373 * lifetime in seconds. You can use the constants
374 * %SOUP_COOKIE_MAX_AGE_ONE_HOUR, %SOUP_COOKIE_MAX_AGE_ONE_DAY,
375 * %SOUP_COOKIE_MAX_AGE_ONE_WEEK and %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or
376 * multiples thereof) to calculate this value. (If you really care
377 * about setting the exact time that the cookie will expire, use
378 * soup_cookie_set_expires().)
380 * Return value: a new #SoupCookie.
385 soup_cookie_new (const char *name, const char *value,
386 const char *domain, const char *path,
389 g_return_val_if_fail (name != NULL, NULL);
390 g_return_val_if_fail (value != NULL, NULL);
392 /* We ought to return if domain is NULL too, but this used to
393 * do be incorrectly documented as legal, and it wouldn't
394 * break anything as long as you called
395 * soup_cookie_set_domain() immediately after. So we warn but
396 * don't return, to discourage that behavior but not actually
397 * break anyone doing it.
399 g_warn_if_fail (domain != NULL);
401 return cookie_new_internal (name, value, domain, path, max_age);
406 * @header: a cookie string (eg, the value of a Set-Cookie header)
407 * @origin: origin of the cookie, or %NULL
409 * Parses @header and returns a #SoupCookie. (If @header contains
410 * multiple cookies, only the first one will be parsed.)
412 * If @header does not have "path" or "domain" attributes, they will
413 * be defaulted from @origin. If @origin is %NULL, path will default
414 * to "/", but domain will be left as %NULL. Note that this is not a
415 * valid state for a #SoupCookie, and you will need to fill in some
416 * appropriate string for the domain if you want to actually make use
419 * Return value: a new #SoupCookie, or %NULL if it could not be
420 * parsed, or contained an illegal "domain" attribute for a cookie
421 * originating from @origin.
426 soup_cookie_parse (const char *cookie, SoupURI *origin)
428 return parse_one_cookie (cookie, origin);
432 * soup_cookie_get_name:
433 * @cookie: a #SoupCookie
435 * Gets @cookie's name
437 * Return value: @cookie's name
442 soup_cookie_get_name (SoupCookie *cookie)
448 * soup_cookie_set_name:
449 * @cookie: a #SoupCookie
450 * @name: the new name
452 * Sets @cookie's name to @name
457 soup_cookie_set_name (SoupCookie *cookie, const char *name)
459 g_free (cookie->name);
460 cookie->name = g_strdup (name);
464 * soup_cookie_get_value:
465 * @cookie: a #SoupCookie
467 * Gets @cookie's value
469 * Return value: @cookie's value
474 soup_cookie_get_value (SoupCookie *cookie)
476 return cookie->value;
480 * soup_cookie_set_value:
481 * @cookie: a #SoupCookie
482 * @value: the new value
484 * Sets @cookie's value to @value
489 soup_cookie_set_value (SoupCookie *cookie, const char *value)
491 g_free (cookie->value);
492 cookie->value = g_strdup (value);
496 * soup_cookie_get_domain:
497 * @cookie: a #SoupCookie
499 * Gets @cookie's domain
501 * Return value: @cookie's domain
506 soup_cookie_get_domain (SoupCookie *cookie)
508 return cookie->domain;
512 * soup_cookie_set_domain:
513 * @cookie: a #SoupCookie
514 * @domain: the new domain
516 * Sets @cookie's domain to @domain
521 soup_cookie_set_domain (SoupCookie *cookie, const char *domain)
523 g_free (cookie->domain);
524 cookie->domain = g_strdup (domain);
528 * soup_cookie_get_path:
529 * @cookie: a #SoupCookie
531 * Gets @cookie's path
533 * Return value: @cookie's path
538 soup_cookie_get_path (SoupCookie *cookie)
544 * soup_cookie_set_path:
545 * @cookie: a #SoupCookie
546 * @path: the new path
548 * Sets @cookie's path to @path
553 soup_cookie_set_path (SoupCookie *cookie, const char *path)
555 g_free (cookie->path);
556 cookie->path = g_strdup (path);
560 * soup_cookie_set_max_age:
561 * @cookie: a #SoupCookie
562 * @max_age: the new max age
564 * Sets @cookie's max age to @max_age. If @max_age is -1, the cookie
565 * is a session cookie, and will expire at the end of the client's
566 * session. Otherwise, it is the number of seconds until the cookie
567 * expires. You can use the constants %SOUP_COOKIE_MAX_AGE_ONE_HOUR,
568 * %SOUP_COOKIE_MAX_AGE_ONE_DAY, %SOUP_COOKIE_MAX_AGE_ONE_WEEK and
569 * %SOUP_COOKIE_MAX_AGE_ONE_YEAR (or multiples thereof) to calculate
570 * this value. (A value of 0 indicates that the cookie should be
571 * considered already-expired.)
573 * (This sets the same property as soup_cookie_set_expires().)
578 soup_cookie_set_max_age (SoupCookie *cookie, int max_age)
581 soup_date_free (cookie->expires);
584 cookie->expires = NULL;
585 else if (max_age == 0) {
586 /* Use a date way in the past, to protect against
589 cookie->expires = soup_date_new (1970, 1, 1, 0, 0, 0);
591 cookie->expires = soup_date_new_from_now (max_age);
595 * SOUP_COOKIE_MAX_AGE_ONE_HOUR:
597 * A constant corresponding to 1 hour, for use with soup_cookie_new()
598 * and soup_cookie_set_max_age().
603 * SOUP_COOKIE_MAX_AGE_ONE_DAY:
605 * A constant corresponding to 1 day, for use with soup_cookie_new()
606 * and soup_cookie_set_max_age().
611 * SOUP_COOKIE_MAX_AGE_ONE_WEEK:
613 * A constant corresponding to 1 week, for use with soup_cookie_new()
614 * and soup_cookie_set_max_age().
619 * SOUP_COOKIE_MAX_AGE_ONE_YEAR:
621 * A constant corresponding to 1 year, for use with soup_cookie_new()
622 * and soup_cookie_set_max_age().
628 * soup_cookie_get_expires:
629 * @cookie: a #SoupCookie
631 * Gets @cookie's expiration time
633 * Return value: (transfer none): @cookie's expiration time, which is
634 * owned by @cookie and should not be modified or freed.
639 soup_cookie_get_expires (SoupCookie *cookie)
641 return cookie->expires;
645 * soup_cookie_set_expires:
646 * @cookie: a #SoupCookie
647 * @expires: the new expiration time, or %NULL
649 * Sets @cookie's expiration time to @expires. If @expires is %NULL,
650 * @cookie will be a session cookie and will expire at the end of the
653 * (This sets the same property as soup_cookie_set_max_age().)
658 soup_cookie_set_expires (SoupCookie *cookie, SoupDate *expires)
661 soup_date_free (cookie->expires);
664 cookie->expires = soup_date_copy (expires);
666 cookie->expires = NULL;
670 * soup_cookie_get_secure:
671 * @cookie: a #SoupCookie
673 * Gets @cookie's secure attribute
675 * Return value: @cookie's secure attribute
680 soup_cookie_get_secure (SoupCookie *cookie)
682 return cookie->secure;
686 * soup_cookie_set_secure:
687 * @cookie: a #SoupCookie
688 * @secure: the new value for the secure attribute
690 * Sets @cookie's secure attribute to @secure. If %TRUE, @cookie will
691 * only be transmitted from the client to the server over secure
692 * (https) connections.
697 soup_cookie_set_secure (SoupCookie *cookie, gboolean secure)
699 cookie->secure = secure;
703 * soup_cookie_get_http_only:
704 * @cookie: a #SoupCookie
706 * Gets @cookie's HttpOnly attribute
708 * Return value: @cookie's HttpOnly attribute
713 soup_cookie_get_http_only (SoupCookie *cookie)
715 return cookie->http_only;
719 * soup_cookie_set_http_only:
720 * @cookie: a #SoupCookie
721 * @http_only: the new value for the HttpOnly attribute
723 * Sets @cookie's HttpOnly attribute to @http_only. If %TRUE, @cookie
724 * will be marked as "http only", meaning it should not be exposed to
725 * web page scripts or other untrusted code.
730 soup_cookie_set_http_only (SoupCookie *cookie, gboolean http_only)
732 cookie->http_only = http_only;
736 serialize_cookie (SoupCookie *cookie, GString *header, gboolean set_cookie)
738 if (!*cookie->name && !*cookie->value)
743 g_string_append (header, ", ");
745 g_string_append (header, "; ");
748 if (set_cookie || *cookie->name) {
749 g_string_append (header, cookie->name);
750 g_string_append (header, "=");
752 g_string_append (header, cookie->value);
756 if (cookie->expires) {
759 g_string_append (header, "; expires=");
760 timestamp = soup_date_to_string (cookie->expires,
762 g_string_append (header, timestamp);
766 g_string_append (header, "; path=");
767 g_string_append (header, cookie->path);
769 if (cookie->domain) {
770 g_string_append (header, "; domain=");
771 g_string_append (header, cookie->domain);
774 g_string_append (header, "; secure");
776 g_string_append (header, "; HttpOnly");
780 * soup_cookie_to_set_cookie_header:
781 * @cookie: a #SoupCookie
783 * Serializes @cookie in the format used by the Set-Cookie header
784 * (ie, for sending a cookie from a #SoupServer to a client).
786 * Return value: the header
791 soup_cookie_to_set_cookie_header (SoupCookie *cookie)
793 GString *header = g_string_new (NULL);
795 serialize_cookie (cookie, header, TRUE);
796 return g_string_free (header, FALSE);
800 * soup_cookie_to_cookie_header:
801 * @cookie: a #SoupCookie
803 * Serializes @cookie in the format used by the Cookie header (ie, for
804 * returning a cookie from a #SoupSession to a server).
806 * Return value: the header
811 soup_cookie_to_cookie_header (SoupCookie *cookie)
813 GString *header = g_string_new (NULL);
815 serialize_cookie (cookie, header, FALSE);
816 return g_string_free (header, FALSE);
821 * @cookie: a #SoupCookie
828 soup_cookie_free (SoupCookie *cookie)
830 g_return_if_fail (cookie != NULL);
832 g_free (cookie->name);
833 g_free (cookie->value);
834 g_free (cookie->domain);
835 g_free (cookie->path);
838 soup_date_free (cookie->expires);
840 g_slice_free (SoupCookie, cookie);
844 * soup_cookies_from_response:
845 * @msg: a #SoupMessage containing a "Set-Cookie" response header
847 * Parses @msg's Set-Cookie response headers and returns a #GSList of
848 * #SoupCookie<!-- -->s. Cookies that do not specify "path" or
849 * "domain" attributes will have their values defaulted from @msg.
851 * Return value: (element-type SoupCookie) (transfer full): a #GSList
852 * of #SoupCookie<!-- -->s, which can be freed with
853 * soup_cookies_free().
858 soup_cookies_from_response (SoupMessage *msg)
861 const char *name, *value;
863 GSList *cookies = NULL;
864 SoupMessageHeadersIter iter;
866 origin = soup_message_get_uri (msg);
868 /* We have to use soup_message_headers_iter rather than
869 * soup_message_headers_get_list() since Set-Cookie isn't
870 * properly mergeable/unmergeable.
872 soup_message_headers_iter_init (&iter, msg->response_headers);
873 while (soup_message_headers_iter_next (&iter, &name, &value)) {
874 if (g_ascii_strcasecmp (name, "Set-Cookie") != 0)
877 cookie = parse_one_cookie (value, origin);
879 cookies = g_slist_prepend (cookies, cookie);
881 return g_slist_reverse (cookies);
885 * soup_cookies_from_request:
886 * @msg: a #SoupMessage containing a "Cookie" request header
888 * Parses @msg's Cookie request header and returns a #GSList of
889 * #SoupCookie<!-- -->s. As the "Cookie" header, unlike "Set-Cookie",
890 * only contains cookie names and values, none of the other
891 * #SoupCookie fields will be filled in. (Thus, you can't generally
892 * pass a cookie returned from this method directly to
893 * soup_cookies_to_response().)
895 * Return value: (element-type SoupCookie) (transfer full): a #GSList
896 * of #SoupCookie<!-- -->s, which can be freed with
897 * soup_cookies_free().
902 soup_cookies_from_request (SoupMessage *msg)
905 GSList *cookies = NULL;
908 gpointer name, value;
911 header = soup_message_headers_get_one (msg->request_headers, "Cookie");
915 params = soup_header_parse_semi_param_list (header);
916 g_hash_table_iter_init (&iter, params);
917 while (g_hash_table_iter_next (&iter, &name, &value)) {
919 cookie = cookie_new_internal (name, value,
921 cookies = g_slist_prepend (cookies, cookie);
924 soup_header_free_param_list (params);
926 return g_slist_reverse (cookies);
930 * soup_cookies_to_response:
931 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
932 * @msg: a #SoupMessage
934 * Appends a "Set-Cookie" response header to @msg for each cookie in
935 * @cookies. (This is in addition to any other "Set-Cookie" headers
936 * @msg may already have.)
941 soup_cookies_to_response (GSList *cookies, SoupMessage *msg)
945 header = g_string_new (NULL);
947 serialize_cookie (cookies->data, header, TRUE);
948 soup_message_headers_append (msg->response_headers,
949 "Set-Cookie", header->str);
950 g_string_truncate (header, 0);
951 cookies = cookies->next;
953 g_string_free (header, TRUE);
957 * soup_cookies_to_request:
958 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
959 * @msg: a #SoupMessage
961 * Adds the name and value of each cookie in @cookies to @msg's
962 * "Cookie" request. (If @msg already has a "Cookie" request header,
963 * these cookies will be appended to the cookies already present. Be
964 * careful that you do not append the same cookies twice, eg, when
965 * requeuing a message.)
970 soup_cookies_to_request (GSList *cookies, SoupMessage *msg)
974 header = g_string_new (soup_message_headers_get_one (msg->request_headers,
977 serialize_cookie (cookies->data, header, FALSE);
978 cookies = cookies->next;
980 soup_message_headers_replace (msg->request_headers,
981 "Cookie", header->str);
982 g_string_free (header, TRUE);
986 * soup_cookies_free: (skip)
987 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
994 soup_cookies_free (GSList *cookies)
998 for (c = cookies; c; c = c->next)
999 soup_cookie_free (c->data);
1000 g_slist_free (cookies);
1004 * soup_cookies_to_cookie_header:
1005 * @cookies: (element-type SoupCookie): a #GSList of #SoupCookie
1007 * Serializes a #GSList of #SoupCookie into a string suitable for
1008 * setting as the value of the "Cookie" header.
1010 * Return value: the serialization of @cookies
1015 soup_cookies_to_cookie_header (GSList *cookies)
1019 g_return_val_if_fail (cookies != NULL, NULL);
1021 str = g_string_new (NULL);
1023 serialize_cookie (cookies->data, str, FALSE);
1024 cookies = cookies->next;
1027 return g_string_free (str, FALSE);
1031 * soup_cookie_applies_to_uri:
1032 * @cookie: a #SoupCookie
1035 * Tests if @cookie should be sent to @uri.
1037 * (At the moment, this does not check that @cookie's domain matches
1038 * @uri, because it assumes that the caller has already done that.
1039 * But don't rely on that; it may change in the future.)
1041 * Return value: %TRUE if @cookie should be sent to @uri, %FALSE if
1047 soup_cookie_applies_to_uri (SoupCookie *cookie, SoupURI *uri)
1051 if (cookie->secure && uri->scheme != SOUP_URI_SCHEME_HTTPS)
1054 if (cookie->expires && soup_date_is_past (cookie->expires))
1057 /* uri->path is required to be non-NULL */
1058 g_return_val_if_fail (uri->path != NULL, FALSE);
1060 plen = strlen (cookie->path);
1063 if (strncmp (cookie->path, uri->path, plen) != 0)
1065 if (cookie->path[plen - 1] != '/' &&
1066 uri->path[plen] && uri->path[plen] != '/')
1073 soup_cookie_equal (SoupCookie *cookie1, SoupCookie *cookie2)
1075 g_return_val_if_fail (cookie1, FALSE);
1076 g_return_val_if_fail (cookie2, FALSE);
1078 return (!strcmp (cookie1->name, cookie2->name) &&
1079 !strcmp (cookie1->value, cookie2->value) &&
1080 !strcmp (cookie1->path, cookie2->path));