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