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