Remove build warning
[platform/upstream/libsoup.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.h"
18
19 #include "TIZEN.h"
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
46 static void
47 soup_cookie_jar_text_init (SoupCookieJarText *text)
48 {
49 }
50
51 static void
52 soup_cookie_jar_text_finalize (GObject *object)
53 {
54         SoupCookieJarTextPrivate *priv =
55                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
56
57         g_free (priv->filename);
58
59         G_OBJECT_CLASS (soup_cookie_jar_text_parent_class)->finalize (object);
60 }
61
62 static void
63 soup_cookie_jar_text_set_property (GObject *object, guint prop_id,
64                                    const GValue *value, GParamSpec *pspec)
65 {
66         SoupCookieJarTextPrivate *priv =
67                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
68
69         switch (prop_id) {
70         case PROP_FILENAME:
71                 priv->filename = g_value_dup_string (value);
72                 load (SOUP_COOKIE_JAR (object));
73                 break;
74         default:
75                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
76                 break;
77         }
78 }
79
80 static void
81 soup_cookie_jar_text_get_property (GObject *object, guint prop_id,
82                                    GValue *value, GParamSpec *pspec)
83 {
84         SoupCookieJarTextPrivate *priv =
85                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (object);
86
87         switch (prop_id) {
88         case PROP_FILENAME:
89                 g_value_set_string (value, priv->filename);
90                 break;
91         default:
92                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
93                 break;
94         }
95 }
96
97 /**
98  * soup_cookie_jar_text_new:
99  * @filename: the filename to read to/write from
100  * @read_only: %TRUE if @filename is read-only
101  *
102  * Creates a #SoupCookieJarText.
103  *
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
109  * jar is destroyed.)
110  *
111  * Return value: the new #SoupCookieJar
112  *
113  * Since: 2.26
114  **/
115 SoupCookieJar *
116 soup_cookie_jar_text_new (const char *filename, gboolean read_only)
117 {
118         g_return_val_if_fail (filename != NULL, NULL);
119
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,
123                              NULL);
124 }
125
126 static SoupCookie*
127 parse_cookie (char *line, time_t now)
128 {
129         char **result;
130         SoupCookie *cookie = NULL;
131         gboolean http_only;
132         gulong expire_time;
133         int max_age;
134         char *host, *path, *secure, *expires, *name, *value;
135
136         if (g_str_has_prefix (line, "#HttpOnly_")) {
137                 http_only = TRUE;
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.
146         */
147         } else if (*line == '#')
148 #else
149         } else if (*line == '#' || g_ascii_isspace (*line))
150 #endif
151                 return cookie;
152         else
153                 http_only = FALSE;
154
155         result = g_strsplit (line, "\t", -1);
156         if (g_strv_length (result) != 7)
157                 goto out;
158
159         /* Check this first */
160         expires = result[4];
161         expire_time = strtoul (expires, NULL, 10);
162         if (now >= expire_time)
163                 goto out;
164         max_age = (expire_time - now <= G_MAXINT ? expire_time - now : G_MAXINT);
165
166         host = result[0];
167
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.
172          */
173
174         path = result[2];
175         secure = result[3];
176
177         name = result[5];
178         value = result[6];
179
180         cookie = soup_cookie_new (name, value, host, path, max_age);
181
182         if (strcmp (secure, "FALSE") != 0)
183                 soup_cookie_set_secure (cookie, TRUE);
184         if (http_only)
185                 soup_cookie_set_http_only (cookie, TRUE);
186
187  out:
188         g_strfreev (result);
189
190         return cookie;
191 }
192
193 static void
194 parse_line (SoupCookieJar *jar, char *line, time_t now)
195 {
196         SoupCookie *cookie;
197
198         cookie = parse_cookie (line, now);
199         if (cookie)
200                 soup_cookie_jar_add_cookie (jar, cookie);
201 }
202
203 static void
204 load (SoupCookieJar *jar)
205 {
206         SoupCookieJarTextPrivate *priv =
207                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
208         char *contents = NULL, *line, *p;
209         gsize length = 0;
210         time_t now = time (NULL);
211
212         /* FIXME: error? */
213         if (!g_file_get_contents (priv->filename, &contents, &length, NULL))
214                 return;
215
216         line = contents;
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') {
220                         *p = '\0';
221                         parse_line (jar, line, now);
222                         line = p + 1;
223                 }
224         }
225         parse_line (jar, line, now);
226
227         g_free (contents);
228 }
229
230 static void
231 write_cookie (FILE *out, SoupCookie *cookie)
232 {
233         fseek (out, 0, SEEK_END);
234
235         fprintf (out, "%s%s\t%s\t%s\t%s\t%lu\t%s\t%s\n",
236                  cookie->http_only ? "#HttpOnly_" : "",
237                  cookie->domain,
238                  *cookie->domain == '.' ? "TRUE" : "FALSE",
239                  cookie->path,
240                  cookie->secure ? "TRUE" : "FALSE",
241                  (gulong)soup_date_to_time_t (cookie->expires),
242                  cookie->name,
243                  cookie->value);
244 }
245
246 static void
247 delete_cookie (const char *filename, SoupCookie *cookie)
248 {
249         char *contents = NULL, *line, *p;
250         gsize length = 0;
251         FILE *f;
252         SoupCookie *c;
253         time_t now = time (NULL);
254
255         if (!g_file_get_contents (filename, &contents, &length, NULL))
256                 return;
257
258         f = fopen (filename, "w");
259         if (!f) {
260                 g_free (contents);
261                 return;
262         }
263
264         line = contents;
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') {
268                         *p = '\0';
269                         c = parse_cookie (line, now);
270                         line = p + 1;
271                         if (!c)
272                                 continue;
273                         if (!soup_cookie_equal (cookie, c))
274                                 write_cookie (f, c);
275                         soup_cookie_free (c);
276                 }
277         }
278         c = parse_cookie (line, now);
279         if (c) {
280                 if (!soup_cookie_equal (cookie, c))
281                         write_cookie (f, c);
282                 soup_cookie_free (c);
283         }
284
285         g_free (contents);
286         fclose (f);
287 }
288
289 static void
290 soup_cookie_jar_text_changed (SoupCookieJar *jar,
291                               SoupCookie    *old_cookie,
292                               SoupCookie    *new_cookie)
293 {
294         FILE *out;
295         SoupCookieJarTextPrivate *priv =
296                 SOUP_COOKIE_JAR_TEXT_GET_PRIVATE (jar);
297
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'
302          * meanings.
303          */
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
307          */
308         if (old_cookie)
309                 delete_cookie (priv->filename, old_cookie);
310
311         if (new_cookie) {
312                 gboolean write_header = FALSE;
313
314                 if (!g_file_test (priv->filename, G_FILE_TEST_EXISTS))
315                         write_header = TRUE;
316
317                 out = fopen (priv->filename, "a");
318                 if (!out) {
319                         /* FIXME: error? */
320                         return;
321                 }
322
323                 if (write_header) {
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");
328                 }
329
330                 if (new_cookie->expires)
331                         write_cookie (out, new_cookie);
332
333                 if (fclose (out) != 0) {
334                         /* FIXME: error? */
335                         return;
336                 }
337         }
338 }
339
340 static gboolean
341 soup_cookie_jar_text_is_persistent (SoupCookieJar *jar)
342 {
343         return TRUE;
344 }
345
346 static void
347 soup_cookie_jar_text_class_init (SoupCookieJarTextClass *text_class)
348 {
349         SoupCookieJarClass *cookie_jar_class =
350                 SOUP_COOKIE_JAR_CLASS (text_class);
351         GObjectClass *object_class = G_OBJECT_CLASS (text_class);
352
353         g_type_class_add_private (text_class, sizeof (SoupCookieJarTextPrivate));
354
355         cookie_jar_class->is_persistent = soup_cookie_jar_text_is_persistent;
356         cookie_jar_class->changed       = soup_cookie_jar_text_changed;
357
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;
361
362         /**
363          * SOUP_COOKIE_JAR_TEXT_FILENAME:
364          *
365          * Alias for the #SoupCookieJarText:filename property. (The
366          * cookie-storage filename.)
367          **/
368         g_object_class_install_property (
369                 object_class, PROP_FILENAME,
370                 g_param_spec_string (SOUP_COOKIE_JAR_TEXT_FILENAME,
371                                      "Filename",
372                                      "Cookie-storage filename",
373                                      NULL,
374                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
375 }