Fix memory leak
[platform/core/appfw/shortcut.git] / lib / src / shortcut_db.c
1 /*
2  * Copyright (c) 2011 - 2017 Samsung Electronics Co., Ltd. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <unistd.h>
19 #include <dlog.h>
20 #include <glib.h>
21 #include <db-util.h>
22 #include <shortcut_private.h>
23 #include <shortcut_db.h>
24 #include <vconf.h>
25 #include <vconf-keys.h>
26 #include <tzplatform_config.h>
27
28 #include "shortcut_error.h"
29
30 #define QUERY_CREATE_SHORTCUT_TABLE \
31         "PRAGMA journal_mode = PERSIST;" \
32         "CREATE TABLE IF NOT EXISTS shortcut_service (\n" \
33         "  id INTEGER PRIMARY KEY AUTOINCREMENT,\n" \
34         "  pkgid TEXT,\n" \
35         "  appid TEXT,\n" \
36         "  icon TEXT,\n" \
37         "  name TEXT,\n" \
38         "  extra_key TEXT,\n" \
39         "  extra_data TEXT);\n" \
40         "CREATE TABLE IF NOT EXISTS shortcut_name (\n" \
41         "  id INTEGER,\n" \
42         "  pkgid TEXT,\n" \
43         "  lang TEXT,\n" \
44         "  name TEXT,\n" \
45         "  icon TEXT);\n" \
46         "CREATE TABLE IF NOT EXISTS version (\n" \
47         "  version INTEGER);"
48
49 static bool is_db_corrupted = false;
50
51 static const char* _db_path = DB_PATH;
52
53 int shortcut_set_db_path(const char *db_path)
54 {
55         _db_path = db_path;
56         return 0;
57 }
58
59 /* LCOV_EXCL_START */
60 static int __check_integrity_cb(void *pid, int argc, char **argv, char **notUsed)
61 {
62         if (strcmp(argv[0], "ok") != 0) {
63                 SHORTCUT_ERR("db integrity result : %s", argv[0]);
64                 is_db_corrupted = true;
65                 return -1;
66         }
67
68         SHORTCUT_INFO("db integrity result : %s", argv[0]);
69         return 0;
70 }
71 /* LCOV_EXCL_STOP */
72
73 /* LCOV_EXCL_START */
74 static int __recover_corrupted_db()
75 {
76         int ret = SHORTCUT_ERROR_NONE;
77         int sql_ret;
78         char *errmsg = NULL;
79         sqlite3 *db = NULL;
80
81         SHORTCUT_INFO("DB is corrupted, start to recover corrupted db");
82
83         unlink(_db_path);
84         sql_ret = sqlite3_open_v2(_db_path, &db,
85                                 SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
86                                 NULL);
87         if (sql_ret != SQLITE_OK) {
88                 SHORTCUT_ERR("Failed to open db[%d]", sql_ret);
89                 unlink(_db_path);
90                 ret = SHORTCUT_ERROR_IO_ERROR;
91                 goto out;
92         }
93
94         sql_ret = sqlite3_exec(db, QUERY_CREATE_SHORTCUT_TABLE, NULL, NULL, &errmsg);
95         if (sql_ret != SQLITE_OK) {
96                 SHORTCUT_ERR("Failed to exec query[%d][%s]", sql_ret, errmsg);
97                 ret = SHORTCUT_ERROR_IO_ERROR;
98         }
99
100 out:
101         if (errmsg)
102                 sqlite3_free(errmsg);
103         if (db)
104                 sqlite3_close_v2(db);
105
106         return ret;
107 }
108 /* LCOV_EXCL_STOP */
109
110 /* LCOV_EXCL_START */
111 EAPI int shortcut_db_init(void)
112 {
113         int ret = SHORTCUT_ERROR_NONE;
114         int sql_ret;
115         sqlite3 *db = NULL;
116         char *errmsg = NULL;
117
118         sql_ret = sqlite3_open_v2(_db_path, &db,
119                         SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
120         if (sql_ret != SQLITE_OK) {
121                 SHORTCUT_ERR("Failed to open db[%d]", sql_ret);
122                 ret = SHORTCUT_ERROR_IO_ERROR;
123                 goto out;
124         }
125
126         sql_ret = sqlite3_exec(db, QUERY_CREATE_SHORTCUT_TABLE,
127                                 NULL, NULL, &errmsg);
128         if (sql_ret != SQLITE_OK) {
129                 SHORTCUT_ERR("Failed to exec query [%d][%s]", sql_ret, errmsg);
130                 ret = SHORTCUT_ERROR_IO_ERROR;
131                 goto out;
132         }
133
134         sql_ret = sqlite3_exec(db, "PRAGMA integrity_check",
135                                __check_integrity_cb, NULL, &errmsg);
136         if (sql_ret != SQLITE_OK || is_db_corrupted) {
137                 SHORTCUT_ERR("Failed to exec query[%d][%s]", sql_ret, errmsg);
138                 ret = SHORTCUT_ERROR_IO_ERROR;
139         }
140
141 out:
142         if (errmsg)
143                 sqlite3_free(errmsg);
144         if (db)
145                 sqlite3_close_v2(db);
146         if (sql_ret == SQLITE_CORRUPT || sql_ret == SQLITE_NOTADB || is_db_corrupted)
147                 ret = __recover_corrupted_db();
148
149         return ret;
150 }
151 /* LCOV_EXCL_STOP */
152
153 static sqlite3 *_open_db(void)
154 {
155         int ret;
156         const char *dbfile = _db_path;
157         sqlite3 *db = NULL;
158
159         ret = db_util_open(dbfile, &db, 0);
160         if (ret != SQLITE_OK) {
161                 /* LCOV_EXCL_START */
162                 SHORTCUT_DBG("Failed to open a %s\n", dbfile);
163                 return NULL;
164                 /* LCOV_EXCL_STOP */
165         }
166
167         return db;
168 }
169
170 static int _close_db(sqlite3 **db)
171 {
172         int ret = 0;
173
174         if (db == NULL || *db == NULL)
175                 return SHORTCUT_ERROR_INVALID_PARAMETER;
176
177         ret = db_util_close(*db);
178         if (ret != SQLITE_OK) {
179                 /* LCOV_EXCL_START */
180                 SHORTCUT_DBG("DB close error(%d)", ret);
181                 return SHORTCUT_ERROR_IO_ERROR;
182                 /* LCOV_EXCL_STOP */
183         }
184
185         *db = NULL;
186
187         return SHORTCUT_ERROR_NONE;
188 }
189
190 /* LCOV_EXCL_START */
191 /*!
192  * \note this function will returns allocated(heap) string
193  */
194 static inline int _get_i18n_name(sqlite3 *handle, const char *lang, int id, char **name, char **icon)
195 {
196         sqlite3_stmt *stmt;
197         static const char *query = "SELECT name, icon FROM shortcut_name WHERE id = ? AND lang = ? COLLATE NOCASE";
198         const unsigned char *_name;
199         const unsigned char *_icon;
200         int ret = 0;
201         int status;
202
203         status = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
204         if (status != SQLITE_OK) {
205                 SHORTCUT_ERR("Failed to prepare stmt: %s\n", sqlite3_errmsg(handle));
206                 return -EFAULT;
207         }
208
209         status = sqlite3_bind_int(stmt, 1, id);
210         if (status != SQLITE_OK) {
211                 SHORTCUT_ERR("Failed to bind id: %s\n", sqlite3_errmsg(handle));
212                 ret = -EFAULT;
213                 goto out;
214         }
215
216         status = sqlite3_bind_text(stmt, 2, lang, -1, SQLITE_TRANSIENT);
217         if (status != SQLITE_OK) {
218                 SHORTCUT_ERR("Failed to bind lang: %s\n", sqlite3_errmsg(handle));
219                 ret = -EFAULT;
220                 goto out;
221         }
222
223         SHORTCUT_DBG("id: %d, lang: %s\n", id, lang);
224         if (SQLITE_ROW != sqlite3_step(stmt)) {
225                 SHORTCUT_ERR("Failed to do step: %s\n", sqlite3_errmsg(handle));
226                 ret = -ENOENT;
227                 goto out;
228         }
229
230         _name = sqlite3_column_text(stmt, 0);
231         if (name) {
232                 if (_name && strlen((const char *)_name)) {
233                         *name = strdup((const char *)_name);
234                         if (!*name) {
235                                 SHORTCUT_ERR("strdup: %d\n", errno);
236                                 ret = -ENOMEM;
237                                 goto out;
238                         }
239                 } else {
240                         *name = NULL;
241                 }
242         }
243
244         _icon = sqlite3_column_text(stmt, 1);
245         if (icon) {
246                 if (_icon && strlen((const char *)_icon)) {
247                         *icon = strdup((const char *)_icon);
248                         if (!*icon) {
249                                 SHORTCUT_ERR("strdup: %d\n", errno);
250                                 ret = -ENOMEM;
251                                 if (name && *name) {
252                                         free(*name);
253                                         *name = NULL;
254                                 }
255
256                                 goto out;
257                         }
258                 } else {
259                         *icon = NULL;
260                 }
261         }
262
263 out:
264         sqlite3_reset(stmt);
265         sqlite3_clear_bindings(stmt);
266         sqlite3_finalize(stmt);
267         return ret;
268 }
269 /* LCOV_EXCL_STOP */
270
271 static inline char *_cur_locale(void)
272 {
273         char *language;
274         char *ptr;
275
276         language = vconf_get_str(VCONFKEY_LANGSET);
277         if (language) {
278                 ptr = language;
279                 while (*ptr) {
280                         if (*ptr == '.') {
281                                 *ptr = '\0';
282                                 break;
283                         }
284
285                         if (*ptr == '_')
286                                 *ptr = '-';
287
288                         ptr++;
289                 }
290         } else {
291                 language = strdup("en-us");
292                 if (!language)
293                         SHORTCUT_ERR("Heap: %d\n", errno);
294         }
295
296         return language;
297 }
298
299 EAPI int shortcut_db_get_list(const char *package_name, GList **shortcut_list)
300 {
301         sqlite3_stmt *stmt;
302         sqlite3 *handle = NULL;
303         const char *query;
304         const unsigned char *name;
305         char *i18n_name = NULL;
306         char *i18n_icon = NULL;
307         const unsigned char *extra_data;
308         const unsigned char *extra_key;
309         const unsigned char *icon;
310         shortcut_info_s *shortcut;
311         int id;
312         int ret;
313         int cnt;
314         char *language;
315
316         handle = _open_db();
317         if (!handle) {
318                 /* LCOV_EXCL_START */
319                 SHORTCUT_ERR("Failed to open a DB\n");
320                 return SHORTCUT_ERROR_IO_ERROR;
321                 /* LCOV_EXCL_STOP */
322         }
323
324         language = _cur_locale();
325         if (!language) {
326                 /* LCOV_EXCL_START */
327                 SHORTCUT_ERR("Locale is not valid\n");
328                 _close_db(&handle);
329                 return SHORTCUT_ERROR_FAULT;
330                 /* LCOV_EXCL_STOP */
331         }
332
333         if (package_name) {
334                 query = "SELECT id, appid, name, extra_key, extra_data, icon FROM shortcut_service WHERE appid = ?";
335                 ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
336                 if (ret != SQLITE_OK) {
337                         /* LCOV_EXCL_START */
338                         SHORTCUT_ERR("prepare: %s\n", sqlite3_errmsg(handle));
339                         free(language);
340                         _close_db(&handle);
341                         return SHORTCUT_ERROR_IO_ERROR;
342                         /* LCOV_EXCL_STOP */
343                 }
344
345                 ret = sqlite3_bind_text(stmt, 1, package_name, -1, SQLITE_TRANSIENT);
346                 if (ret != SQLITE_OK) {
347                         /* LCOV_EXCL_START */
348                         SHORTCUT_ERR("bind text: %s\n", sqlite3_errmsg(handle));
349                         sqlite3_finalize(stmt);
350                         free(language);
351                         _close_db(&handle);
352                         return SHORTCUT_ERROR_IO_ERROR;
353                         /* LCOV_EXCL_STOP */
354                 }
355         } else {
356                 query = "SELECT id, appid, name, extra_key, extra_data, icon FROM shortcut_service";
357                 ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
358                 if (ret != SQLITE_OK) {
359                         /* LCOV_EXCL_START */
360                         SHORTCUT_ERR("prepare: %s\n", sqlite3_errmsg(handle));
361                         free(language);
362                         _close_db(&handle);
363                         return SHORTCUT_ERROR_IO_ERROR;
364                         /* LCOV_EXCL_STOP */
365                 }
366         }
367
368         cnt = 0;
369         *shortcut_list = NULL;
370         while (SQLITE_ROW == sqlite3_step(stmt)) {
371                 /* LCOV_EXCL_START */
372                 id = sqlite3_column_int(stmt, 0);
373
374                 package_name = (const char *)sqlite3_column_text(stmt, 1);
375                 if (!package_name) {
376                         LOGE("Failed to get package name\n");
377                         continue;
378                 }
379
380                 name = sqlite3_column_text(stmt, 2);
381                 if (!name) {
382                         LOGE("Failed to get name\n");
383                         continue;
384                 }
385
386                 extra_key = sqlite3_column_text(stmt, 3);
387                 if (!extra_key) {
388                         LOGE("Failed to get service\n");
389                         continue;
390                 }
391
392                 extra_data = sqlite3_column_text(stmt, 4);
393                 if (!extra_data) {
394                         LOGE("Failed to get service\n");
395                         continue;
396                 }
397
398                 icon = sqlite3_column_text(stmt, 5);
399                 if (!icon) {
400                         LOGE("Failed to get icon\n");
401                         continue;
402                 }
403
404                 /*!
405                  * \todo
406                  * Implement the "GET LOCALE" code
407                  */
408                 /* if (get_i18n_name(language, id, &i18n_name, &i18n_icon) < 0) { */
409                         /* Okay, we can't manage this. just use the fallback string */
410                 /* } */
411                 _get_i18n_name(handle, language, id, &i18n_name, &i18n_icon);
412
413                 cnt++;
414                 shortcut = (shortcut_info_s *)calloc(sizeof(shortcut_info_s), 1);
415                 if (shortcut == NULL) {
416                         free(i18n_name);
417                         free(i18n_icon);
418                         break;
419                 }
420                 shortcut->package_name = strdup(package_name);
421                 shortcut->icon = strdup((i18n_icon != NULL ? i18n_icon : (char *)icon));
422                 shortcut->name = strdup((i18n_name != NULL ? i18n_name : (char *)name));
423                 shortcut->extra_key = strdup((char *)extra_key);
424                 shortcut->extra_data = strdup((char *)extra_data);
425                 *shortcut_list = g_list_append(*shortcut_list, shortcut);
426
427                 if (i18n_name) {
428                         free(i18n_name);
429                         i18n_name = NULL;
430                 }
431
432                 if (i18n_icon) {
433                         free(i18n_icon);
434                         i18n_icon = NULL;
435                 }
436                 /* LCOV_EXCL_STOP */
437         }
438
439         sqlite3_reset(stmt);
440         sqlite3_clear_bindings(stmt);
441         sqlite3_finalize(stmt);
442         free(language);
443         _close_db(&handle);
444
445         return cnt;
446 }