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