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