Reapplying patch to disable attempts to use gtk-doc
[profile/ivi/libsoup2.4.git] / libsoup / soup-cookie-jar-text.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-cookie-jar-text.c: cookies.txt-based cookie storage
4  *
5  * Copyright (C) 2007, 2008 Red Hat, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15
16 #include "soup-cookie-jar-text.h"
17 #include "soup-cookie.h"
18 #include "soup-date.h"
19
20 /**
21  * SECTION:soup-cookie-jar-text
22  * @short_description: Text-file-based ("cookies.txt") Cookie Jar
23  *
24  * #SoupCookieJarText is a #SoupCookieJar that reads cookies from and
25  * writes them to a text file in the Mozilla "cookies.txt" format.
26  **/
27
28 enum {
29         PROP_0,
30
31         PROP_FILENAME,
32
33         LAST_PROP
34 };
35
36 typedef struct {
37         char *filename;
38
39 } SoupCookieJarTextPrivate;
40 #define SOUP_COOKIE_JAR_TEXT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_TEXT, SoupCookieJarTextPrivate))
41
42 G_DEFINE_TYPE (SoupCookieJarText, soup_cookie_jar_text, SOUP_TYPE_COOKIE_JAR)
43
44 static void load    (SoupCookieJar *jar);
45 static void changed (SoupCookieJar *jar,
46                      SoupCookie    *old_cookie,
47                      SoupCookie    *new_cookie);
48 static gboolean is_persistent (SoupCookieJar *jar);
49
50 static void set_property (GObject *object, guint prop_id,
51                           const GValue *value, GParamSpec *pspec);
52 static void get_property (GObject *object, guint prop_id,
53                           GValue *value, GParamSpec *pspec);
54
55 static void
56 soup_cookie_jar_text_init (SoupCookieJarText *text)
57 {
58 }
59
60 static void
61 finalize (GObject *object)
62 {
63         SoupCookieJarTextPrivate *priv =
64                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
65
66         g_free (priv->filename);
67
68         G_OBJECT_CLASS (soup_cookie_jar_text_parent_class)->finalize (object);
69 }
70
71 static void
72 soup_cookie_jar_text_class_init (SoupCookieJarTextClass *text_class)
73 {
74         SoupCookieJarClass *cookie_jar_class =
75                 SOUP_COOKIE_JAR_CLASS (text_class);
76         GObjectClass *object_class = G_OBJECT_CLASS (text_class);
77
78         g_type_class_add_private (text_class, sizeof (SoupCookieJarTextPrivate));
79
80         cookie_jar_class->is_persistent = is_persistent;
81         cookie_jar_class->changed       = changed;
82
83         object_class->finalize     = finalize;
84         object_class->set_property = set_property;
85         object_class->get_property = get_property;
86
87         /**
88          * SOUP_COOKIE_JAR_TEXT_FILENAME:
89          *
90          * Alias for the #SoupCookieJarText:filename property. (The
91          * cookie-storage filename.)
92          **/
93         g_object_class_install_property (
94                 object_class, PROP_FILENAME,
95                 g_param_spec_string (SOUP_COOKIE_JAR_TEXT_FILENAME,
96                                      "Filename",
97                                      "Cookie-storage filename",
98                                      NULL,
99                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
100 }
101
102 static void
103 set_property (GObject *object, guint prop_id,
104               const GValue *value, GParamSpec *pspec)
105 {
106         SoupCookieJarTextPrivate *priv =
107                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
108
109         switch (prop_id) {
110         case PROP_FILENAME:
111                 priv->filename = g_value_dup_string (value);
112                 load (SOUP_COOKIE_JAR (object));
113                 break;
114         default:
115                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
116                 break;
117         }
118 }
119
120 static void
121 get_property (GObject *object, guint prop_id,
122               GValue *value, GParamSpec *pspec)
123 {
124         SoupCookieJarTextPrivate *priv =
125                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
126
127         switch (prop_id) {
128         case PROP_FILENAME:
129                 g_value_set_string (value, priv->filename);
130                 break;
131         default:
132                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
133                 break;
134         }
135 }
136
137 /**
138  * soup_cookie_jar_text_new:
139  * @filename: the filename to read to/write from
140  * @read_only: %TRUE if @filename is read-only
141  *
142  * Creates a #SoupCookieJarText.
143  *
144  * @filename will be read in at startup to create an initial set of
145  * cookies. If @read_only is %FALSE, then the non-session cookies will
146  * be written to @filename when the 'changed' signal is emitted from
147  * the jar. (If @read_only is %TRUE, then the cookie jar will only be
148  * used for this session, and changes made to it will be lost when the
149  * jar is destroyed.)
150  *
151  * Return value: the new #SoupCookieJar
152  *
153  * Since: 2.26
154  **/
155 SoupCookieJar *
156 soup_cookie_jar_text_new (const char *filename, gboolean read_only)
157 {
158         g_return_val_if_fail (filename != NULL, NULL);
159
160         return g_object_new (SOUP_TYPE_COOKIE_JAR_TEXT,
161                              SOUP_COOKIE_JAR_TEXT_FILENAME, filename,
162                              SOUP_COOKIE_JAR_READ_ONLY, read_only,
163                              NULL);
164 }
165
166 static SoupCookie*
167 parse_cookie (char *line, time_t now)
168 {
169         char **result;
170         SoupCookie *cookie = NULL;
171         gboolean http_only;
172         gulong expire_time;
173         int max_age;
174         char *host, *path, *secure, *expires, *name, *value;
175
176         if (g_str_has_prefix (line, "#HttpOnly_")) {
177                 http_only = TRUE;
178                 line += strlen ("#HttpOnly_");
179         } else if (*line == '#' || g_ascii_isspace (*line))
180                 return cookie;
181         else
182                 http_only = FALSE;
183
184         result = g_strsplit (line, "\t", -1);
185         if (g_strv_length (result) != 7)
186                 goto out;
187
188         /* Check this first */
189         expires = result[4];
190         expire_time = strtoul (expires, NULL, 10);
191         if (now >= expire_time)
192                 goto out;
193         max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
194
195         host = result[0];
196
197         /* result[1] is not used because it's redundat; it's a boolean
198          * value regarding whether the cookie should be used for
199          * sub-domains of the domain that is set for the cookie. It is
200          * TRUE if host starts with '.', and FALSE otherwise.
201          */
202
203         path = result[2];
204         secure = result[3];
205
206         name = result[5];
207         value = result[6];
208
209         cookie = soup_cookie_new (name, value, host, path, max_age);
210
211         if (strcmp (secure, "FALSE") != 0)
212                 soup_cookie_set_secure (cookie, TRUE);
213         if (http_only)
214                 soup_cookie_set_http_only (cookie, TRUE);
215
216  out:
217         g_strfreev (result);
218
219         return cookie;
220 }
221
222 static void
223 parse_line (SoupCookieJar *jar, char *line, time_t now)
224 {
225         SoupCookie *cookie;
226
227         cookie = parse_cookie (line, now);
228         if (cookie)
229                 soup_cookie_jar_add_cookie (jar, cookie);
230 }
231
232 static void
233 load (SoupCookieJar *jar)
234 {
235         SoupCookieJarTextPrivate *priv =
236                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
237         char *contents = NULL, *line, *p;
238         gsize length = 0;
239         time_t now = time (NULL);
240
241         /* FIXME: error? */
242         if (!g_file_get_contents (priv->filename, &contents, &length, NULL))
243                 return;
244
245         line = contents;
246         for (p = contents; *p; p++) {
247                 /* \r\n comes out as an extra empty line and gets ignored */
248                 if (*p == '\r' || *p == '\n') {
249                         *p = '\0';
250                         parse_line (jar, line, now);
251                         line = p + 1;
252                 }
253         }
254         parse_line (jar, line, now);
255
256         g_free (contents);
257 }
258
259 static void
260 write_cookie (FILE *out, SoupCookie *cookie)
261 {
262         fseek (out, 0, SEEK_END);
263
264         fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
265                  cookie->http_only ? "#HttpOnly_" : "",
266                  cookie->domain,
267                  *cookie->domain == '.' ? "TRUE" : "FALSE",
268                  cookie->path,
269                  cookie->secure ? "TRUE" : "FALSE",
270                  (gulong)soup_date_to_time_t (cookie->expires),
271                  cookie->name,
272                  cookie->value);
273 }
274
275 static void
276 delete_cookie (const char *filename, SoupCookie *cookie)
277 {
278         char *contents = NULL, *line, *p;
279         gsize length = 0;
280         FILE *f;
281         SoupCookie *c;
282         time_t now = time (NULL);
283
284         if (!g_file_get_contents (filename, &contents, &length, NULL))
285                 return;
286
287         f = fopen (filename, "w");
288         if (!f) {
289                 g_free (contents);
290                 return;
291         }
292
293         line = contents;
294         for (p = contents; *p; p++) {
295                 /* \r\n comes out as an extra empty line and gets ignored */
296                 if (*p == '\r' || *p == '\n') {
297                         *p = '\0';
298                         c = parse_cookie (line, now);
299                         line = p + 1;
300                         if (!c)
301                                 continue;
302                         if (!soup_cookie_equal (cookie, c))
303                                 write_cookie (f, c);
304                         soup_cookie_free (c);
305                 }
306         }
307         c = parse_cookie (line, now);
308         if (c) {
309                 if (!soup_cookie_equal (cookie, c))
310                         write_cookie (f, c);
311                 soup_cookie_free (c);
312         }
313
314         g_free (contents);
315         fclose (f);
316 }
317
318 static void
319 changed (SoupCookieJar *jar,
320          SoupCookie    *old_cookie,
321          SoupCookie    *new_cookie)
322 {
323         FILE *out;
324         SoupCookieJarTextPrivate *priv =
325                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
326
327         /* We can sort of ignore the semantics of the 'changed'
328          * signal here and simply delete the old cookie if present
329          * and write the new cookie if present. That will do the
330          * right thing for all 'added', 'deleted' and 'modified'
331          * meanings.
332          */
333         /* Also, delete_cookie takes the filename and write_cookie
334          * a FILE pointer. Seems more convenient that way considering
335          * the implementations of the functions
336          */
337         if (old_cookie)
338                 delete_cookie (priv->filename, old_cookie);
339
340         if (new_cookie) {
341                 gboolean write_header = FALSE;
342
343                 if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS))
344                         write_header = TRUE;
345
346                 out = fopen (priv->filename, "a");
347                 if (!out) {
348                         /* FIXME: error? */
349                         return;
350                 }
351
352                 if (write_header) {
353                         fprintf (out, "# HTTP Cookie File\n");
354                         fprintf (out, "# http://www.netscape.com/newsref/std/cookie_spec.html\n");
355                         fprintf (out, "# This is a generated file!  Do not edit.\n");
356                         fprintf (out, "# To delete cookies, use the Cookie Manager.\n\n");
357                 }
358
359                 if (new_cookie->expires)
360                         write_cookie (out, new_cookie);
361
362                 if (fclose (out) != 0) {
363                         /* FIXME: error? */
364                         return;
365                 }
366         }
367 }
368
369 static gboolean
370 is_persistent (SoupCookieJar *jar)
371 {
372         return TRUE;
373 }