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