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