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