1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-cookie-jar-text.c: cookies.txt-based cookie storage
5 * Copyright (C) 2007, 2008 Red Hat, Inc.
16 #include "soup-cookie-jar-text.h"
21 * SECTION:soup-cookie-jar-text
22 * @short_description: Text-file-based ("cookies.txt") Cookie Jar
24 * #SoupCookieJarText is a #SoupCookieJar that reads cookies from and
25 * writes them to a text file in the Mozilla "cookies.txt" format.
39 } SoupCookieJarTextPrivate;
40 #define SOUP_COOKIE_JAR_TEXT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextPrivate))
42 G_DEFINE_TYPE (SoupCookieJarText, soup_cookie_jar_text, SOUP_TYPE_COOKIE_JAR)
44 static void load (SoupCookieJar *jar);
47 soup_cookie_jar_text_init (SoupCookieJarText *text)
52 soup_cookie_jar_text_finalize (GObject *object)
54 SoupCookieJarTextPrivate *priv =
55 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
57 g_free (priv->filename);
59 G_OBJECT_CLASS (soup_cookie_jar_text_parent_class)->finalize (object);
63 soup_cookie_jar_text_set_property (GObject *object, guint prop_id,
64 const GValue *value, GParamSpec *pspec)
66 SoupCookieJarTextPrivate *priv =
67 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
71 priv->filename = g_value_dup_string (value);
72 load (SOUP_COOKIE_JAR (object));
75 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
81 soup_cookie_jar_text_get_property (GObject *object, guint prop_id,
82 GValue *value, GParamSpec *pspec)
84 SoupCookieJarTextPrivate *priv =
85 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
89 g_value_set_string (value, priv->filename);
92 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
98 * soup_cookie_jar_text_new:
99 * @filename: the filename to read to/write from
100 * @read_only: %TRUE if @filename is read-only
102 * Creates a #SoupCookieJarText.
104 * @filename will be read in at startup to create an initial set of
105 * cookies. If @read_only is %FALSE, then the non-session cookies will
106 * be written to @filename when the 'changed' signal is emitted from
107 * the jar. (If @read_only is %TRUE, then the cookie jar will only be
108 * used for this session, and changes made to it will be lost when the
111 * Return value: the new #SoupCookieJar
116 soup_cookie_jar_text_new (const char *filename, gboolean read_only)
118 g_return_val_if_fail (filename != NULL, NULL);
120 return g_object_new (SOUP_TYPE_COOKIE_JAR_TEXT,
121 SOUP_COOKIE_JAR_TEXT_FILENAME, filename,
122 SOUP_COOKIE_JAR_READ_ONLY, read_only,
127 parse_cookie (char *line, time_t now)
130 SoupCookie *cookie = NULL;
134 char *host, *path, *secure, *expires, *name, *value;
136 if (g_str_has_prefix (line, "#HttpOnly_")) {
138 line += strlen ("#HttpOnly_");
139 #if ENABLE(TIZEN_TV_FIX_TEXT_STORAGE_MODE_OF_COOKIE)
140 /* For file:// protocol, When use document.cookie to get/set cookie.
141 * host(first field of cookie string) will be empty.'\t' has been
142 * used as a separator to split each field. Because host is empty,
143 * the first char of cookie string will be '\t'. it may cause
144 * parse_cookie() interrupted and returned. This modification can
145 * make cookie parsing normally for file:// protocol.
147 } else if (*line == '#')
149 } else if (*line == '#' || g_ascii_isspace (*line))
155 result = g_strsplit (line, "\t", -1);
156 if (g_strv_length (result) != 7)
159 /* Check this first */
161 expire_time = strtoul (expires, NULL, 10);
162 if (now >= expire_time)
164 max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
168 /* result[1] is not used because it's redundat; it's a boolean
169 * value regarding whether the cookie should be used for
170 * sub-domains of the domain that is set for the cookie. It is
171 * TRUE if host starts with '.', and FALSE otherwise.
180 cookie = soup_cookie_new (name, value, host, path, max_age);
182 if (strcmp (secure, "FALSE") != 0)
183 soup_cookie_set_secure (cookie, TRUE);
185 soup_cookie_set_http_only (cookie, TRUE);
194 parse_line (SoupCookieJar *jar, char *line, time_t now)
198 cookie = parse_cookie (line, now);
200 soup_cookie_jar_add_cookie (jar, cookie);
204 load (SoupCookieJar *jar)
206 SoupCookieJarTextPrivate *priv =
207 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
208 char *contents = NULL, *line, *p;
210 time_t now = time (NULL);
213 if (!g_file_get_contents (priv->filename, &contents, &length, NULL))
217 for (p = contents; *p; p++) {
218 /* \r\n comes out as an extra empty line and gets ignored */
219 if (*p == '\r' || *p == '\n') {
221 parse_line (jar, line, now);
225 parse_line (jar, line, now);
231 write_cookie (FILE *out, SoupCookie *cookie)
233 fseek (out, 0, SEEK_END);
235 fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
236 cookie->http_only ? "#HttpOnly_" : "",
238 *cookie->domain == '.' ? "TRUE" : "FALSE",
240 cookie->secure ? "TRUE" : "FALSE",
241 (gulong)soup_date_to_time_t (cookie->expires),
247 delete_cookie (const char *filename, SoupCookie *cookie)
249 char *contents = NULL, *line, *p;
253 time_t now = time (NULL);
255 if (!g_file_get_contents (filename, &contents, &length, NULL))
258 f = fopen (filename, "w");
265 for (p = contents; *p; p++) {
266 /* \r\n comes out as an extra empty line and gets ignored */
267 if (*p == '\r' || *p == '\n') {
269 c = parse_cookie (line, now);
273 if (!soup_cookie_equal (cookie, c))
275 soup_cookie_free (c);
278 c = parse_cookie (line, now);
280 if (!soup_cookie_equal (cookie, c))
282 soup_cookie_free (c);
290 soup_cookie_jar_text_changed (SoupCookieJar *jar,
291 SoupCookie *old_cookie,
292 SoupCookie *new_cookie)
295 SoupCookieJarTextPrivate *priv =
296 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
298 /* We can sort of ignore the semantics of the 'changed'
299 * signal here and simply delete the old cookie if present
300 * and write the new cookie if present. That will do the
301 * right thing for all 'added', 'deleted' and 'modified'
304 /* Also, delete_cookie takes the filename and write_cookie
305 * a FILE pointer. Seems more convenient that way considering
306 * the implementations of the functions
309 delete_cookie (priv->filename, old_cookie);
312 gboolean write_header = FALSE;
314 if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS))
317 out = fopen (priv->filename, "a");
324 fprintf (out, "# HTTP Cookie File\n");
325 fprintf (out, "# http://www.netscape.com/newsref/std/cookie_spec.html\n");
326 fprintf (out, "# This is a generated file! Do not edit.\n");
327 fprintf (out, "# To delete cookies, use the Cookie Manager.\n\n");
330 if (new_cookie->expires)
331 write_cookie (out, new_cookie);
333 if (fclose (out) != 0) {
341 soup_cookie_jar_text_is_persistent (SoupCookieJar *jar)
347 soup_cookie_jar_text_class_init (SoupCookieJarTextClass *text_class)
349 SoupCookieJarClass *cookie_jar_class =
350 SOUP_COOKIE_JAR_CLASS (text_class);
351 GObjectClass *object_class = G_OBJECT_CLASS (text_class);
353 g_type_class_add_private (text_class, sizeof (SoupCookieJarTextPrivate));
355 cookie_jar_class->is_persistent = soup_cookie_jar_text_is_persistent;
356 cookie_jar_class->changed = soup_cookie_jar_text_changed;
358 object_class->finalize = soup_cookie_jar_text_finalize;
359 object_class->set_property = soup_cookie_jar_text_set_property;
360 object_class->get_property = soup_cookie_jar_text_get_property;
363 * SOUP_COOKIE_JAR_TEXT_FILENAME:
365 * Alias for the #SoupCookieJarText:filename property. (The
366 * cookie-storage filename.)
368 g_object_class_install_property (
369 object_class, PROP_FILENAME,
370 g_param_spec_string (SOUP_COOKIE_JAR_TEXT_FILENAME,
372 "Cookie-storage filename",
374 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));