Reset error pointer to NULL when we are about to use it again.
[platform/upstream/libsoup.git] / libsoup / soup-cookie-jar-sqlite.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-cookie-jar-sqlite.c: ff sqlite-based cookie storage
4  *
5  * Using danw's soup-cookie-jar-text as template
6  * Copyright (C) 2008 Diego Escalante Urrelo
7  */
8
9 #ifdef HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sqlite3.h>
17
18 #include "soup-cookie-jar-sqlite.h"
19 #include "soup-cookie.h"
20 #include "soup-date.h"
21
22 /**
23  * SECTION:soup-cookie-jar-sqlite
24  * @short_description: SQLite-based Cookie Jar
25  *
26  * #SoupCookieJarSqlite is a #SoupCookieJar that reads cookies from and
27  * writes them to an SQLite file in the new Mozilla format.
28  **/
29
30 enum {
31         PROP_0,
32
33         PROP_FILENAME,
34
35         LAST_PROP
36 };
37
38 typedef struct {
39         char *filename;
40
41 } SoupCookieJarSqlitePrivate;
42
43 #define SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_COOKIE_JAR_SQLITE, SoupCookieJarSqlitePrivate))
44
45 G_DEFINE_TYPE (SoupCookieJarSqlite, soup_cookie_jar_sqlite, SOUP_TYPE_COOKIE_JAR)
46
47 static void load (SoupCookieJar *jar);
48 static void changed (SoupCookieJar *jar,
49                      SoupCookie    *old_cookie,
50                      SoupCookie    *new_cookie);
51
52 static void set_property (GObject *object, guint prop_id,
53                           const GValue *value, GParamSpec *pspec);
54 static void get_property (GObject *object, guint prop_id,
55                           GValue *value, GParamSpec *pspec);
56
57 static void
58 soup_cookie_jar_sqlite_init (SoupCookieJarSqlite *sqlite)
59 {
60 }
61
62 static void
63 finalize (GObject *object)
64 {
65         SoupCookieJarSqlitePrivate *priv =
66                 SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
67
68         g_free (priv->filename);
69
70         G_OBJECT_CLASS (soup_cookie_jar_sqlite_parent_class)->finalize (object);
71 }
72
73 static void
74 soup_cookie_jar_sqlite_class_init (SoupCookieJarSqliteClass *sqlite_class)
75 {
76         SoupCookieJarClass *cookie_jar_class =
77                 SOUP_COOKIE_JAR_CLASS (sqlite_class);
78         GObjectClass *object_class = G_OBJECT_CLASS (sqlite_class);
79
80         g_type_class_add_private (sqlite_class, sizeof (SoupCookieJarSqlitePrivate));
81
82         cookie_jar_class->changed = changed;
83
84         object_class->finalize     = finalize;
85         object_class->set_property = set_property;
86         object_class->get_property = get_property;
87
88         /**
89          * SOUP_COOKIE_JAR_SQLITE_FILENAME:
90          *
91          * Alias for the #SoupCookieJarSqlite:filename property. (The
92          * cookie-storage filename.)
93          **/
94         g_object_class_install_property (
95                 object_class, PROP_FILENAME,
96                 g_param_spec_string (SOUP_COOKIE_JAR_SQLITE_FILENAME,
97                                      "Filename",
98                                      "Cookie-storage filename",
99                                      NULL,
100                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
101 }
102
103 static void
104 set_property (GObject *object, guint prop_id,
105               const GValue *value, GParamSpec *pspec)
106 {
107         SoupCookieJarSqlitePrivate *priv =
108                 SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
109
110         switch (prop_id) {
111         case PROP_FILENAME:
112                 priv->filename = g_value_dup_string (value);
113                 load (SOUP_COOKIE_JAR (object));
114                 break;
115         default:
116                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
117                 break;
118         }
119 }
120
121 static void
122 get_property (GObject *object, guint prop_id,
123               GValue *value, GParamSpec *pspec)
124 {
125         SoupCookieJarSqlitePrivate *priv =
126                 SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (object);
127
128         switch (prop_id) {
129         case PROP_FILENAME:
130                 g_value_set_string (value, priv->filename);
131                 break;
132         default:
133                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
134                 break;
135         }
136 }
137
138 /**
139  * soup_cookie_jar_sqlite_new:
140  * @filename: the filename to read to/write from, or %NULL
141  * @read_only: %TRUE if @filename is read-only
142  *
143  * Creates a #SoupCookieJarSqlite.
144  *
145  * @filename will be read in at startup to create an initial set of
146  * cookies. If @read_only is %FALSE, then the non-session cookies will
147  * be written to @filename when the 'changed' signal is emitted from
148  * the jar. (If @read_only is %TRUE, then the cookie jar will only be
149  * used for this session, and changes made to it will be lost when the
150  * jar is destroyed.)
151  *
152  * Return value: the new #SoupCookieJar
153  *
154  * Since: 2.26
155  **/
156 SoupCookieJar *
157 soup_cookie_jar_sqlite_new (const char *filename, gboolean read_only)
158 {
159         g_return_val_if_fail (filename != NULL, NULL);
160
161         return g_object_new (SOUP_TYPE_COOKIE_JAR_SQLITE,
162                              SOUP_COOKIE_JAR_SQLITE_FILENAME, filename,
163                              SOUP_COOKIE_JAR_READ_ONLY, read_only,
164                              NULL);
165 }
166
167 #define QUERY_ALL "SELECT * FROM moz_cookies;"
168 #define CREATE_TABLE "CREATE TABLE moz_cookies (id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT,expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER)"
169 #define QUERY_INSERT "INSERT INTO moz_cookies VALUES(NULL, %Q, %Q, %Q, %Q, %d, NULL, %d, %d);"
170 #define QUERY_DELETE "DELETE FROM moz_cookies WHERE name=%Q AND host=%Q;"
171
172 enum {
173         COL_ID,
174         COL_NAME,
175         COL_VALUE,
176         COL_HOST,
177         COL_PATH,
178         COL_EXPIRY,
179         COL_LAST_ACCESS,
180         COL_SECURE,
181         COL_HTTP_ONLY,
182         N_COL,
183 };
184
185 static int
186 callback (void *data, int argc, char **argv, char **colname)
187 {
188         SoupCookie *cookie = NULL;
189         SoupCookieJar *jar = SOUP_COOKIE_JAR (data);
190
191         char *name, *value, *host, *path;
192         time_t max_age, now;
193         gboolean http_only = FALSE, secure = FALSE;
194
195         now = time (NULL);
196
197         name = argv[COL_NAME];
198         value = argv[COL_VALUE];
199         host = argv[COL_HOST];
200         path = argv[COL_PATH];
201         max_age = strtoul (argv[COL_EXPIRY], NULL, 10) - now;
202
203         if (max_age <= 0)
204                 return 0;
205
206         http_only = (strcmp (argv[COL_HTTP_ONLY], "1") == 0);
207         secure = (strcmp (argv[COL_SECURE], "1") == 0);
208
209         cookie = soup_cookie_new (name, value, host, path, max_age);
210
211         if (secure)
212                 soup_cookie_set_secure (cookie, TRUE);
213         if (http_only)
214                 soup_cookie_set_http_only (cookie, TRUE);
215
216         soup_cookie_jar_add_cookie (jar, cookie);
217
218         return 0;
219 }
220
221 static void
222 try_create_table (sqlite3 *db)
223 {
224         char *error = NULL;
225
226         if (sqlite3_exec (db, CREATE_TABLE, NULL, NULL, &error)) {
227                 g_warning ("Failed to execute query: %s", error);
228                 sqlite3_free (error);
229         }
230 }
231
232 static void
233 exec_query_with_try_create_table (sqlite3 *db,
234                                   const char *sql,
235                                   int (*callback)(void*,int,char**,char**),
236                                   void *argument)
237 {
238         char *error = NULL;
239         gboolean try_create = TRUE;
240
241 try_exec:
242         if (sqlite3_exec (db, sql, callback, argument, &error)) {
243                 if (try_create) {
244                         try_create = FALSE;
245                         try_create_table (db);
246                         sqlite3_free (error);
247                         error = NULL;
248                         goto try_exec;
249                 } else {
250                         g_warning ("Failed to execute query: %s", error);
251                         sqlite3_free (error);
252                 }
253         }
254 }
255                                          
256 static void
257 load (SoupCookieJar *jar)
258 {
259         SoupCookieJarSqlitePrivate *priv =
260                 SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
261
262         sqlite3 *db;
263
264         if (sqlite3_open (priv->filename, &db)) {
265                 sqlite3_close (db);
266                 g_warning ("Can't open %s", priv->filename);
267                 return;
268         }
269
270         exec_query_with_try_create_table (db, QUERY_ALL, callback, jar);
271
272         sqlite3_close (db);
273 }
274
275 static void
276 changed (SoupCookieJar *jar,
277          SoupCookie    *old_cookie,
278          SoupCookie    *new_cookie)
279 {
280         SoupCookieJarSqlitePrivate *priv =
281                 SOUP_COOKIE_JAR_SQLITE_GET_PRIVATE (jar);
282         sqlite3 *db;
283         char *query;
284
285         if (sqlite3_open (priv->filename, &db)) {
286                 sqlite3_close (db);
287                 g_warning ("Can't open %s", priv->filename);
288                 return;
289         }
290
291         if (old_cookie) {
292                 query = sqlite3_mprintf (QUERY_DELETE,
293                                          old_cookie->name,
294                                          old_cookie->domain);
295                 exec_query_with_try_create_table (db, query, NULL, NULL);
296                 sqlite3_free (query);
297         }
298
299         if (new_cookie && new_cookie->expires) {
300                 gulong expires;
301                 
302                 expires = (gulong)soup_date_to_time_t (new_cookie->expires);
303                 query = sqlite3_mprintf (QUERY_INSERT, 
304                                          new_cookie->name,
305                                          new_cookie->value,
306                                          new_cookie->domain,
307                                          new_cookie->path,
308                                          expires,
309                                          new_cookie->secure,
310                                          new_cookie->http_only);
311                 exec_query_with_try_create_table (db, query, NULL, NULL);
312                 sqlite3_free (query);
313         }
314
315         sqlite3_close (db);
316 }