e129c3ac8ba429a9c47b766f41cd4246ba0f0f8f
[platform/core/appfw/shortcut.git] / lib / src / icon.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 #include <stdlib.h>
18 #include <errno.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <sys/socket.h>
23 #include <sys/ioctl.h>
24 #include <libgen.h>
25
26 #include <dlog.h>
27 #include <glib.h>
28 #include <db-util.h>
29 #include <vconf.h>
30 #include <vconf-keys.h>
31
32 #include <packet.h>
33 #include <com-core.h>
34 #include <com-core_packet.h>
35
36 #include "shortcut_internal.h"
37 #include "shortcut.h"
38 #include "dlist.h"
39
40
41
42 #define CREATED 0x00BEEF00
43 #define DESTROYED 0x00DEAD00
44
45
46 static struct info {
47         int fd;
48         int (*init_cb)(int status, void *data);
49         void *cbdata;
50         int initialized;
51
52         const char *utility_socket;
53
54         struct dlist *pending_list;
55 } s_info = {
56         .fd = -1,
57         .init_cb = NULL,
58         .cbdata = NULL,
59         .initialized = 0,
60
61         .utility_socket = "/tmp/.utility.service",
62         .pending_list = NULL,
63 };
64
65
66
67 struct request_item {
68         struct shortcut_icon *handle;
69         icon_request_cb_t result_cb;
70         void *data;
71 };
72
73
74
75 struct pending_item {
76         struct request_item *item;
77         struct packet *packet;
78 };
79
80
81
82 struct block {
83         unsigned int idx;
84
85         char *type;
86         char *part;
87         char *data;
88         char *option;
89         char *id;
90         char *target_id;
91 };
92
93
94
95 struct shortcut_icon {
96         unsigned int state;
97         struct shortcut_desc *desc;
98         int refcnt;
99         void *data;
100 };
101
102
103
104 struct shortcut_desc {
105         int for_pd;
106
107         unsigned int last_idx;
108
109         struct dlist *block_list;
110 };
111
112
113
114 static inline void delete_block(struct block *block)
115 {
116         DbgPrint("Release block: %p\n", block);
117         free(block->type);
118         free(block->part);
119         free(block->data);
120         free(block->option);
121         free(block->id);
122         free(block->target_id);
123         free(block);
124 }
125
126
127
128 static inline int shortcut_icon_desc_close(struct shortcut_desc *handle)
129 {
130         struct dlist *l;
131         struct dlist *n;
132         struct block *block;
133
134         dlist_foreach_safe(handle->block_list, l, n, block) {
135                 handle->block_list = dlist_remove(handle->block_list, l);
136                 delete_block(block);
137         }
138
139         free(handle);
140         return 0;
141 }
142
143
144
145 static inline struct shortcut_icon *shortcut_icon_request_unref(struct shortcut_icon *handle)
146 {
147         handle->refcnt--;
148         DbgPrint("Handle: refcnt[%d]\n", handle->refcnt);
149
150         if (handle->refcnt == 0) {
151                 handle->state = DESTROYED;
152                 shortcut_icon_desc_close(handle->desc);
153                 free(handle);
154                 handle = NULL;
155         }
156
157         return handle;
158 }
159
160
161
162 static inline struct shortcut_icon *shortcut_icon_request_ref(struct shortcut_icon *handle)
163 {
164         handle->refcnt++;
165         DbgPrint("Handle: refcnt[%d]\n", handle->refcnt);
166         return handle;
167 }
168
169
170
171 static int disconnected_cb(int handle, void *data)
172 {
173         if (s_info.fd != handle) {
174                 return 0;
175         }
176
177         ErrPrint("Disconnected\n");
178         s_info.fd = -1;
179         s_info.init_cb = NULL;
180         s_info.cbdata = NULL;
181         s_info.initialized = 0;
182         return 0;
183 }
184
185
186
187 static inline struct shortcut_desc *shortcut_icon_desc_open(void)
188 {
189         struct shortcut_desc *handle;
190
191         handle = calloc(1, sizeof(*handle));
192         if (!handle) {
193                 ErrPrint("Error: %s\n", strerror(errno));
194                 return NULL;
195         }
196
197         return handle;
198 }
199
200
201
202 static inline int shortcut_icon_desc_save(struct shortcut_desc *handle, const char *filename)
203 {
204         struct dlist *l;
205         struct dlist *n;
206         struct block *block;
207         FILE *fp;
208
209         if (!handle) {
210                 return -EINVAL;
211         }
212
213         fp = fopen(filename, "w+t");
214         if (!fp) {
215                 ErrPrint("Error: %s\n", strerror(errno));
216                 return -EIO;
217         }
218
219         DbgPrint("Close and flush\n");
220         dlist_foreach_safe(handle->block_list, l, n, block) {
221                 DbgPrint("{\n");
222                 fprintf(fp, "{\n");
223                 if (block->type) {
224                         fprintf(fp, "type=%s\n", block->type);
225                         DbgPrint("type=%s\n", block->type);
226                 }
227
228                 if (block->part) {
229                         fprintf(fp, "part=%s\n", block->part);
230                         DbgPrint("part=%s\n", block->part);
231                 }
232
233                 if (block->data) {
234                         fprintf(fp, "data=%s\n", block->data);
235                         DbgPrint("data=%s\n", block->data);
236                 }
237
238                 if (block->option) {
239                         fprintf(fp, "option=%s\n", block->option);
240                         DbgPrint("option=%s\n", block->option);
241                 }
242
243                 if (block->id) {
244                         fprintf(fp, "id=%s\n", block->id);
245                         DbgPrint("id=%s\n", block->id);
246                 }
247
248                 if (block->target_id) {
249                         fprintf(fp, "target=%s\n", block->target_id);
250                         DbgPrint("target=%s\n", block->target_id);
251                 }
252
253                 fprintf(fp, "}\n");
254                 DbgPrint("}\n");
255         }
256
257         if (fclose(fp) != 0) {
258                 ErrPrint("fclose: %s\n", strerror(errno));
259         }
260         return 0;
261 }
262
263
264
265 static inline struct block *find_block(struct shortcut_desc *handle, const char *id, const char *part)
266 {
267         struct block *block;
268         struct dlist *l;
269
270         dlist_foreach(handle->block_list, l, block) {
271                 if (!strcmp(block->part, part) && (!id || !strcmp(block->id, id))) {
272                         return block;
273                 }
274         }
275
276         return NULL;
277 }
278
279
280
281 static inline int update_block(struct block *block, const char *data, const char *option)
282 {
283         char *_data = NULL;
284         char *_option = NULL;
285
286         if (data) {
287                 _data = strdup(data);
288                 if (!_data) {
289                         ErrPrint("Heap: %s\n", strerror(errno));
290                         return -ENOMEM;
291                 }
292         }
293
294         if (option) {
295                 _option = strdup(option);
296                 if (!_option) {
297                         ErrPrint("Heap: %s\n", strerror(errno));
298                         return -ENOMEM;
299                 }
300         }
301
302         free(block->data);
303         free(block->option);
304
305         block->data = _data;
306         block->option = _option;
307         return 0;
308 }
309
310
311
312 /*!
313  * \return idx
314  */
315
316
317
318 static inline int shortcut_icon_desc_add_block(struct shortcut_desc *handle, const char *id, const char *type, const char *part, const char *data, const char *option, const char *target_id)
319 {
320         struct block *block;
321
322         if (!handle || !type) {
323                 return SHORTCUT_ERROR_INVALID;
324         }
325
326         if (!part) {
327                 part = "";
328         }
329
330         if (!data) {
331                 data = "";
332         }
333
334         if (target_id) {
335                 if (strcmp(type, SHORTCUT_ICON_TYPE_SCRIPT)) {
336                         ErrPrint("target id only can be used for script type\n");
337                         return -EINVAL;
338                 }
339         }
340
341         block = find_block(handle, id, part);
342         if (!block) {
343                 block = calloc(1, sizeof(*block));
344                 if (!block) {
345                         ErrPrint("Heap: %s\n", strerror(errno));
346                         return SHORTCUT_ERROR_MEMORY;
347                 }
348
349                 block->type = strdup(type);
350                 if (!block->type) {
351                         ErrPrint("Heap: %s\n", strerror(errno));
352                         free(block);
353                         return SHORTCUT_ERROR_MEMORY;
354                 }
355
356                 block->part = strdup(part);
357                 if (!block->part) {
358                         ErrPrint("Heap: %s\n", strerror(errno));
359                         free(block->type);
360                         free(block);
361                         return SHORTCUT_ERROR_MEMORY;
362                 }
363
364                 block->data = strdup(data);
365                 if (!block->data) {
366                         ErrPrint("Heap: %s\n", strerror(errno));
367                         free(block->type);
368                         free(block->part);
369                         free(block);
370                         return SHORTCUT_ERROR_MEMORY;
371                 }
372
373                 if (option) {
374                         block->option = strdup(option);
375                         if (!block->option) {
376                                 ErrPrint("Heap: %s\n", strerror(errno));
377                                 free(block->data);
378                                 free(block->type);
379                                 free(block->part);
380                                 free(block);
381                                 return SHORTCUT_ERROR_MEMORY;
382                         }
383                 }
384
385                 if (id) {
386                         block->id = strdup(id);
387                         if (!block->id) {
388                                 ErrPrint("Heap: %s\n", strerror(errno));
389                                 free(block->option);
390                                 free(block->data);
391                                 free(block->type);
392                                 free(block->part);
393                                 free(block);
394                                 return SHORTCUT_ERROR_MEMORY;
395                         }
396                 }
397
398                 if (target_id) {
399                         block->target_id = strdup(target_id);
400                         if (!block->target_id) {
401                                 ErrPrint("Heap: %s\n", strerror(errno));
402                                 free(block->id);
403                                 free(block->option);
404                                 free(block->data);
405                                 free(block->type);
406                                 free(block->part);
407                                 free(block);
408                                 return SHORTCUT_ERROR_MEMORY;
409                         }
410                 }
411
412                 block->idx = handle->last_idx++;
413                 handle->block_list = dlist_append(handle->block_list, block);
414         } else {
415                 if (strcmp(block->type, type)) {
416                         ErrPrint("type is not valid (%s, %s)\n", block->type, type);
417                         return -EINVAL;
418                 }
419
420                 if ((block->target_id && !target_id) || (!block->target_id && target_id)) {
421                         ErrPrint("type is not valid (%s, %s)\n", block->type, type);
422                         return -EINVAL;
423                 }
424
425                 if (block->target_id && target_id && strcmp(block->target_id, target_id)) {
426                         ErrPrint("type is not valid (%s, %s)\n", block->type, type);
427                         return -EINVAL;
428                 }
429
430                 update_block(block, data, option);
431         }
432
433         return block->idx;
434 }
435
436
437
438 static int icon_request_cb(pid_t pid, int handle, const struct packet *packet, void *data)
439 {
440         struct request_item *item = data;
441         int ret;
442
443         if (!packet) {
444                 ret = -EFAULT;
445                 DbgPrint("Disconnected?\n");
446         } else {
447                 if (packet_get(packet, "i", &ret) != 1) {
448                         DbgPrint("Invalid packet\n");
449                         ret = -EINVAL;
450                 }
451         }
452
453         if (item->result_cb) {
454                 item->result_cb(item->handle, ret, item->data);
455         }
456
457         (void)shortcut_icon_request_unref(item->handle);
458         free(item);
459         return 0;
460 }
461
462
463
464 static inline int make_connection(void)
465 {
466         int ret;
467         static struct method service_table[] = {
468                 {
469                         .cmd = NULL,
470                         .handler = NULL,
471                 },
472         };
473
474         s_info.fd = com_core_packet_client_init(s_info.utility_socket, 0, service_table);
475         if (s_info.fd < 0) {
476                 ret = SHORTCUT_ERROR_COMM;
477
478                 if (s_info.init_cb) {
479                         s_info.init_cb(ret, s_info.cbdata);
480                 }
481         } else {
482                 struct dlist *l;
483                 struct dlist *n;
484                 struct pending_item *pend;
485
486                 if (s_info.init_cb) {
487                         s_info.init_cb(SHORTCUT_SUCCESS, s_info.cbdata);
488                 }
489
490                 dlist_foreach_safe(s_info.pending_list, l, n, pend) {
491                         s_info.pending_list = dlist_remove(s_info.pending_list, l);
492
493                         ret = com_core_packet_async_send(s_info.fd, pend->packet, 0.0f, icon_request_cb, pend->item);
494                         packet_destroy(pend->packet);
495                         if (ret < 0) {
496                                 ErrPrint("ret: %d\n", ret);
497                                 if (pend->item->result_cb) {
498                                         pend->item->result_cb(pend->item->handle, ret, pend->item->data);
499                                 }
500                                 free(pend->item);
501                         }
502
503                         free(pend);
504                 }
505
506                 ret = SHORTCUT_SUCCESS;
507         }
508
509         return ret;
510 }
511
512
513
514 static void master_started_cb(keynode_t *node, void *user_data)
515 {
516         int state = 0;
517
518         if (vconf_get_bool(VCONFKEY_MASTER_STARTED, &state) < 0) {
519                 ErrPrint("Unable to get \"%s\"\n", VCONFKEY_MASTER_STARTED);
520         }
521
522         if (state == 1 && make_connection() == SHORTCUT_SUCCESS) {
523                 int ret;
524                 ret = vconf_ignore_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb);
525                 DbgPrint("Ignore VCONF [%d]\n", ret);
526         }
527 }
528
529
530
531 EAPI int shortcut_icon_service_init(int (*init_cb)(int status, void *data), void *data)
532 {
533         int ret;
534
535         if (s_info.fd >= 0) {
536                 return -EALREADY;
537         }
538
539         if (s_info.initialized) {
540                 s_info.initialized = 1;
541                 com_core_add_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
542         }
543
544         s_info.init_cb = init_cb;
545         s_info.cbdata = data;
546
547         ret = vconf_notify_key_changed(VCONFKEY_MASTER_STARTED, master_started_cb, NULL);
548         if (ret < 0) {
549                 ErrPrint("Failed to add vconf for service state [%d]\n", ret);
550         } else {
551                 DbgPrint("vconf is registered\n");
552         }
553
554         master_started_cb(NULL, NULL);
555         return 0;
556 }
557
558
559
560 EAPI int shortcut_icon_service_fini(void)
561 {
562         struct dlist *l;
563         struct dlist *n;
564         struct pending_item *pend;
565
566         if (s_info.initialized) {
567                 com_core_del_event_callback(CONNECTOR_DISCONNECTED, disconnected_cb, NULL);
568                 s_info.initialized = 0;
569         }
570
571         if (s_info.fd < 0) {
572                 return -EINVAL;
573         }
574
575         com_core_packet_client_fini(s_info.fd);
576         s_info.init_cb = NULL;
577         s_info.cbdata = NULL;
578         s_info.fd = -1;
579
580         dlist_foreach_safe(s_info.pending_list, l, n, pend) {
581                 s_info.pending_list = dlist_remove(s_info.pending_list, l);
582                 packet_unref(pend->packet);
583                 if (pend->item->result_cb) {
584                         pend->item->result_cb(pend->item->handle, SHORTCUT_ERROR_COMM, pend->item->data);
585                 }
586                 free(pend->item);
587                 free(pend);
588         }
589         return 0;
590 }
591
592
593
594 EAPI struct shortcut_icon *shortcut_icon_request_create(void)
595 {
596         struct shortcut_icon *handle;
597
598         handle = malloc(sizeof(*handle));
599         if (!handle) {
600                 ErrPrint("Heap: %s\n", strerror(errno));
601                 return NULL;
602         }
603
604         handle->desc = shortcut_icon_desc_open();
605         if (!handle->desc) {
606                 ErrPrint("Uanble to open desc\n");
607                 free(handle);
608                 return NULL;
609         }
610
611         handle->state = CREATED;
612         handle->refcnt = 1;
613         return handle;
614 }
615
616
617 EAPI int shortcut_icon_request_set_data(struct shortcut_icon *handle, void *data)
618 {
619         if (!handle || handle->state != CREATED) {
620                 ErrPrint("Handle is not valid\n");
621                 return -EINVAL;
622         }
623
624         handle->data = data;
625         return 0;
626 }
627
628
629
630 EAPI void *shortcut_icon_request_data(struct shortcut_icon *handle)
631 {
632         if (!handle || handle->state != CREATED) {
633                 ErrPrint("Handle is not valid\n");
634                 return NULL;
635         }
636
637         return handle->data;
638 }
639
640
641
642 EAPI int shortcut_icon_request_set_info(struct shortcut_icon *handle, const char *id, const char *type, const char *part, const char *data, const char *option, const char *subid)
643 {
644         if (!handle || handle->state != CREATED) {
645                 ErrPrint("Handle is not valid\n");
646                 return -EINVAL;
647         }
648
649         return shortcut_icon_desc_add_block(handle->desc, id, type, part, data, option, subid);
650 }
651
652
653
654 EAPI int shortcut_icon_request_destroy(struct shortcut_icon *handle)
655 {
656         if (!handle || handle->state != CREATED) {
657                 ErrPrint("Handle is not valid\n");
658                 return -EINVAL;
659         }
660
661         (void)shortcut_icon_request_unref(handle);
662         return 0;
663 }
664
665
666
667 EAPI int shortcut_icon_request_send(struct shortcut_icon *handle, int size_type, const char *layout, const char *group, const char *outfile, icon_request_cb_t result_cb, void *data)
668 {
669         int ret;
670         struct packet *packet;
671         struct request_item *item;
672         char *filename;
673         int len;
674
675         if (!handle || handle->state != CREATED) {
676                 ErrPrint("Handle is not valid\n");
677                 return -EINVAL;
678         }
679
680         if (!layout) {
681                 layout = DEFAULT_ICON_LAYOUT;
682         }
683
684         if (!group) {
685                 group = DEFAULT_ICON_GROUP;
686         }
687
688         len = strlen(outfile) + strlen(".desc") + 1;
689         filename = malloc(len);
690         if (!filename) {
691                 ErrPrint("Heap: %s\n", strerror(errno));
692                 return -ENOMEM;
693         }
694
695         snprintf(filename, len, "%s.desc", outfile);
696
697         ret = shortcut_icon_desc_save(handle->desc, filename);
698         if (ret < 0) {
699                 goto out;
700         }
701
702         item = malloc(sizeof(*item));
703         if (!item) {
704                 ErrPrint("Heap: %s\n", strerror(errno));
705                 if (unlink(filename) < 0) {
706                         ErrPrint("Unlink: %s\n", strerror(errno));
707                 }
708                 ret = -ENOMEM;
709                 goto out;
710         }
711
712         item->result_cb = result_cb;
713         item->data = data;
714         item->handle = shortcut_icon_request_ref(handle);
715
716         packet = packet_create("icon_create", "sssis", layout, group, filename, size_type, outfile);
717         if (!packet) {
718                 ErrPrint("Failed to create a packet\n");
719                 if (unlink(filename) < 0) {
720                         ErrPrint("Unlink: %s\n", strerror(errno));
721                 }
722                 free(item);
723                 (void)shortcut_icon_request_unref(handle);
724                 ret = -EFAULT;
725                 goto out;
726         }
727
728         if (s_info.fd >= 0 && !s_info.pending_list) {
729                 ret = com_core_packet_async_send(s_info.fd, packet, 0.0f, icon_request_cb, item);
730                 packet_destroy(packet);
731                 if (ret < 0) {
732                         ErrPrint("ret: %d\n", ret);
733                         if (unlink(filename) < 0) {
734                                 ErrPrint("Unlink: %s\n", strerror(errno));
735                         }
736                         free(item);
737                         (void)shortcut_icon_request_unref(handle);
738                 }
739                 DbgPrint("Request is sent\n");
740         } else {
741                 struct pending_item *pend;
742
743                 pend = malloc(sizeof(*pend));
744                 if (!pend) {
745                         ErrPrint("Heap: %s\n", strerror(errno));
746                         packet_destroy(packet);
747                         free(item);
748                         if (unlink(filename) < 0) {
749                                 ErrPrint("Unlink: %s\n", strerror(errno));
750                         }
751                         (void)shortcut_icon_request_unref(handle);
752                         ret = -ENOMEM;
753                         goto out;
754                 }
755
756                 pend->packet = packet;
757                 pend->item = item;
758
759                 s_info.pending_list = dlist_append(s_info.pending_list, pend);
760                 DbgPrint("Request is pended\n");
761
762                 ret = 0;
763         }
764
765 out:
766         free(filename);
767         return ret;
768 }
769
770
771 /* End of a file */