Fix build error (set but unused variables)
[platform/core/appfw/shortcut.git] / lib / src / main.c
1 /*
2  * Copyright (c) 2000 - 2013 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 <stdlib.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <sys/socket.h>
24 #include <sys/ioctl.h>
25 #include <libgen.h>
26
27 #include <dlog.h>
28 #include <glib.h>
29 #include <db-util.h>
30 #include <vconf.h>
31 #include <vconf-keys.h>
32
33 #include <packet.h>
34 #include <com-core.h>
35 #include <com-core_packet.h>
36
37 #include "shortcut.h"
38 #include "shortcut_internal.h"
39
40 int errno;
41
42 static struct info {
43         const char *dbfile;
44         sqlite3 *handle;
45         int server_fd;
46         int client_fd;
47         const char *socket_file;
48         struct {
49                 int (*request_cb)(const char *appid, const char *name, int type, const char *content, const char *icon, pid_t pid, double period, int allow_duplicate, void *data);
50                 void *data;
51         } server_cb;
52         int initialized;
53         int db_opened;
54         guint timer_id;
55 } s_info = {
56         .server_fd = -1,
57         .client_fd = -1,
58         .socket_file = "/tmp/.shortcut.service",
59         .dbfile = "/opt/dbspace/.shortcut_service.db",
60         .handle = NULL,
61         .initialized = 0,
62         .db_opened = 0,
63         .timer_id = 0,
64 };
65
66
67 static inline int make_connection(void);
68
69
70 static struct packet *remove_shortcut_handler(pid_t pid, int handle, const struct packet *packet)
71 {
72         const char *appid;
73         const char *name;
74         const char *content_info;
75         int ret;
76         int sender_pid;
77
78         if (!packet) {
79                 ErrPrint("Packet is NIL, maybe disconnected?\n");
80                 return NULL;
81         }
82
83         if (packet_get(packet, "isss", &sender_pid, &appid, &name, &content_info) != 4) {
84                 ErrPrint("Invalid apcket\n");
85                 return NULL;
86         }
87
88         DbgPrint("appid[%s], name[%s], content_info[%s]\n", appid, name, content_info);
89
90         if (s_info.server_cb.request_cb) {
91                 ret = s_info.server_cb.request_cb(appid, name, SHORTCUT_REMOVE, content_info, NULL, sender_pid, -1.0f, 0, s_info.server_cb.data);
92         } else {
93                 ret = SHORTCUT_ERROR_UNSUPPORTED;
94         }
95
96         return packet_create_reply(packet, "i", ret);
97 }
98
99
100
101 static struct packet *remove_livebox_handler(pid_t pid, int handle, const struct packet *packet)
102 {
103         const char *appid;
104         const char *name;
105         int ret;
106         int sender_pid;
107
108         if (!packet) {
109                 ErrPrint("PAcket is NIL, maybe disconnected?\n");
110                 return NULL;
111         }
112
113         if (packet_get(packet, "iss", &sender_pid, &appid, &name) != 3) {
114                 ErrPrint("Invalid packet\n");
115                 return NULL;
116         }
117
118         DbgPrint("appid[%s], name[%s]\n", appid, name);
119
120         if (s_info.server_cb.request_cb) {
121                 ret = s_info.server_cb.request_cb(appid, name, LIVEBOX_REMOVE, NULL, NULL, sender_pid, -1.0f, 0, s_info.server_cb.data);
122         } else {
123                 ret = SHORTCUT_ERROR_UNSUPPORTED;
124         }
125
126         return packet_create_reply(packet, "i", ret);
127 }
128
129
130
131 static struct packet *add_shortcut_handler(pid_t pid, int handle, const struct packet *packet)
132 {
133         const char *appid;
134         const char *name;
135         int type;
136         const char *content;
137         const char *icon;
138         int allow_duplicate;
139         int ret;
140         int sender_pid;
141
142         if (!packet) {
143                 return NULL;
144         }
145
146         if (packet_get(packet, "ississi", &sender_pid, &appid, &name, &type, &content, &icon, &allow_duplicate) != 7) {
147                 ErrPrint("Invalid packet\n");
148                 return NULL;
149         }
150
151         DbgPrint("appid[%s], name[%s], type[0x%x], content[%s], icon[%s] allow_duplicate[%d]\n", appid, name, type, content, icon, allow_duplicate);
152
153         if (s_info.server_cb.request_cb) {
154                 ret = s_info.server_cb.request_cb(appid, name, type, content, icon, sender_pid, -1.0f, allow_duplicate, s_info.server_cb.data);
155         } else {
156                 ret = SHORTCUT_ERROR_UNSUPPORTED;
157         }
158
159         return packet_create_reply(packet, "i", ret);
160 }
161
162
163
164 static struct packet *add_livebox_handler(pid_t pid, int handle, const struct packet *packet)
165 {
166         const char *appid;
167         const char *name;
168         int type;
169         const char *content;
170         const char *icon;
171         double period;
172         int allow_duplicate;
173         int ret;
174         int sender_pid;
175
176         if (!packet) {
177                 return NULL;
178         }
179
180         if (packet_get(packet, "ississdi", &sender_pid, &appid, &name, &type, &content, &icon, &period, &allow_duplicate) != 8) {
181                 ErrPrint("Invalid packet\n");
182                 return NULL;
183         }
184
185         DbgPrint("appid[%s], name[%s], type[0x%x], content[%s], icon[%s], period[%lf], allow_duplicate[%d]\n", appid, name, type, content, icon, period, allow_duplicate);
186
187         if (s_info.server_cb.request_cb) {
188                 ret = s_info.server_cb.request_cb(appid, name, type, content, icon, sender_pid, period, allow_duplicate, s_info.server_cb.data);
189         } else {
190                 ret = 0;
191         }
192
193         return packet_create_reply(packet, "i", ret);
194 }
195
196
197
198 static void master_started_cb(keynode_t *node, void *user_data)
199 {
200         int state = 0;
201
202         if (vconf_get_bool(VCONFKEY_MASTER_STARTED, &state) < 0) {
203                 ErrPrint("Unable to get \"%s\"\n", VCONFKEY_MASTER_STARTED);
204         }
205
206         if (state == 1 && make_connection() == SHORTCUT_SUCCESS) {
207                 (void)vconf_ignore_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb);
208         }
209 }
210
211
212
213 static gboolean timeout_cb(void *data)
214 {
215         int ret;
216
217         ret = vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL);
218         if (ret < 0) {
219                 ErrPrint("Failed to add vconf for service state [%d]\n", ret);
220         } else {
221                 DbgPrint("vconf is registered\n");
222         }
223
224         master_started_cb(NULL, NULL);
225
226         s_info.timer_id = 0;
227         return FALSE;
228 }
229
230
231
232 static int disconnected_cb(int handle, void *data)
233 {
234         if (s_info.client_fd == handle) {
235                 s_info.client_fd = SHORTCUT_ERROR_INVALID;
236                 return 0;
237         }
238
239         if (s_info.server_fd == handle) {
240                 if (!s_info.timer_id) {
241                         s_info.server_fd = SHORTCUT_ERROR_INVALID;
242                         s_info.timer_id = g_timeout_add(1000, timeout_cb, NULL);
243                         if (!s_info.timer_id) {
244                                 ErrPrint("Unable to add timer\n");
245                         }
246                 }
247                 return 0;
248         }
249
250         return 0;
251 }
252
253
254
255 static inline int make_connection(void)
256 {
257         int ret;
258         struct packet *packet;
259         static struct method service_table[] = {
260                 {
261                         .cmd = "add_shortcut",
262                         .handler = add_shortcut_handler,
263                 },
264                 {
265                         .cmd = "add_livebox",
266                         .handler = add_livebox_handler,
267                 },
268                 {
269                         .cmd = "rm_shortcut",
270                         .handler = remove_shortcut_handler,
271                 },
272                 {
273                         .cmd = "rm_livebox",
274                         .handler = remove_livebox_handler,
275                 },
276                 {
277                         .cmd = NULL,
278                         .handler = NULL,
279                 },
280         };
281
282         if (s_info.initialized == 0) {
283                 s_info.initialized = 1;
284                 com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
285         }
286
287         s_info.server_fd = com_core_packet_client_init(s_info.socket_file, 0, service_table);
288         if (s_info.server_fd < 0) {
289                 ErrPrint("Failed to make a connection to the master\n");
290                 return SHORTCUT_ERROR_COMM;
291         }
292
293         packet = packet_create_noack("service_register", "");
294         if (!packet) {
295                 ErrPrint("Failed to build a packet\n");
296                 return SHORTCUT_ERROR_FAULT;
297         }
298
299         ret = com_core_packet_send_only(s_info.server_fd, packet);
300         DbgPrint("Service register sent: %d\n", ret);
301         packet_destroy(packet);
302         if (ret != 0) {
303                 com_core_packet_client_fini(s_info.server_fd);
304                 s_info.server_fd = -1;
305                 ret = SHORTCUT_ERROR_COMM;
306         } else {
307                 ret = SHORTCUT_SUCCESS;
308         }
309
310         DbgPrint("Server FD: %d\n", s_info.server_fd);
311         return ret;
312 }
313
314
315
316 EAPI int shortcut_set_request_cb(request_cb_t request_cb, void *data)
317 {
318         s_info.server_cb.request_cb = request_cb;
319         s_info.server_cb.data = data;
320
321         if (s_info.server_fd < 0) {
322                 int ret;
323
324                 ret = vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL);
325                 if (ret < 0) {
326                         ErrPrint("Failed to add vconf for service state [%d]\n", ret);
327                 } else {
328                         DbgPrint("vconf is registered\n");
329                 }
330
331                 master_started_cb(NULL, NULL);
332         }
333
334         return SHORTCUT_SUCCESS;
335 }
336
337
338
339 struct result_cb_item {
340         result_cb_t result_cb;
341         void *data;
342 };
343
344
345
346 static int shortcut_send_cb(pid_t pid, int handle, const struct packet *packet, void *data)
347 {
348         struct result_cb_item *item = data;
349         int ret;
350
351         if (!packet) {
352                 ErrPrint("Packet is not valid\n");
353                 ret = SHORTCUT_ERROR_FAULT;
354         } else if (packet_get(packet, "i", &ret) != 1) {
355                 ErrPrint("Packet is not valid\n");
356                 ret = SHORTCUT_ERROR_INVALID;
357         }
358
359         if (item->result_cb) {
360                 ret = item->result_cb(ret, pid, item->data);
361         } else {
362                 ret = SHORTCUT_SUCCESS;
363         }
364         free(item);
365         return ret;
366 }
367
368
369
370 EAPI int add_to_home_remove_shortcut(const char *appid, const char *name, const char *content_info, result_cb_t result_cb, void *data)
371 {
372         struct packet *packet;
373         struct result_cb_item *item;
374         int ret;
375
376         if (!appid || !name) {
377                 ErrPrint("Invalid argument\n");
378                 return SHORTCUT_ERROR_INVALID;
379         }
380
381         if (!s_info.initialized) {
382                 s_info.initialized = 1;
383                 com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
384         }
385
386         if (s_info.client_fd < 0) {
387                 static struct method service_table[] = {
388                         {
389                                 .cmd = NULL,
390                                 .handler = NULL,
391                         },
392                 };
393
394                 s_info.client_fd = com_core_packet_client_init(s_info.socket_file, 0, service_table);
395                 if (s_info.client_fd < 0) {
396                         ErrPrint("Failed to make connection\n");
397                         return SHORTCUT_ERROR_COMM;
398                 }
399         }
400
401         item = malloc(sizeof(*item));
402         if (!item) {
403                 ErrPrint("Heap: %s\n", strerror(errno));
404                 return SHORTCUT_ERROR_MEMORY;
405         }
406
407         item->result_cb = result_cb;
408         item->data = data;
409
410         packet = packet_create("rm_shortcut", "isss", getpid(), appid, name, content_info);
411         if (!packet) {
412                 ErrPrint("Failed to build a packet\n");
413                 free(item);
414                 return SHORTCUT_ERROR_FAULT;
415         }
416
417         ret = com_core_packet_async_send(s_info.client_fd, packet, 0.0f, shortcut_send_cb, item);
418         if (ret < 0) {
419                 packet_destroy(packet);
420                 free(item);
421                 com_core_packet_client_fini(s_info.client_fd);
422                 s_info.client_fd = SHORTCUT_ERROR_INVALID;
423                 return SHORTCUT_ERROR_COMM;
424         }
425
426         return SHORTCUT_SUCCESS;
427 }
428
429
430
431 EAPI int add_to_home_remove_livebox(const char *appid, const char *name, result_cb_t result_cb, void *data)
432 {
433         struct packet *packet;
434         struct result_cb_item *item;
435         int ret;
436
437         if (!appid || !name) {
438                 ErrPrint("Invalid argument\n");
439                 return SHORTCUT_ERROR_INVALID;
440         }
441
442         if (!s_info.initialized) {
443                 s_info.initialized = 1;
444                 com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
445         }
446
447         if (s_info.client_fd < 0) {
448                 static struct method service_table[] = {
449                         {
450                                 .cmd = NULL,
451                                 .handler = NULL,
452                         },
453                 };
454
455
456                 s_info.client_fd = com_core_packet_client_init(s_info.socket_file, 0, service_table);
457                 if (s_info.client_fd < 0) {
458                         ErrPrint("Failed to make connection\n");
459                         return SHORTCUT_ERROR_COMM;
460                 }
461         }
462
463         item = malloc(sizeof(*item));
464         if (!item) {
465                 ErrPrint("Heap: %s\n", strerror(errno));
466                 return SHORTCUT_ERROR_MEMORY;
467         }
468
469         item->result_cb = result_cb;
470         item->data = data;
471
472         packet = packet_create("rm_livebox", "iss", getpid(), appid, name);
473         if (!packet) {
474                 ErrPrint("Failed to build a packet\n");
475                 free(item);
476                 return SHORTCUT_ERROR_FAULT;
477         }
478
479         ret = com_core_packet_async_send(s_info.client_fd, packet, 0.0f, shortcut_send_cb, item);
480         if (ret < 0) {
481                 packet_destroy(packet);
482                 free(item);
483                 com_core_packet_client_fini(s_info.client_fd);
484                 s_info.client_fd = SHORTCUT_ERROR_INVALID;
485                 return SHORTCUT_ERROR_COMM;
486         }
487
488         return SHORTCUT_SUCCESS;
489 }
490
491
492
493 EAPI int add_to_home_shortcut(const char *appid, const char *name, int type, const char *content, const char *icon, int allow_duplicate, result_cb_t result_cb, void *data)
494 {
495         struct packet *packet;
496         struct result_cb_item *item;
497         int ret;
498
499         if (ADD_TO_HOME_IS_LIVEBOX(type)) {
500                 ErrPrint("Invalid type used for adding a shortcut\n");
501         }
502
503         if (!s_info.initialized) {
504                 s_info.initialized = 1;
505                 com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
506         }
507
508         if (s_info.client_fd < 0) {
509                 static struct method service_table[] = {
510                         {
511                                 .cmd = NULL,
512                                 .handler = NULL,
513                         },
514                 };
515
516                 s_info.client_fd = com_core_packet_client_init(s_info.socket_file, 0, service_table);
517                 if (s_info.client_fd < 0) {
518                         ErrPrint("Failed to make connection\n");
519                         return SHORTCUT_ERROR_COMM;
520                 }
521         }
522
523         item = malloc(sizeof(*item));
524         if (!item) {
525                 ErrPrint("Heap: %s\n", strerror(errno));
526                 return SHORTCUT_ERROR_MEMORY;
527         }
528
529         item->result_cb = result_cb;
530         item->data = data;
531
532         if (!appid) {
533                 appid = "";
534         }
535
536         if (!name) {
537                 name = "";
538         }
539
540         if (!content) {
541                 content = "";
542         }
543
544         if (!icon) {
545                 icon = "";
546         }
547
548         packet = packet_create("add_shortcut", "ississi", getpid(), appid, name, type, content, icon, allow_duplicate);
549         if (!packet) {
550                 ErrPrint("Failed to build a packet\n");
551                 free(item);
552                 return SHORTCUT_ERROR_FAULT;
553         }
554
555         ret = com_core_packet_async_send(s_info.client_fd, packet, 0.0f, shortcut_send_cb, item);
556         if (ret < 0) {
557                 packet_destroy(packet);
558                 free(item);
559                 com_core_packet_client_fini(s_info.client_fd);
560                 s_info.client_fd = SHORTCUT_ERROR_INVALID;
561                 return SHORTCUT_ERROR_COMM;
562         }
563
564         return SHORTCUT_SUCCESS;
565 }
566
567
568
569 EAPI int add_to_home_livebox(const char *appid, const char *name, int type, const char *content, const char *icon, double period, int allow_duplicate, result_cb_t result_cb, void *data)
570 {
571         struct packet *packet;
572         struct result_cb_item *item;
573         int ret;
574
575         if (!ADD_TO_HOME_IS_LIVEBOX(type)) {
576                 ErrPrint("Invalid type is used for adding a livebox\n");
577         }
578
579         if (!s_info.initialized) {
580                 s_info.initialized = 1;
581                 com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
582         }
583
584         if (s_info.client_fd < 0) {
585                 static struct method service_table[] = {
586                         {
587                                 .cmd = NULL,
588                                 .handler = NULL,
589                         },
590                 };
591
592                 s_info.client_fd = com_core_packet_client_init(s_info.socket_file, 0, service_table);
593                 if (s_info.client_fd < 0) {
594                         return SHORTCUT_ERROR_COMM;
595                 }
596         }
597
598         item = malloc(sizeof(*item));
599         if (!item) {
600                 ErrPrint("Heap: %s\n", strerror(errno));
601                 return SHORTCUT_ERROR_MEMORY;
602         }
603
604         item->result_cb = result_cb;
605         item->data = data;
606
607         packet = packet_create("add_livebox", "ississdi", getpid(), appid, name, type, content, icon, period, allow_duplicate);
608         if (!packet) {
609                 ErrPrint("Failed to build a packet\n");
610                 free(item);
611                 return SHORTCUT_ERROR_FAULT;
612         }
613
614         ret = com_core_packet_async_send(s_info.client_fd, packet, 0.0f, shortcut_send_cb, item);
615         if (ret < 0) {
616                 packet_destroy(packet);
617                 free(item);
618                 com_core_packet_client_fini(s_info.client_fd);
619                 s_info.client_fd = SHORTCUT_ERROR_INVALID;
620                 return SHORTCUT_ERROR_COMM;
621         }
622
623         return SHORTCUT_SUCCESS;
624 }
625
626
627 static inline int open_db(void)
628 {
629         int ret;
630
631         ret = db_util_open(s_info.dbfile, &s_info.handle, DB_UTIL_REGISTER_HOOK_METHOD);
632         if (ret != SQLITE_OK) {
633                 DbgPrint("Failed to open a %s\n", s_info.dbfile);
634                 return SHORTCUT_ERROR_IO;
635         }
636
637         return SHORTCUT_SUCCESS;
638 }
639
640
641
642 /*!
643  * \note this function will returns allocated(heap) string
644  */
645 static inline int get_i18n_name(const char *lang, int id, char **name, char **icon)
646 {
647         sqlite3_stmt *stmt;
648         static const char *query = "SELECT name, icon FROM shortcut_name WHERE id = ? AND lang = ? COLLATE NOCASE";
649         const unsigned char *_name;
650         const unsigned char *_icon;
651         int ret = 0;
652         int status;
653
654         status = sqlite3_prepare_v2(s_info.handle, query, -1, &stmt, NULL);
655         if (status != SQLITE_OK) {
656                 ErrPrint("Failed to prepare stmt: %s\n", sqlite3_errmsg(s_info.handle));
657                 return -EFAULT;
658         }
659
660         status = sqlite3_bind_int(stmt, 1, id);
661         if (status != SQLITE_OK) {
662                 ErrPrint("Failed to bind id: %s\n", sqlite3_errmsg(s_info.handle));
663                 ret = -EFAULT;
664                 goto out;
665         }
666
667         status = sqlite3_bind_text(stmt, 2, lang, -1, SQLITE_TRANSIENT);
668         if (status != SQLITE_OK) {
669                 ErrPrint("Failed to bind lang: %s\n", sqlite3_errmsg(s_info.handle));
670                 ret = -EFAULT;
671                 goto out;
672         }
673
674         DbgPrint("id: %d, lang: %s\n", id, lang);
675         if (SQLITE_ROW != sqlite3_step(stmt)) {
676                 ErrPrint("Failed to do step: %s\n", sqlite3_errmsg(s_info.handle));
677                 ret = -ENOENT;
678                 goto out;
679         }
680
681         _name = sqlite3_column_text(stmt, 0);
682         if (name) {
683                 if (_name && strlen((const char *)_name)) {
684                         *name = strdup((const char *)_name);
685                         if (!*name) {
686                                 ErrPrint("strdup: %s\n", strerror(errno));
687                                 ret = -ENOMEM;
688                                 goto out;
689                         }
690                 } else {
691                         *name = NULL;
692                 }
693         }
694
695         _icon = sqlite3_column_text(stmt, 1);
696         if (icon) {
697                 if (_icon && strlen((const char *)_icon)) {
698                         *icon = strdup((const char *)_icon);
699                         if (!*icon) {
700                                 ErrPrint("strdup: %s\n", strerror(errno));
701                                 ret = -ENOMEM;
702                                 if (name && *name) {
703                                         free(*name);
704                                 }
705                                 goto out;
706                         }
707                 } else {
708                         *icon = NULL;
709                 }
710         }
711
712 out:
713         sqlite3_reset(stmt);
714         sqlite3_clear_bindings(stmt);
715         sqlite3_finalize(stmt);
716         return ret;
717 }
718
719
720
721 static inline char *cur_locale(void)
722 {
723         char *language;
724         language = vconf_get_str(VCONFKEY_LANGSET);
725         if (language) {
726                 char *ptr;
727
728                 ptr = language;
729                 while (*ptr) {
730                         if (*ptr == '.') {
731                                 *ptr = '\0';
732                                 break;
733                         }
734
735                         if (*ptr == '_') {
736                                 *ptr = '-';
737                         }
738
739                         ptr++;
740                 }
741         } else {
742                 language = strdup("en-us");
743                 if (!language) {
744                         ErrPrint("Heap: %s\n", strerror(errno));
745                 }
746         }
747
748         return language;
749 }
750
751
752
753 /*!
754  * \note READ ONLY DB
755  */
756 EAPI int shortcut_get_list(const char *appid, int (*cb)(const char *appid, const char *icon, const char *name, const char *extra_key, const char *extra_data, void *data), void *data)
757 {
758         sqlite3_stmt *stmt;
759         const char *query;
760         const unsigned char *name;
761         char *i18n_name = NULL;
762         char *i18n_icon = NULL;
763         const unsigned char *extra_data;
764         const unsigned char *extra_key;
765         const unsigned char *icon;
766         int id;
767         int ret;
768         int cnt;
769         char *language;
770
771         if (!s_info.db_opened) {
772                 s_info.db_opened = (open_db() == 0);
773         }
774
775         if (!s_info.db_opened) {
776                 ErrPrint("Failed to open a DB\n");
777                 return SHORTCUT_ERROR_IO;
778         }
779
780         language = cur_locale();
781         if (!language) {
782                 ErrPrint("Locale is not valid\n");
783                 return SHORTCUT_ERROR_FAULT;
784         }
785
786         if (appid) {
787                 query = "SELECT id, appid, name, extra_key, extra_data, icon FROM shortcut_service WHERE appid = ?";
788                 ret = sqlite3_prepare_v2(s_info.handle, query, -1, &stmt, NULL);
789                 if (ret != SQLITE_OK) {
790                         ErrPrint("prepare: %s\n", sqlite3_errmsg(s_info.handle));
791                         free(language);
792                         return SHORTCUT_ERROR_IO;
793                 }
794
795                 ret = sqlite3_bind_text(stmt, 1, appid, -1, SQLITE_TRANSIENT);
796                 if (ret != SQLITE_OK) {
797                         ErrPrint("bind text: %s\n", sqlite3_errmsg(s_info.handle));
798                         sqlite3_finalize(stmt);
799                         free(language);
800                         return SHORTCUT_ERROR_IO;
801                 }
802         } else {
803                 query = "SELECT id, appid, name, extra_key, extra_data, icon FROM shortcut_service";
804                 ret = sqlite3_prepare_v2(s_info.handle, query, -1, &stmt, NULL);
805                 if (ret != SQLITE_OK) {
806                         ErrPrint("prepare: %s\n", sqlite3_errmsg(s_info.handle));
807                         free(language);
808                         return SHORTCUT_ERROR_IO;
809                 }
810         }
811
812         cnt = 0;
813         while (SQLITE_ROW == sqlite3_step(stmt)) {
814                 id = sqlite3_column_int(stmt, 0);
815
816                 appid = (const char *)sqlite3_column_text(stmt, 1);
817                 if (!appid) {
818                         LOGE("Failed to get package name\n");
819                         continue;
820                 }
821
822                 name = sqlite3_column_text(stmt, 2);
823                 if (!name) {
824                         LOGE("Failed to get name\n");
825                         continue;
826                 }
827
828                 extra_key = sqlite3_column_text(stmt, 3);
829                 if (!extra_key) {
830                         LOGE("Failed to get service\n");
831                         continue;
832                 }
833
834                 extra_data = sqlite3_column_text(stmt, 4);
835                 if (!extra_data) {
836                         LOGE("Failed to get service\n");
837                         continue;
838                 }
839
840                 icon = sqlite3_column_text(stmt, 5);
841                 if (!icon) {
842                         LOGE("Failed to get icon\n");
843                         continue;
844                 }
845
846                 /*!
847                  * \todo
848                  * Implement the "GET LOCALE" code
849                  */
850                 if (get_i18n_name(language, id, &i18n_name, &i18n_icon) < 0) {
851                         /* Okay, we can't manage this. just use the fallback string */
852                 }
853
854                 cnt++;
855                 if (cb(appid, (i18n_icon != NULL ? i18n_icon : (char *)icon), (i18n_name != NULL ? i18n_name : (char *)name), (char *)extra_key, (char *)extra_data, data) < 0) {
856                         free(i18n_name);
857                         break;
858                 }
859
860                 free(i18n_name);
861                 free(i18n_icon);
862         }
863
864         sqlite3_reset(stmt);
865         sqlite3_clear_bindings(stmt);
866         sqlite3_finalize(stmt);
867         free(language);
868         return cnt;
869 }
870
871 /* End of a file */