ea1cc3c879c4c6935dec1bc38689e15ef8c3db33
[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(sqlite3 *db)
75 {
76         int ret = SHORTCUT_ERROR_NONE;
77         int sql_ret;
78         char *errmsg = NULL;
79
80         SHORTCUT_INFO("DB is corrupted, start to recover corrupted db");
81         if (db)
82                 sqlite3_close(db);
83         unlink(_db_path);
84
85         sql_ret = sqlite3_open_v2(_db_path, &db,
86                                 SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE,
87                                 NULL);
88         if (sql_ret != SQLITE_OK) {
89                 SHORTCUT_ERR("Failed to open db[%d]", sql_ret);
90                 unlink(_db_path);
91                 ret = SHORTCUT_ERROR_IO_ERROR;
92                 goto out;
93         }
94
95         sql_ret = sqlite3_exec(db, QUERY_CREATE_SHORTCUT_TABLE, NULL, NULL, &errmsg);
96         if (sql_ret != SQLITE_OK) {
97                 SHORTCUT_ERR("Failed to exec query[%d][%s]", sql_ret, errmsg);
98                 ret = SHORTCUT_ERROR_IO_ERROR;
99         }
100
101 out:
102         if (errmsg)
103                 sqlite3_free(errmsg);
104
105         return ret;
106 }
107 /* LCOV_EXCL_STOP */
108
109 /* LCOV_EXCL_START */
110 EAPI int shortcut_db_init(void)
111 {
112         int ret = SHORTCUT_ERROR_NONE;
113         int sql_ret;
114         sqlite3 *db = NULL;
115         char *errmsg = NULL;
116
117         sql_ret = sqlite3_open_v2(_db_path, &db,
118                         SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, NULL);
119         if (sql_ret != SQLITE_OK) {
120                 SHORTCUT_ERR("Failed to open db[%d]", sql_ret);
121                 ret = SHORTCUT_ERROR_IO_ERROR;
122                 goto out;
123         }
124
125         sql_ret = sqlite3_exec(db, QUERY_CREATE_SHORTCUT_TABLE,
126                                 NULL, NULL, &errmsg);
127         if (sql_ret != SQLITE_OK) {
128                 SHORTCUT_ERR("Failed to exec query [%d][%s]", sql_ret, errmsg);
129                 ret = SHORTCUT_ERROR_IO_ERROR;
130                 goto out;
131         }
132
133         sql_ret = sqlite3_exec(db, "PRAGMA integrity_check",
134                                __check_integrity_cb, NULL, &errmsg);
135         if (sql_ret != SQLITE_OK || is_db_corrupted) {
136                 SHORTCUT_ERR("Failed to exec query[%d][%s]", sql_ret, errmsg);
137                 ret = SHORTCUT_ERROR_IO_ERROR;
138         }
139
140 out:
141         if (sql_ret == SQLITE_CORRUPT || sql_ret == SQLITE_NOTADB || is_db_corrupted)
142                 ret = __recover_corrupted_db(db);
143         if (errmsg)
144                 sqlite3_free(errmsg);
145         if (db)
146                 sqlite3_close(db);
147
148         return ret;
149 }
150 /* LCOV_EXCL_STOP */
151
152 static sqlite3 *_open_db(void)
153 {
154         int ret;
155         const char *dbfile = _db_path;
156         sqlite3 *db = NULL;
157
158         ret = db_util_open(dbfile, &db, 0);
159         if (ret != SQLITE_OK) {
160                 /* LCOV_EXCL_START */
161                 SHORTCUT_DBG("Failed to open a %s\n", dbfile);
162                 return NULL;
163                 /* LCOV_EXCL_STOP */
164         }
165
166         return db;
167 }
168
169 static int _close_db(sqlite3 **db)
170 {
171         int ret = 0;
172
173         if (db == NULL || *db == NULL)
174                 return SHORTCUT_ERROR_INVALID_PARAMETER;
175
176         ret = db_util_close(*db);
177         if (ret != SQLITE_OK) {
178                 /* LCOV_EXCL_START */
179                 SHORTCUT_DBG("DB close error(%d)", ret);
180                 return SHORTCUT_ERROR_IO_ERROR;
181                 /* LCOV_EXCL_STOP */
182         }
183
184         *db = NULL;
185
186         return SHORTCUT_ERROR_NONE;
187 }
188
189 /* LCOV_EXCL_START */
190 /*!
191  * \note this function will returns allocated(heap) string
192  */
193 static inline int _get_i18n_name(sqlite3 *handle, const char *lang, int id, char **name, char **icon)
194 {
195         sqlite3_stmt *stmt;
196         static const char *query = "SELECT name, icon FROM shortcut_name WHERE id = ? AND lang = ? COLLATE NOCASE";
197         const unsigned char *_name;
198         const unsigned char *_icon;
199         int ret = 0;
200         int status;
201
202         status = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
203         if (status != SQLITE_OK) {
204                 SHORTCUT_ERR("Failed to prepare stmt: %s\n", sqlite3_errmsg(handle));
205                 return -EFAULT;
206         }
207
208         status = sqlite3_bind_int(stmt, 1, id);
209         if (status != SQLITE_OK) {
210                 SHORTCUT_ERR("Failed to bind id: %s\n", sqlite3_errmsg(handle));
211                 ret = -EFAULT;
212                 goto out;
213         }
214
215         status = sqlite3_bind_text(stmt, 2, lang, -1, SQLITE_TRANSIENT);
216         if (status != SQLITE_OK) {
217                 SHORTCUT_ERR("Failed to bind lang: %s\n", sqlite3_errmsg(handle));
218                 ret = -EFAULT;
219                 goto out;
220         }
221
222         SHORTCUT_DBG("id: %d, lang: %s\n", id, lang);
223         if (SQLITE_ROW != sqlite3_step(stmt)) {
224                 SHORTCUT_ERR("Failed to do step: %s\n", sqlite3_errmsg(handle));
225                 ret = -ENOENT;
226                 goto out;
227         }
228
229         _name = sqlite3_column_text(stmt, 0);
230         if (name) {
231                 if (_name && strlen((const char *)_name)) {
232                         *name = strdup((const char *)_name);
233                         if (!*name) {
234                                 SHORTCUT_ERR("strdup: %d\n", errno);
235                                 ret = -ENOMEM;
236                                 goto out;
237                         }
238                 } else {
239                         *name = NULL;
240                 }
241         }
242
243         _icon = sqlite3_column_text(stmt, 1);
244         if (icon) {
245                 if (_icon && strlen((const char *)_icon)) {
246                         *icon = strdup((const char *)_icon);
247                         if (!*icon) {
248                                 SHORTCUT_ERR("strdup: %d\n", errno);
249                                 ret = -ENOMEM;
250                                 if (name && *name) {
251                                         free(*name);
252                                         *name = NULL;
253                                 }
254
255                                 goto out;
256                         }
257                 } else {
258                         *icon = NULL;
259                 }
260         }
261
262 out:
263         sqlite3_reset(stmt);
264         sqlite3_clear_bindings(stmt);
265         sqlite3_finalize(stmt);
266         return ret;
267 }
268 /* LCOV_EXCL_STOP */
269
270 static inline char *_cur_locale(void)
271 {
272         char *language;
273         char *ptr;
274
275         language = vconf_get_str(VCONFKEY_LANGSET);
276         if (language) {
277                 ptr = language;
278                 while (*ptr) {
279                         if (*ptr == '.') {
280                                 *ptr = '\0';
281                                 break;
282                         }
283
284                         if (*ptr == '_')
285                                 *ptr = '-';
286
287                         ptr++;
288                 }
289         } else {
290                 language = strdup("en-us");
291                 if (!language)
292                         SHORTCUT_ERR("Heap: %d\n", errno);
293         }
294
295         return language;
296 }
297
298 EAPI int shortcut_db_get_list(const char *package_name, GList **shortcut_list)
299 {
300         sqlite3_stmt *stmt;
301         sqlite3 *handle = NULL;
302         const char *query;
303         const unsigned char *name;
304         char *i18n_name = NULL;
305         char *i18n_icon = NULL;
306         const unsigned char *extra_data;
307         const unsigned char *extra_key;
308         const unsigned char *icon;
309         shortcut_info_s *shortcut;
310         int id;
311         int ret;
312         int cnt;
313         char *language;
314
315         handle = _open_db();
316         if (!handle) {
317                 /* LCOV_EXCL_START */
318                 SHORTCUT_ERR("Failed to open a DB\n");
319                 return SHORTCUT_ERROR_IO_ERROR;
320                 /* LCOV_EXCL_STOP */
321         }
322
323         language = _cur_locale();
324         if (!language) {
325                 /* LCOV_EXCL_START */
326                 SHORTCUT_ERR("Locale is not valid\n");
327                 _close_db(&handle);
328                 return SHORTCUT_ERROR_FAULT;
329                 /* LCOV_EXCL_STOP */
330         }
331
332         if (package_name) {
333                 query = "SELECT id, appid, name, extra_key, extra_data, icon FROM shortcut_service WHERE appid = ?";
334                 ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
335                 if (ret != SQLITE_OK) {
336                         /* LCOV_EXCL_START */
337                         SHORTCUT_ERR("prepare: %s\n", sqlite3_errmsg(handle));
338                         free(language);
339                         _close_db(&handle);
340                         return SHORTCUT_ERROR_IO_ERROR;
341                         /* LCOV_EXCL_STOP */
342                 }
343
344                 ret = sqlite3_bind_text(stmt, 1, package_name, -1, SQLITE_TRANSIENT);
345                 if (ret != SQLITE_OK) {
346                         /* LCOV_EXCL_START */
347                         SHORTCUT_ERR("bind text: %s\n", sqlite3_errmsg(handle));
348                         sqlite3_finalize(stmt);
349                         free(language);
350                         _close_db(&handle);
351                         return SHORTCUT_ERROR_IO_ERROR;
352                         /* LCOV_EXCL_STOP */
353                 }
354         } else {
355                 query = "SELECT id, appid, name, extra_key, extra_data, icon FROM shortcut_service";
356                 ret = sqlite3_prepare_v2(handle, query, -1, &stmt, NULL);
357                 if (ret != SQLITE_OK) {
358                         /* LCOV_EXCL_START */
359                         SHORTCUT_ERR("prepare: %s\n", sqlite3_errmsg(handle));
360                         free(language);
361                         _close_db(&handle);
362                         return SHORTCUT_ERROR_IO_ERROR;
363                         /* LCOV_EXCL_STOP */
364                 }
365         }
366
367         cnt = 0;
368         *shortcut_list = NULL;
369         while (SQLITE_ROW == sqlite3_step(stmt)) {
370                 /* LCOV_EXCL_START */
371                 id = sqlite3_column_int(stmt, 0);
372
373                 package_name = (const char *)sqlite3_column_text(stmt, 1);
374                 if (!package_name) {
375                         LOGE("Failed to get package name\n");
376                         continue;
377                 }
378
379                 name = sqlite3_column_text(stmt, 2);
380                 if (!name) {
381                         LOGE("Failed to get name\n");
382                         continue;
383                 }
384
385                 extra_key = sqlite3_column_text(stmt, 3);
386                 if (!extra_key) {
387                         LOGE("Failed to get service\n");
388                         continue;
389                 }
390
391                 extra_data = sqlite3_column_text(stmt, 4);
392                 if (!extra_data) {
393                         LOGE("Failed to get service\n");
394                         continue;
395                 }
396
397                 icon = sqlite3_column_text(stmt, 5);
398                 if (!icon) {
399                         LOGE("Failed to get icon\n");
400                         continue;
401                 }
402
403                 /*!
404                  * \todo
405                  * Implement the "GET LOCALE" code
406                  */
407                 /* if (get_i18n_name(language, id, &i18n_name, &i18n_icon) < 0) { */
408                         /* Okay, we can't manage this. just use the fallback string */
409                 /* } */
410                 _get_i18n_name(handle, language, id, &i18n_name, &i18n_icon);
411
412                 cnt++;
413                 shortcut = (shortcut_info_s *)calloc(sizeof(shortcut_info_s), 1);
414                 if (shortcut == NULL) {
415                         free(i18n_name);
416                         free(i18n_icon);
417                         break;
418                 }
419                 shortcut->package_name = strdup(package_name);
420                 shortcut->icon = strdup((i18n_icon != NULL ? i18n_icon : (char *)icon));
421                 shortcut->name = strdup((i18n_name != NULL ? i18n_name : (char *)name));
422                 shortcut->extra_key = strdup((char *)extra_key);
423                 shortcut->extra_data = strdup((char *)extra_data);
424                 *shortcut_list = g_list_append(*shortcut_list, shortcut);
425
426                 if (i18n_name) {
427                         free(i18n_name);
428                         i18n_name = NULL;
429                 }
430
431                 if (i18n_icon) {
432                         free(i18n_icon);
433                         i18n_icon = NULL;
434                 }
435                 /* LCOV_EXCL_STOP */
436         }
437
438         sqlite3_reset(stmt);
439         sqlite3_clear_bindings(stmt);
440         sqlite3_finalize(stmt);
441         free(language);
442         _close_db(&handle);
443
444         return cnt;
445 }