1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 *
3 * Copyright (C) 2014 Richard Hughes <richard@hughsie.com>
4 *
5 * Licensed under the GNU General Public License Version 2
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "config.h"
23
24 #include <gio/gio.h>
25 #include <glib-object.h>
26 #include <sqlite3.h>
27
28 #include "cd-common.h"
29 #include "cd-profile-db.h"
30
31 static void cd_profile_db_finalize (GObject *object);
32
33 #define GET_PRIVATE(o) (cd_profile_db_get_instance_private (o))
34
35 typedef struct
36 {
37 sqlite3 *db;
38 } CdProfileDbPrivate;
39
40 static gpointer cd_profile_db_object = NULL;
41
G_DEFINE_TYPE_WITH_PRIVATE(CdProfileDb,cd_profile_db,G_TYPE_OBJECT)42 G_DEFINE_TYPE_WITH_PRIVATE (CdProfileDb, cd_profile_db, G_TYPE_OBJECT)
43
44 /**
45 * cd_profile_db_load:
46 **/
47 gboolean
48 cd_profile_db_load (CdProfileDb *pdb,
49 const gchar *filename,
50 GError **error)
51 {
52 CdProfileDbPrivate *priv = GET_PRIVATE (pdb);
53 const gchar *statement;
54 gchar *error_msg = NULL;
55 gint rc;
56 g_autofree gchar *path = NULL;
57
58 g_return_val_if_fail (CD_IS_PROFILE_DB (pdb), FALSE);
59 g_return_val_if_fail (priv->db == NULL, FALSE);
60
61 /* ensure the path exists */
62 path = g_path_get_dirname (filename);
63 if (!cd_main_mkdir_with_parents (path, error))
64 return FALSE;
65
66 g_debug ("CdProfileDb: trying to open database '%s'", filename);
67 g_info ("Using profile database file %s", filename);
68 rc = sqlite3_open (filename, &priv->db);
69 if (rc != SQLITE_OK) {
70 g_set_error (error,
71 CD_CLIENT_ERROR,
72 CD_CLIENT_ERROR_INTERNAL,
73 "Can't open database: %s\n",
74 sqlite3_errmsg (priv->db));
75 sqlite3_close (priv->db);
76 return FALSE;
77 }
78
79 /* we don't need to keep doing fsync */
80 sqlite3_exec (priv->db, "PRAGMA synchronous=OFF",
81 NULL, NULL, NULL);
82
83 /* check schema */
84 rc = sqlite3_exec (priv->db, "SELECT * FROM properties_pu LIMIT 1",
85 NULL, NULL, &error_msg);
86 if (rc != SQLITE_OK) {
87 statement = "CREATE TABLE properties_pu ("
88 "profile_id TEXT,"
89 "property TEXT,"
90 "uid INTEGER,"
91 "value TEXT,"
92 "PRIMARY KEY (profile_id, property, uid));";
93 sqlite3_exec (priv->db, statement, NULL, NULL, NULL);
94 }
95 return TRUE;
96 }
97
98 /**
99 * cd_profile_db_empty:
100 **/
101 gboolean
cd_profile_db_empty(CdProfileDb * pdb,GError ** error)102 cd_profile_db_empty (CdProfileDb *pdb, GError **error)
103 {
104 CdProfileDbPrivate *priv = GET_PRIVATE (pdb);
105 const gchar *statement;
106 gchar *error_msg = NULL;
107 gint rc;
108
109 g_return_val_if_fail (CD_IS_PROFILE_DB (pdb), FALSE);
110 g_return_val_if_fail (priv->db != NULL, FALSE);
111
112 statement = "DELETE FROM properties_pu;";
113 rc = sqlite3_exec (priv->db, statement,
114 NULL, NULL, &error_msg);
115 if (rc != SQLITE_OK) {
116 g_set_error (error,
117 CD_CLIENT_ERROR,
118 CD_CLIENT_ERROR_INTERNAL,
119 "SQL error: %s",
120 error_msg);
121 sqlite3_free (error_msg);
122 return FALSE;
123 }
124 return TRUE;
125 }
126
127 /**
128 * cd_profile_db_set_property:
129 **/
130 gboolean
cd_profile_db_set_property(CdProfileDb * pdb,const gchar * profile_id,const gchar * property,guint uid,const gchar * value,GError ** error)131 cd_profile_db_set_property (CdProfileDb *pdb,
132 const gchar *profile_id,
133 const gchar *property,
134 guint uid,
135 const gchar *value,
136 GError **error)
137 {
138 CdProfileDbPrivate *priv = GET_PRIVATE (pdb);
139 gboolean ret = TRUE;
140 gchar *error_msg = NULL;
141 gchar *statement;
142 gint rc;
143
144 g_return_val_if_fail (CD_IS_PROFILE_DB (pdb), FALSE);
145 g_return_val_if_fail (priv->db != NULL, FALSE);
146
147 g_debug ("CdProfileDb: add profile property %s [%s=%s]",
148 profile_id, property, value);
149 statement = sqlite3_mprintf ("INSERT OR REPLACE INTO properties_pu (profile_id, "
150 "property, uid, value) "
151 "VALUES ('%q', '%q', '%u', '%q');",
152 profile_id, property, uid, value);
153
154 /* insert the entry */
155 rc = sqlite3_exec (priv->db, statement, NULL, NULL, &error_msg);
156 if (rc != SQLITE_OK) {
157 g_set_error (error,
158 CD_CLIENT_ERROR,
159 CD_CLIENT_ERROR_INTERNAL,
160 "SQL error: %s",
161 error_msg);
162 sqlite3_free (error_msg);
163 ret = FALSE;
164 goto out;
165 }
166 out:
167 sqlite3_free (statement);
168 return ret;
169 }
170
171 /**
172 * cd_profile_db_remove:
173 **/
174 gboolean
cd_profile_db_remove(CdProfileDb * pdb,const gchar * profile_id,const gchar * property,guint uid,GError ** error)175 cd_profile_db_remove (CdProfileDb *pdb,
176 const gchar *profile_id,
177 const gchar *property,
178 guint uid,
179 GError **error)
180 {
181 CdProfileDbPrivate *priv = GET_PRIVATE (pdb);
182 gboolean ret = TRUE;
183 gchar *error_msg = NULL;
184 gchar *statement = NULL;
185 gint rc;
186
187 g_return_val_if_fail (CD_IS_PROFILE_DB (pdb), FALSE);
188 g_return_val_if_fail (priv->db != NULL, FALSE);
189
190 /* remove the entry */
191 g_debug ("CdProfileDb: remove profile %s", profile_id);
192 statement = sqlite3_mprintf ("DELETE FROM properties_pu WHERE "
193 "profile_id = '%q' AND "
194 "uid = '%i' AND "
195 "property = '%q' LIMIT 1;",
196 profile_id, uid, property);
197 rc = sqlite3_exec (priv->db, statement, NULL, NULL, &error_msg);
198 if (rc != SQLITE_OK) {
199 g_set_error (error,
200 CD_CLIENT_ERROR,
201 CD_CLIENT_ERROR_INTERNAL,
202 "SQL error: %s",
203 error_msg);
204 sqlite3_free (error_msg);
205 ret = FALSE;
206 goto out;
207 }
208 out:
209 sqlite3_free (statement);
210 return ret;
211 }
212
213 /**
214 * cd_profile_db_sqlite_cb:
215 **/
216 static gint
cd_profile_db_sqlite_cb(void * data,gint argc,gchar ** argv,gchar ** col_name)217 cd_profile_db_sqlite_cb (void *data,
218 gint argc,
219 gchar **argv,
220 gchar **col_name)
221 {
222 gchar **value = (gchar **) data;
223
224 /* should only be one entry */
225 g_debug ("CdProfileDb: got sql result %s", argv[0]);
226 *value = g_strdup (argv[0]);
227 return 0;
228 }
229
230 /**
231 * cd_profile_db_get_property:
232 **/
233 gboolean
cd_profile_db_get_property(CdProfileDb * pdb,const gchar * profile_id,const gchar * property,guint uid,gchar ** value,GError ** error)234 cd_profile_db_get_property (CdProfileDb *pdb,
235 const gchar *profile_id,
236 const gchar *property,
237 guint uid,
238 gchar **value,
239 GError **error)
240 {
241 CdProfileDbPrivate *priv = GET_PRIVATE (pdb);
242 gboolean ret = TRUE;
243 gchar *error_msg = NULL;
244 gchar *statement;
245 gint rc;
246
247 g_return_val_if_fail (CD_IS_PROFILE_DB (pdb), FALSE);
248 g_return_val_if_fail (priv->db != NULL, FALSE);
249
250 g_debug ("CdProfileDb: get property %s for %s", property, profile_id);
251 statement = sqlite3_mprintf ("SELECT value FROM properties_pu WHERE "
252 "profile_id = '%q' AND "
253 "uid = '%i' AND "
254 "property = '%q' LIMIT 1;",
255 profile_id, uid, property);
256
257 /* retrieve the entry */
258 rc = sqlite3_exec (priv->db,
259 statement,
260 cd_profile_db_sqlite_cb,
261 value,
262 &error_msg);
263 if (rc != SQLITE_OK) {
264 ret = FALSE;
265 g_set_error (error,
266 CD_CLIENT_ERROR,
267 CD_CLIENT_ERROR_INTERNAL,
268 "SQL error: %s",
269 error_msg);
270 sqlite3_free (error_msg);
271 goto out;
272 }
273 out:
274 sqlite3_free (statement);
275 return ret;
276 }
277
278 /**
279 * cd_profile_db_class_init:
280 **/
281 static void
cd_profile_db_class_init(CdProfileDbClass * klass)282 cd_profile_db_class_init (CdProfileDbClass *klass)
283 {
284 GObjectClass *object_class = G_OBJECT_CLASS (klass);
285 object_class->finalize = cd_profile_db_finalize;
286 }
287
288 /**
289 * cd_profile_db_init:
290 **/
291 static void
cd_profile_db_init(CdProfileDb * pdb)292 cd_profile_db_init (CdProfileDb *pdb)
293 {
294 }
295
296 /**
297 * cd_profile_db_finalize:
298 **/
299 static void
cd_profile_db_finalize(GObject * object)300 cd_profile_db_finalize (GObject *object)
301 {
302 CdProfileDb *pdb = CD_PROFILE_DB (object);
303 CdProfileDbPrivate *priv = GET_PRIVATE (pdb);
304
305 /* close the database */
306 sqlite3_close (priv->db);
307
308 G_OBJECT_CLASS (cd_profile_db_parent_class)->finalize (object);
309 }
310
311 /**
312 * cd_profile_db_new:
313 **/
314 CdProfileDb *
cd_profile_db_new(void)315 cd_profile_db_new (void)
316 {
317 if (cd_profile_db_object != NULL) {
318 g_object_ref (cd_profile_db_object);
319 } else {
320 cd_profile_db_object = g_object_new (CD_TYPE_PROFILE_DB, NULL);
321 g_object_add_weak_pointer (cd_profile_db_object, &cd_profile_db_object);
322 }
323 return CD_PROFILE_DB (cd_profile_db_object);
324 }
325
326