Fix memory alignment and remove dead/useless code.
[framework/uifw/ethumb.git] / src / bin / ethumbd.c
1 /**
2  * @file
3  *
4  * Copyright (C) 2009 by ProFUSION embedded systems
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,  but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  *
21  * @author Rafael Antognolli <antognolli@profusion.mobi>
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <limits.h>
30 #include <unistd.h>
31 #include <errno.h>
32
33 #include <Ethumb.h>
34 #include <Eina.h>
35 #include <Ecore_Getopt.h>
36 #include <Ecore.h>
37 #include <E_DBus.h>
38
39 #include "ethumbd_private.h"
40
41 #ifndef PATH_MAX
42 #define PATH_MAX 4096
43 #endif
44
45 #define MAX_ID 2000000
46
47 #define DBG(...) EINA_LOG_DOM_DBG(_log_domain, __VA_ARGS__)
48 #define INF(...) EINA_LOG_DOM_INFO(_log_domain, __VA_ARGS__)
49 #define WRN(...) EINA_LOG_DOM_WARN(_log_domain, __VA_ARGS__)
50 #define ERR(...) EINA_LOG_DOM_ERR(_log_domain, __VA_ARGS__)
51 #define CRIT(...) EINA_LOG_DOM_CRIT(_log_domain, __VA_ARGS__)
52
53 static const char _ethumb_dbus_bus_name[] = "org.enlightenment.Ethumb";
54 static const char _ethumb_dbus_interface[] = "org.enlightenment.Ethumb";
55 static const char _ethumb_dbus_objects_interface[] = "org.enlightenment.Ethumb.objects";
56 static const char _ethumb_dbus_path[] = "/org/enlightenment/Ethumb";
57 static const char fdo_interface[] = "org.freedesktop.DBus";
58 static const char fdo_bus_name[] = "org.freedesktop.DBus";
59 static const char fdo_path[] = "/org/freedesktop/DBus";
60
61 static int _log_domain = -1;
62
63 struct _Ethumb_Setup
64 {
65    struct
66    {
67       Eina_Bool fdo : 1;
68       Eina_Bool size : 1;
69       Eina_Bool format : 1;
70       Eina_Bool aspect : 1;
71       Eina_Bool crop : 1;
72       Eina_Bool quality : 1;
73       Eina_Bool compress : 1;
74       Eina_Bool directory : 1;
75       Eina_Bool category : 1;
76       Eina_Bool frame : 1;
77       Eina_Bool video_time : 1;
78       Eina_Bool video_start : 1;
79       Eina_Bool video_interval : 1;
80       Eina_Bool video_ntimes : 1;
81       Eina_Bool video_fps : 1;
82       Eina_Bool document_page : 1;
83    } flags;
84    int fdo;
85    int tw, th;
86    int format;
87    int aspect;
88    float cx, cy;
89    int quality;
90    int compress;
91    const char *directory;
92    const char *category;
93    const char *theme_file;
94    const char *group;
95    const char *swallow;
96    float video_time;
97    float video_start;
98    float video_interval;
99    unsigned int video_ntimes;
100    unsigned int video_fps;
101    unsigned int document_page;
102 };
103
104 struct _Ethumb_Request
105 {
106    int id;
107    const char *file, *key;
108    const char *thumb, *thumb_key;
109    struct _Ethumb_Setup setup;
110 };
111
112 struct _Ethumb_Object
113 {
114    int used;
115    const char *path;
116    const char *client;
117    Eina_List *queue;
118    int nqueue;
119    int id_count;
120    int max_id;
121    int min_id;
122    E_DBus_Object *dbus_obj;
123 };
124
125 struct _Ethumb_Queue
126 {
127    int count;
128    int max_count;
129    int nqueue;
130    int last;
131    int current;
132    struct _Ethumb_Object *table;
133    int *list;
134 };
135
136 struct _Ethumb_Slave
137 {
138    Ecore_Exe *exe;
139    Ecore_Event_Handler *data_cb;
140    Ecore_Event_Handler *del_cb;
141    char *bufcmd; // buffer to read commands from slave
142    int scmd; // size of command to read
143    int pcmd; // position in the command buffer
144 };
145
146 struct _Ethumbd
147 {
148    E_DBus_Connection *conn;
149    E_DBus_Signal_Handler *name_owner_changed_handler;
150    E_DBus_Interface *eiface, *objects_iface;
151    E_DBus_Object *dbus_obj;
152    Ecore_Idler *idler;
153    struct _Ethumb_Request *processing;
154    struct _Ethumb_Queue queue;
155    double timeout;
156    Ecore_Timer *timeout_timer;
157    struct _Ethumb_Slave slave;
158 };
159
160 struct _Ethumb_Object_Data
161 {
162    int index;
163    struct _Ethumbd *ed;
164 };
165
166 struct _Ethumb_DBus_Method_Table
167 {
168    const char *name;
169    const char *signature;
170    const char *reply;
171    E_DBus_Method_Cb function;
172 };
173
174 struct _Ethumb_DBus_Signal_Table
175 {
176    const char *name;
177    const char *signature;
178 };
179
180 const Ecore_Getopt optdesc = {
181   "ethumbd",
182   NULL,
183   PACKAGE_VERSION,
184   "(C) 2009 - ProFUSION embedded systems",
185   "LGPL v3 - GNU Lesser General Public License",
186   "Ethumb daemon.\n"
187   "\n"
188   "ethumbd uses the Ethumb library to create thumbnails for any "
189   "program that requests it (now just by dbus).\n",
190   0,
191   {
192     ECORE_GETOPT_STORE_DOUBLE
193     ('t', "timeout", "finish ethumbd after <timeout> seconds of no activity."),
194     ECORE_GETOPT_LICENSE('L', "license"),
195     ECORE_GETOPT_COPYRIGHT('C', "copyright"),
196     ECORE_GETOPT_VERSION('V', "version"),
197     ECORE_GETOPT_HELP('h', "help"),
198     ECORE_GETOPT_SENTINEL
199   }
200 };
201
202 static void _ethumb_dbus_generated_signal(struct _Ethumbd *ed, int *id, const char *thumb_path, const char *thumb_key, Eina_Bool success);
203 static int _ethumbd_slave_spawn(struct _Ethumbd *ed);
204
205 static Eina_Bool
206 _ethumbd_timeout_cb(void *data)
207 {
208    struct _Ethumbd *ed = data;
209
210    ecore_main_loop_quit();
211    ed->timeout_timer = NULL;
212
213    return 0;
214 }
215
216 static void
217 _ethumbd_timeout_start(struct _Ethumbd *ed)
218 {
219    if (ed->timeout < 0)
220      return;
221
222    if (!ed->timeout_timer)
223      ed->timeout_timer = ecore_timer_add(ed->timeout, _ethumbd_timeout_cb, ed);
224 }
225
226 static void
227 _ethumbd_timeout_stop(struct _Ethumbd *ed)
228 {
229    if (!ed->timeout_timer)
230      return;
231
232    ecore_timer_del(ed->timeout_timer);
233    ed->timeout_timer = NULL;
234 }
235
236 static int
237 _ethumb_dbus_check_id(struct _Ethumb_Object *eobject, int id)
238 {
239    if (id < 0 || id > MAX_ID)
240      return 0;
241
242    if (eobject->min_id < eobject->max_id)
243      return id < eobject->min_id || id > eobject->max_id;
244    else if (eobject->min_id > eobject->max_id)
245      return id < eobject->min_id && id > eobject->max_id;
246    else
247      return id != eobject->max_id;
248 }
249
250 static void
251 _ethumb_dbus_inc_max_id(struct _Ethumb_Object *eobject, int id)
252 {
253    if (eobject->min_id < 0 && eobject->max_id < 0)
254      eobject->min_id = id;
255
256    eobject->max_id = id;
257 }
258
259 static void
260 _ethumb_dbus_inc_min_id(struct _Ethumb_Object *eobject)
261 {
262    Eina_List *l;
263
264    l = eobject->queue;
265    while (l)
266      {
267         struct _Ethumb_Request *request = l->data;
268         if (request->id >= 0)
269           {
270              eobject->min_id = request->id;
271              break;
272           }
273
274         l = l->next;
275      }
276
277    if (!l)
278      {
279         eobject->min_id = -1;
280         eobject->max_id = -1;
281      }
282 }
283
284 int
285 _ethumbd_write_safe(struct _Ethumbd *ed, const void *buf, ssize_t size)
286 {
287
288    if (!ed->slave.exe)
289      {
290         ERR("slave process isn't running.");
291         return 0;
292      }
293
294    ecore_exe_send(ed->slave.exe, buf, size);
295    return 1;
296 }
297
298 static void
299 _ethumbd_child_write_op_new(struct _Ethumbd *ed, int index)
300 {
301    int id = ETHUMBD_OP_NEW;
302    _ethumbd_write_safe(ed, &id, sizeof(id));
303    _ethumbd_write_safe(ed, &index, sizeof(index));
304 }
305
306 static void
307 _ethumbd_child_write_op_del(struct _Ethumbd *ed, int index)
308 {
309    int id = ETHUMBD_OP_DEL;
310    _ethumbd_write_safe(ed, &id, sizeof(id));
311    _ethumbd_write_safe(ed, &index, sizeof(index));
312 }
313
314 static void
315 _ethumbd_pipe_str_write(struct _Ethumbd *ed, const char *str)
316 {
317    int len;
318
319    if (str)
320      len = strlen(str) + 1;
321    else
322      len = 0;
323
324    _ethumbd_write_safe(ed, &len, sizeof(len));
325    _ethumbd_write_safe(ed, str, len);
326 }
327
328 static void
329 _ethumbd_child_write_op_generate(struct _Ethumbd *ed, int index, const char *path, const char *key, const char *thumb_path, const char *thumb_key)
330 {
331    int id = ETHUMBD_OP_GENERATE;
332
333    _ethumbd_write_safe(ed, &id, sizeof(id));
334    _ethumbd_write_safe(ed, &index, sizeof(index));
335
336    _ethumbd_pipe_str_write(ed, path);
337    _ethumbd_pipe_str_write(ed, key);
338    _ethumbd_pipe_str_write(ed, thumb_path);
339    _ethumbd_pipe_str_write(ed, thumb_key);
340 }
341
342 static void
343 _generated_cb(struct _Ethumbd *ed, Eina_Bool success, const char *thumb_path, const char *thumb_key)
344 {
345    int i = ed->queue.current;
346
347    DBG("thumbnail ready at: \"%s:%s\"", thumb_path, thumb_key);
348
349    if (ed->queue.table[i].used)
350      _ethumb_dbus_generated_signal
351        (ed, &ed->processing->id, thumb_path, thumb_key, success);
352    eina_stringshare_del(ed->processing->file);
353    eina_stringshare_del(ed->processing->key);
354    eina_stringshare_del(ed->processing->thumb);
355    eina_stringshare_del(ed->processing->thumb_key);
356    free(ed->processing);
357    ed->processing = NULL;
358 }
359
360 static void
361 _ethumbd_slave_cmd_ready(struct _Ethumbd *ed)
362 {
363    const char *bufcmd = ed->slave.bufcmd;
364    Eina_Bool success;
365    const char *thumb_path = NULL;
366    const char *thumb_key = NULL;
367    int size_path, size_key;
368
369    /* NOTE: accessing values directly on bufcmd breaks alignment
370     * as the first item is an Eina_Bool (size 1) and second is
371     * an integer (size 4, alignment 4).
372     * Thus copy to stack values before using them, to have proper alignment.
373     */
374 #define READVAL(dst)                            \
375    memcpy(&dst, bufcmd, sizeof(dst));           \
376    bufcmd += sizeof(dst);
377
378    READVAL(success);
379
380    READVAL(size_path);
381    if (size_path)
382      {
383         thumb_path = bufcmd;
384         bufcmd += size_path;
385      }
386
387    READVAL(size_key);
388    if (size_key) thumb_key = bufcmd;
389
390 #undef READVAL
391
392    _generated_cb(ed, success, thumb_path, thumb_key);
393
394    free(ed->slave.bufcmd);
395    ed->slave.bufcmd = NULL;
396    ed->slave.scmd = 0;
397 }
398
399 static int
400 _ethumbd_slave_alloc_cmd(struct _Ethumbd *ed, int ssize, char *sdata)
401 {
402    int *scmd;
403
404    if (ed->slave.bufcmd)
405      return 0;
406
407    scmd = (int *)sdata;
408    if (ssize < (int)sizeof(*scmd)) {
409         ERR("could not read size of command.");
410         return 0;
411    }
412    ed->slave.bufcmd = malloc(*scmd);
413    ed->slave.scmd = *scmd;
414    ed->slave.pcmd = 0;
415
416    return sizeof(*scmd);
417 }
418
419 static Eina_Bool
420 _ethumbd_slave_data_read_cb(void *data, int type __UNUSED__, void *event)
421 {
422    struct _Ethumbd *ed = data;
423    Ecore_Exe_Event_Data *ev = event;
424    int ssize;
425    char *sdata;
426
427    if (ev->exe != ed->slave.exe)
428      {
429         ERR("PARENT ERROR: slave != ev->exe");
430         return 0;
431      }
432
433    ssize = ev->size;
434    sdata = ev->data;
435
436    while (ssize > 0)
437      {
438         if (!ed->slave.bufcmd)
439           {
440              int n;
441              n = _ethumbd_slave_alloc_cmd(ed, ssize, sdata);
442              ssize -= n;
443              sdata += n;
444           }
445         else
446           {
447              char *bdata;
448              int nbytes;
449              bdata = ed->slave.bufcmd + ed->slave.pcmd;
450              nbytes = ed->slave.scmd - ed->slave.pcmd;
451              nbytes = ssize < nbytes ? ssize : nbytes;
452              memcpy(bdata, sdata, nbytes);
453              sdata += nbytes;
454              ssize -= nbytes;
455              ed->slave.pcmd += nbytes;
456
457              if (ed->slave.pcmd == ed->slave.scmd)
458                _ethumbd_slave_cmd_ready(ed);
459           }
460      }
461
462    return 1;
463 }
464
465 static Eina_Bool
466 _ethumbd_slave_del_cb(void *data, int type __UNUSED__, void *event)
467 {
468    struct _Ethumbd *ed = data;
469    Ecore_Exe_Event_Del *ev = event;
470    int i;
471
472    if (ev->exe != ed->slave.exe)
473      return 1;
474
475    if (ev->exited)
476      ERR("slave exited with code: %d", ev->exit_code);
477    else if (ev->signalled)
478      ERR("slave exited by signal: %d", ev->exit_signal);
479
480    if (!ed->processing)
481      goto end;
482
483    i = ed->queue.current;
484    ERR("failed to generate thumbnail for: \"%s:%s\"",
485        ed->processing->file, ed->processing->key);
486
487    if (ed->queue.table[i].used)
488      _ethumb_dbus_generated_signal
489        (ed, &ed->processing->id, NULL, NULL, EINA_FALSE);
490    eina_stringshare_del(ed->processing->file);
491    eina_stringshare_del(ed->processing->key);
492    eina_stringshare_del(ed->processing->thumb);
493    eina_stringshare_del(ed->processing->thumb_key);
494    free(ed->processing);
495    ed->processing = NULL;
496
497 end:
498    ed->slave.exe = NULL;
499    if (ed->slave.bufcmd)
500      free(ed->slave.bufcmd);
501
502    return _ethumbd_slave_spawn(ed);
503 }
504
505 static void
506 _ethumbd_pipe_write_setup(struct _Ethumbd *ed, int type, const void *data)
507 {
508    const int *i_value;
509    const float *f_value;
510
511    _ethumbd_write_safe(ed, &type, sizeof(type));
512
513    switch (type)
514      {
515       case ETHUMBD_FDO:
516       case ETHUMBD_FORMAT:
517       case ETHUMBD_ASPECT:
518       case ETHUMBD_QUALITY:
519       case ETHUMBD_COMPRESS:
520       case ETHUMBD_SIZE_W:
521       case ETHUMBD_SIZE_H:
522       case ETHUMBD_DOCUMENT_PAGE:
523       case ETHUMBD_VIDEO_NTIMES:
524       case ETHUMBD_VIDEO_FPS:
525          i_value = data;
526          _ethumbd_write_safe(ed, i_value, sizeof(*i_value));
527          break;
528       case ETHUMBD_CROP_X:
529       case ETHUMBD_CROP_Y:
530       case ETHUMBD_VIDEO_TIME:
531       case ETHUMBD_VIDEO_START:
532       case ETHUMBD_VIDEO_INTERVAL:
533          f_value = data;
534          _ethumbd_write_safe(ed, f_value, sizeof(*f_value));
535          break;
536       case ETHUMBD_DIRECTORY:
537       case ETHUMBD_CATEGORY:
538       case ETHUMBD_FRAME_FILE:
539       case ETHUMBD_FRAME_GROUP:
540       case ETHUMBD_FRAME_SWALLOW:
541          _ethumbd_pipe_str_write(ed, data);
542          break;
543       case ETHUMBD_SETUP_FINISHED:
544          break;
545       default:
546          ERR("wrong ethumb setup parameter.");
547      }
548 }
549
550 static void
551 _process_setup(struct _Ethumbd *ed)
552 {
553    int op_id = ETHUMBD_OP_SETUP;
554    int index = ed->queue.current;
555
556    struct _Ethumb_Setup *setup = &ed->processing->setup;
557
558    _ethumbd_write_safe(ed, &op_id, sizeof(op_id));
559    _ethumbd_write_safe(ed, &index, sizeof(index));
560
561    if (setup->flags.fdo)
562      _ethumbd_pipe_write_setup(ed, ETHUMBD_FDO, &setup->fdo);
563    if (setup->flags.size)
564      {
565         _ethumbd_pipe_write_setup(ed, ETHUMBD_SIZE_W, &setup->tw);
566         _ethumbd_pipe_write_setup(ed, ETHUMBD_SIZE_H, &setup->th);
567      }
568    if (setup->flags.format)
569      _ethumbd_pipe_write_setup(ed, ETHUMBD_FORMAT, &setup->format);
570    if (setup->flags.aspect)
571      _ethumbd_pipe_write_setup(ed, ETHUMBD_ASPECT, &setup->aspect);
572    if (setup->flags.crop)
573      {
574         _ethumbd_pipe_write_setup(ed, ETHUMBD_CROP_X, &setup->cx);
575         _ethumbd_pipe_write_setup(ed, ETHUMBD_CROP_Y, &setup->cy);
576      }
577    if (setup->flags.quality)
578      _ethumbd_pipe_write_setup(ed, ETHUMBD_QUALITY, &setup->quality);
579    if (setup->flags.compress)
580      _ethumbd_pipe_write_setup(ed, ETHUMBD_COMPRESS, &setup->compress);
581    if (setup->flags.directory)
582      _ethumbd_pipe_write_setup(ed, ETHUMBD_DIRECTORY, setup->directory);
583    if (setup->flags.category)
584      _ethumbd_pipe_write_setup(ed, ETHUMBD_CATEGORY, setup->category);
585    if (setup->flags.frame)
586      {
587         _ethumbd_pipe_write_setup(ed, ETHUMBD_FRAME_FILE, setup->theme_file);
588         _ethumbd_pipe_write_setup(ed, ETHUMBD_FRAME_GROUP, setup->group);
589         _ethumbd_pipe_write_setup(ed, ETHUMBD_FRAME_SWALLOW, setup->swallow);
590      }
591    if (setup->flags.video_time)
592      _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_TIME, &setup->video_time);
593    if (setup->flags.video_start)
594      _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_START, &setup->video_start);
595    if (setup->flags.video_interval)
596      _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_INTERVAL,
597                                &setup->video_interval);
598    if (setup->flags.video_ntimes)
599      _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_NTIMES, &setup->video_ntimes);
600    if (setup->flags.video_fps)
601      _ethumbd_pipe_write_setup(ed, ETHUMBD_VIDEO_FPS, &setup->video_fps);
602    if (setup->flags.document_page)
603      _ethumbd_pipe_write_setup(ed, ETHUMBD_DOCUMENT_PAGE,
604                                &setup->document_page);
605    _ethumbd_pipe_write_setup(ed, ETHUMBD_SETUP_FINISHED, NULL);
606
607
608    if (setup->directory) eina_stringshare_del(setup->directory);
609    if (setup->category) eina_stringshare_del(setup->category);
610    if (setup->theme_file) eina_stringshare_del(setup->theme_file);
611    if (setup->group) eina_stringshare_del(setup->group);
612    if (setup->swallow) eina_stringshare_del(setup->swallow);
613
614    free(ed->processing);
615    ed->processing = NULL;
616 }
617
618 static void
619 _process_file(struct _Ethumbd *ed)
620 {
621    _ethumbd_child_write_op_generate
622      (ed, ed->queue.current, ed->processing->file,
623       ed->processing->key, ed->processing->thumb, ed->processing->thumb_key);
624 }
625
626 static int
627 _get_next_on_queue(struct _Ethumb_Queue *queue)
628 {
629    int i, index;
630    struct _Ethumb_Object *eobject;
631
632    i = queue->last;
633    i++;
634    if (i >= queue->count)
635      i = 0;
636
637    index = queue->list[i];
638    eobject = &(queue->table[index]);
639    while (!eobject->nqueue)
640      {
641         i = (i + 1) % queue->count;
642
643         index = queue->list[i];
644         eobject = &(queue->table[index]);
645      }
646
647    return queue->list[i];
648 }
649
650 static Eina_Bool
651 _process_queue_cb(void *data)
652 {
653    struct _Ethumb_Object *eobject;
654    int i;
655    struct _Ethumbd *ed = data;
656    struct _Ethumb_Queue *queue = &ed->queue;
657    struct _Ethumb_Request *request;
658
659    if (ed->processing)
660      return 1;
661
662    if (!queue->nqueue)
663      {
664         ed->idler = NULL;
665         if (!queue->count)
666           _ethumbd_timeout_start(ed);
667         ed->idler = NULL;
668         return 0;
669      }
670
671    i = _get_next_on_queue(queue);
672    eobject = &(queue->table[i]);
673
674    request = eina_list_data_get(eobject->queue);
675    eobject->queue = eina_list_remove_list(eobject->queue, eobject->queue);
676    ed->queue.current = i;
677    DBG("processing file: \"%s:%s\"...", request->file,
678        request->key);
679    ed->processing = request;
680
681    if (request->id < 0)
682      _process_setup(ed);
683    else
684      {
685         _process_file(ed);
686         _ethumb_dbus_inc_min_id(eobject);
687      }
688    eobject->nqueue--;
689    queue->nqueue--;
690
691    queue->last = i;
692
693    return 1;
694 }
695
696 static void
697 _process_queue_start(struct _Ethumbd *ed)
698 {
699    if (!ed->idler)
700      ed->idler = ecore_idler_add(_process_queue_cb, ed);
701 }
702
703 static void
704 _process_queue_stop(struct _Ethumbd *ed)
705 {
706    if (ed->idler)
707      {
708         ecore_idler_del(ed->idler);
709         ed->idler = NULL;
710      }
711 }
712
713 static int
714 _ethumb_table_append(struct _Ethumbd *ed)
715 {
716    int i;
717    char buf[1024];
718    struct _Ethumb_Queue *q = &ed->queue;
719
720    if (q->count == q->max_count)
721      {
722         int new_max = q->max_count + 5;
723         int start, size;
724         void *tmp;
725
726         start = q->max_count;
727         size = new_max - q->max_count;
728
729         tmp = realloc(q->table, new_max * sizeof(struct _Ethumb_Object));
730         if (!tmp)
731           {
732              CRIT("could not realloc q->table to %zd bytes: %s",
733                   new_max * sizeof(struct _Ethumb_Object), strerror(errno));
734              return -1;
735           }
736         q->table = tmp;
737         memset(&q->table[start], 0, size * sizeof(struct _Ethumb_Object));
738
739         tmp = realloc(q->list, new_max * sizeof(int));
740         if (!tmp)
741           {
742              CRIT("could not realloc q->list to %zd bytes: %s",
743                   new_max * sizeof(int), strerror(errno));
744              return -1;
745           }
746         q->list = tmp;
747
748         q->max_count = new_max;
749      }
750
751    for (i = 0; i < q->max_count; i++)
752      {
753         if (!q->table[i].used)
754           break;
755      }
756
757    snprintf(buf, sizeof(buf), "%s/%d", _ethumb_dbus_path, i);
758    q->table[i].used = 1;
759    q->table[i].path = eina_stringshare_add(buf);
760    q->table[i].max_id = -1;
761    q->table[i].min_id = -1;
762    q->list[q->count] = i;
763    q->count++;
764    DBG("new object: %s, index = %d, count = %d", buf, i, q->count);
765
766    return i;
767 }
768
769 static inline int
770 _get_index_for_path(const char *path)
771 {
772    int i;
773    int n;
774    n = sscanf(path, "/org/enlightenment/Ethumb/%d", &i);
775    if (!n)
776      return -1;
777    return i;
778 }
779
780 static void
781 _ethumb_table_del(struct _Ethumbd *ed, int i)
782 {
783    int j;
784    Eina_List *l;
785    const Eina_List *il;
786    struct _Ethumb_Queue *q = &ed->queue;
787    struct _Ethumb_Object_Data *odata;
788
789    eina_stringshare_del(q->table[i].path);
790
791    l = q->table[i].queue;
792    while (l)
793      {
794         struct _Ethumb_Request *request = l->data;
795         eina_stringshare_del(request->file);
796         eina_stringshare_del(request->key);
797         eina_stringshare_del(request->thumb);
798         eina_stringshare_del(request->thumb_key);
799         free(request);
800         l = eina_list_remove_list(l, l);
801      }
802    q->nqueue -= q->table[i].nqueue;
803
804    il = e_dbus_object_interfaces_get(q->table[i].dbus_obj);
805    while (il)
806      {
807         e_dbus_object_interface_detach(q->table[i].dbus_obj, il->data);
808         il = e_dbus_object_interfaces_get(q->table[i].dbus_obj);
809      }
810    odata = e_dbus_object_data_get(q->table[i].dbus_obj);
811    free(odata);
812    e_dbus_object_free(q->table[i].dbus_obj);
813
814    memset(&(q->table[i]), 0, sizeof(struct _Ethumb_Object));
815    for (j = 0; j < q->count; j++)
816      {
817         if (q->list[j] == i)
818           q->list[j] = q->list[q->count - 1];
819      }
820
821    q->count--;
822    _ethumbd_child_write_op_del(ed, i);
823    if (!q->count && !ed->processing)
824      _ethumbd_timeout_start(ed);
825 }
826
827 static void
828 _ethumb_table_clear(struct _Ethumbd *ed)
829 {
830    int i;
831
832    for (i = 0; i < ed->queue.max_count; i++)
833      if (ed->queue.table[i].used)
834        _ethumb_table_del(ed, i);
835 }
836
837 static void
838 _name_owner_changed_cb(void *data, DBusMessage *msg)
839 {
840    DBusError err;
841    struct _Ethumbd *ed = data;
842    struct _Ethumb_Queue *q = &ed->queue;
843    const char *name, *from, *to;
844    int i;
845
846    dbus_error_init(&err);
847    if (!dbus_message_get_args(msg, &err,
848                               DBUS_TYPE_STRING, &name,
849                               DBUS_TYPE_STRING, &from,
850                               DBUS_TYPE_STRING, &to,
851                               DBUS_TYPE_INVALID))
852      {
853         ERR("could not get NameOwnerChanged arguments: %s: %s",
854             err.name, err.message);
855         dbus_error_free(&err);
856         return;
857      }
858
859    DBG("NameOwnerChanged: name = %s, from = %s, to = %s", name, from, to);
860
861    if (from[0] == '\0' || to[0] != '\0')
862      return;
863
864    from = eina_stringshare_add(from);
865    for (i = 0; i < q->max_count; i++)
866      {
867         if (q->table[i].used && q->table[i].client == from)
868           {
869              _ethumb_table_del(ed, i);
870              DBG("deleting [%d] from queue table.", i);
871           }
872      }
873 }
874
875 static void
876 _ethumb_dbus_add_name_owner_changed_cb(struct _Ethumbd *ed)
877 {
878    ed->name_owner_changed_handler = e_dbus_signal_handler_add
879      (ed->conn, fdo_bus_name, fdo_path, fdo_interface, "NameOwnerChanged",
880       _name_owner_changed_cb, ed);
881 }
882
883 DBusMessage *
884 _ethumb_dbus_ethumb_new_cb(E_DBus_Object *object, DBusMessage *msg)
885 {
886    DBusMessage *reply;
887    DBusMessageIter iter;
888    E_DBus_Object *dbus_object;
889    struct _Ethumb_Object_Data *odata;
890    int i;
891    const char *return_path = "";
892    const char *client;
893    struct _Ethumbd *ed;
894
895    ed = e_dbus_object_data_get(object);
896    client = dbus_message_get_sender(msg);
897    if (!client)
898      goto end_new;
899
900    i = _ethumb_table_append(ed);
901    if (i < 0)
902      goto end_new;
903
904    odata = calloc(1, sizeof(*odata));
905    odata->index = i;
906    odata->ed = ed;
907
908    ed->queue.table[i].client = eina_stringshare_add(client);
909    return_path = ed->queue.table[i].path;
910
911    dbus_object = e_dbus_object_add(ed->conn, return_path, odata);
912    if (!dbus_object)
913      {
914         ERR("could not create dbus_object.");
915         free(odata);
916         return_path = "";
917         goto end_new;
918      }
919
920    e_dbus_object_interface_attach(dbus_object, ed->objects_iface);
921    ed->queue.table[i].dbus_obj = dbus_object;
922
923    _ethumbd_child_write_op_new(ed, i);
924    _ethumbd_timeout_stop(ed);
925
926  end_new:
927    reply = dbus_message_new_method_return(msg);
928    dbus_message_iter_init_append(reply, &iter);
929    dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
930                                   &return_path);
931    return reply;
932 }
933
934 static struct _Ethumb_DBus_Method_Table _ethumb_dbus_methods[] =
935   {
936     {"new", "", "o", _ethumb_dbus_ethumb_new_cb},
937     {NULL, NULL, NULL, NULL}
938   };
939
940 static const char *
941 _ethumb_dbus_get_bytearray(DBusMessageIter *iter)
942 {
943    int el_type;
944    int length;
945    DBusMessageIter riter;
946    const char *result;
947
948    el_type = dbus_message_iter_get_element_type(iter);
949    if (el_type != DBUS_TYPE_BYTE)
950      {
951         ERR("not an byte array element.");
952         return NULL;
953      }
954
955    dbus_message_iter_recurse(iter, &riter);
956    dbus_message_iter_get_fixed_array(&riter, &result, &length);
957
958    if ((length == 0) || (result[0] == '\0'))
959      return NULL;
960    else
961      return eina_stringshare_add_length(result, length);
962 }
963
964 static void
965 _ethumb_dbus_append_bytearray(DBusMessageIter *iter, const char *string)
966 {
967    DBusMessageIter viter;
968
969    if (!string)
970      string = "";
971
972    dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &viter);
973    dbus_message_iter_append_fixed_array
974      (&viter, DBUS_TYPE_BYTE, &string, strlen(string) + 1);
975    dbus_message_iter_close_container(iter, &viter);
976 }
977
978 DBusMessage *
979 _ethumb_dbus_queue_add_cb(E_DBus_Object *object, DBusMessage *msg)
980 {
981    DBusMessage *reply;
982    DBusMessageIter iter;
983    const char *file, *key;
984    const char *thumb, *thumb_key;
985    struct _Ethumb_Object_Data *odata;
986    struct _Ethumb_Object *eobject;
987    struct _Ethumbd *ed;
988    struct _Ethumb_Request *request;
989    dbus_int32_t id = -1;
990
991    dbus_message_iter_init(msg, &iter);
992    dbus_message_iter_get_basic(&iter, &id);
993    dbus_message_iter_next(&iter);
994    file = _ethumb_dbus_get_bytearray(&iter);
995    dbus_message_iter_next(&iter);
996    key = _ethumb_dbus_get_bytearray(&iter);
997    dbus_message_iter_next(&iter);
998    thumb = _ethumb_dbus_get_bytearray(&iter);
999    dbus_message_iter_next(&iter);
1000    thumb_key = _ethumb_dbus_get_bytearray(&iter);
1001
1002    if (!file)
1003      {
1004         ERR("no filename given.");
1005         goto end;
1006      }
1007
1008    odata = e_dbus_object_data_get(object);
1009    if (!odata)
1010      {
1011         ERR("could not get dbus_object data.");
1012         goto end;
1013      }
1014
1015    ed = odata->ed;
1016    eobject = &(ed->queue.table[odata->index]);
1017    if (!_ethumb_dbus_check_id(eobject, id))
1018      goto end;
1019    request = calloc(1, sizeof(*request));
1020    request->id = id;
1021    request->file = file;
1022    request->key = key;
1023    request->thumb = thumb;
1024    request->thumb_key = thumb_key;
1025    eobject->queue = eina_list_append(eobject->queue, request);
1026    eobject->nqueue++;
1027    ed->queue.nqueue++;
1028    _ethumb_dbus_inc_max_id(eobject, id);
1029
1030    _process_queue_start(ed);
1031
1032  end:
1033    reply = dbus_message_new_method_return(msg);
1034    dbus_message_iter_init_append(reply, &iter);
1035    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &id);
1036    return reply;
1037 }
1038
1039 DBusMessage *
1040 _ethumb_dbus_queue_remove_cb(E_DBus_Object *object, DBusMessage *msg)
1041 {
1042    DBusMessage *reply;
1043    DBusMessageIter iter;
1044    dbus_int32_t id;
1045    struct _Ethumb_Object_Data *odata;
1046    struct _Ethumb_Object *eobject;
1047    struct _Ethumb_Request *request;
1048    struct _Ethumbd *ed;
1049    dbus_bool_t r = 0;
1050    Eina_List *l;
1051
1052    dbus_message_iter_init(msg, &iter);
1053    dbus_message_iter_get_basic(&iter, &id);
1054
1055    odata = e_dbus_object_data_get(object);
1056    if (!odata)
1057      {
1058         ERR("could not get dbus_object data.");
1059         goto end;
1060      }
1061
1062    ed = odata->ed;
1063    eobject = &ed->queue.table[odata->index];
1064    l = eobject->queue;
1065    while (l)
1066      {
1067         request = l->data;
1068         if (id == request->id)
1069           break;
1070         l = l->next;
1071      }
1072
1073    if (l)
1074      {
1075         r = 1;
1076         eina_stringshare_del(request->file);
1077         eina_stringshare_del(request->key);
1078         eina_stringshare_del(request->thumb);
1079         eina_stringshare_del(request->thumb_key);
1080         free(request);
1081         eobject->queue = eina_list_remove_list(eobject->queue, l);
1082         eobject->nqueue--;
1083         ed->queue.nqueue--;
1084         _ethumb_dbus_inc_min_id(eobject);
1085      }
1086
1087  end:
1088    reply = dbus_message_new_method_return(msg);
1089    dbus_message_iter_init_append(reply, &iter);
1090    dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &r);
1091    return reply;
1092 }
1093
1094 DBusMessage *
1095 _ethumb_dbus_queue_clear_cb(E_DBus_Object *object, DBusMessage *msg)
1096 {
1097    DBusMessage *reply;
1098    struct _Ethumb_Object_Data *odata;
1099    struct _Ethumb_Object *eobject;
1100    struct _Ethumbd *ed;
1101    Eina_List *l;
1102
1103    odata = e_dbus_object_data_get(object);
1104    if (!odata)
1105      {
1106         ERR("could not get dbus_object data.");
1107         goto end;
1108      }
1109
1110    ed = odata->ed;
1111    eobject = &ed->queue.table[odata->index];
1112    l = eobject->queue;
1113    while (l)
1114      {
1115         struct _Ethumb_Request *request = l->data;
1116         eina_stringshare_del(request->file);
1117         eina_stringshare_del(request->key);
1118         eina_stringshare_del(request->thumb);
1119         eina_stringshare_del(request->thumb_key);
1120         free(request);
1121         l = eina_list_remove_list(l, l);
1122      }
1123    ed->queue.nqueue -= eobject->nqueue;
1124    eobject->nqueue = 0;
1125
1126  end:
1127    reply = dbus_message_new_method_return(msg);
1128    return reply;
1129 }
1130
1131 DBusMessage *
1132 _ethumb_dbus_delete_cb(E_DBus_Object *object, DBusMessage *msg)
1133 {
1134    DBusMessage *reply;
1135    DBusMessageIter iter;
1136    struct _Ethumb_Object_Data *odata;
1137    struct _Ethumbd *ed;
1138
1139    dbus_message_iter_init(msg, &iter);
1140    reply = dbus_message_new_method_return(msg);
1141
1142    odata = e_dbus_object_data_get(object);
1143    if (!odata)
1144      {
1145         ERR("could not get dbus_object data for del_cb.");
1146         return reply;
1147      }
1148    ed = odata->ed;
1149    _ethumb_table_del(ed, odata->index);
1150    free(odata);
1151
1152    return reply;
1153 }
1154
1155 static int
1156 _ethumb_dbus_fdo_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request __UNUSED__)
1157 {
1158    int type;
1159    dbus_int32_t fdo;
1160
1161    type = dbus_message_iter_get_arg_type(iter);
1162    if (type != DBUS_TYPE_INT32)
1163      {
1164         ERR("invalid param for fdo_set.");
1165         return 0;
1166      }
1167
1168    dbus_message_iter_get_basic(iter, &fdo);
1169    DBG("setting fdo to: %d", fdo);
1170
1171    return 1;
1172 }
1173
1174 static int
1175 _ethumb_dbus_size_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1176 {
1177    DBusMessageIter oiter;
1178    int type;
1179    dbus_int32_t w, h;
1180
1181    type = dbus_message_iter_get_arg_type(iter);
1182    if (type != DBUS_TYPE_STRUCT)
1183      {
1184         ERR("invalid param for size_set.");
1185         return 0;
1186      }
1187
1188    dbus_message_iter_recurse(iter, &oiter);
1189    dbus_message_iter_get_basic(&oiter, &w);
1190    dbus_message_iter_next(&oiter);
1191    dbus_message_iter_get_basic(&oiter, &h);
1192    DBG("setting size to: %dx%d", w, h);
1193    request->setup.flags.size = 1;
1194    request->setup.tw = w;
1195    request->setup.th = h;
1196
1197    return 1;
1198 }
1199
1200 static int
1201 _ethumb_dbus_format_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1202 {
1203    int type;
1204    dbus_int32_t format;
1205
1206    type = dbus_message_iter_get_arg_type(iter);
1207    if (type != DBUS_TYPE_INT32)
1208      {
1209         ERR("invalid param for format_set.");
1210         return 0;
1211      }
1212
1213    dbus_message_iter_get_basic(iter, &format);
1214    DBG("setting format to: %d", format);
1215    request->setup.flags.format = 1;
1216    request->setup.format = format;
1217
1218    return 1;
1219 }
1220
1221 static int
1222 _ethumb_dbus_aspect_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1223 {
1224    int type;
1225    dbus_int32_t aspect;
1226
1227    type = dbus_message_iter_get_arg_type(iter);
1228    if (type != DBUS_TYPE_INT32)
1229      {
1230         ERR("invalid param for aspect_set.");
1231         return 0;
1232      }
1233
1234    dbus_message_iter_get_basic(iter, &aspect);
1235    DBG("setting aspect to: %d", aspect);
1236    request->setup.flags.aspect = 1;
1237    request->setup.aspect = aspect;
1238
1239    return 1;
1240 }
1241
1242 static int
1243 _ethumb_dbus_crop_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1244 {
1245    DBusMessageIter oiter;
1246    int type;
1247    double x, y;
1248
1249    type = dbus_message_iter_get_arg_type(iter);
1250    if (type != DBUS_TYPE_STRUCT)
1251      {
1252         ERR("invalid param for crop_set.");
1253         return 0;
1254      }
1255
1256    dbus_message_iter_recurse(iter, &oiter);
1257    dbus_message_iter_get_basic(&oiter, &x);
1258    dbus_message_iter_next(&oiter);
1259    dbus_message_iter_get_basic(&oiter, &y);
1260    DBG("setting crop to: %3.2f,%3.2f", x, y);
1261    request->setup.flags.crop = 1;
1262    request->setup.cx = x;
1263    request->setup.cy = y;
1264
1265    return 1;
1266 }
1267
1268 static int
1269 _ethumb_dbus_quality_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1270 {
1271    int type;
1272    dbus_int32_t quality;
1273
1274    type = dbus_message_iter_get_arg_type(iter);
1275    if (type != DBUS_TYPE_INT32)
1276      {
1277         ERR("invalid param for quality_set.");
1278         return 0;
1279      }
1280
1281    dbus_message_iter_get_basic(iter, &quality);
1282    DBG("setting quality to: %d", quality);
1283    request->setup.flags.quality = 1;
1284    request->setup.quality = quality;
1285
1286    return 1;
1287 }
1288
1289
1290 static int
1291 _ethumb_dbus_compress_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1292 {
1293    int type;
1294    dbus_int32_t compress;
1295
1296    type = dbus_message_iter_get_arg_type(iter);
1297    if (type != DBUS_TYPE_INT32)
1298      {
1299         ERR("invalid param for compress_set.");
1300         return 0;
1301      }
1302
1303    dbus_message_iter_get_basic(iter, &compress);
1304    DBG("setting compress to: %d", compress);
1305    request->setup.flags.compress = 1;
1306    request->setup.compress = compress;
1307
1308    return 1;
1309 }
1310
1311 static int
1312 _ethumb_dbus_frame_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1313 {
1314    DBusMessageIter oiter;
1315    int type;
1316    const char *file, *group, *swallow;
1317
1318    type = dbus_message_iter_get_arg_type(iter);
1319    if (type != DBUS_TYPE_STRUCT)
1320      {
1321         ERR("invalid param for frame_set.");
1322         return 0;
1323      }
1324
1325    dbus_message_iter_recurse(iter, &oiter);
1326    file = _ethumb_dbus_get_bytearray(&oiter);
1327    dbus_message_iter_next(&oiter);
1328    group = _ethumb_dbus_get_bytearray(&oiter);
1329    dbus_message_iter_next(&oiter);
1330    swallow = _ethumb_dbus_get_bytearray(&oiter);
1331    DBG("setting frame to \"%s:%s:%s\"", file, group, swallow);
1332    request->setup.flags.frame = 1;
1333    request->setup.theme_file = eina_stringshare_add(file);
1334    request->setup.group = eina_stringshare_add(group);
1335    request->setup.swallow = eina_stringshare_add(swallow);
1336
1337    return 1;
1338 }
1339
1340 static int
1341 _ethumb_dbus_directory_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1342 {
1343    int type;
1344    const char *directory;
1345
1346    type = dbus_message_iter_get_arg_type(iter);
1347    if (type != DBUS_TYPE_ARRAY)
1348      {
1349         ERR("invalid param for dir_path_set.");
1350         return 0;
1351      }
1352
1353    directory = _ethumb_dbus_get_bytearray(iter);
1354    DBG("setting directory to: %s", directory);
1355    request->setup.flags.directory = 1;
1356    request->setup.directory = eina_stringshare_add(directory);
1357
1358    return 1;
1359 }
1360
1361 static int
1362 _ethumb_dbus_category_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1363 {
1364    int type;
1365    const char *category;
1366
1367    type = dbus_message_iter_get_arg_type(iter);
1368    if (type != DBUS_TYPE_ARRAY)
1369      {
1370         ERR("invalid param for category.");
1371         return 0;
1372      }
1373
1374    category = _ethumb_dbus_get_bytearray(iter);
1375    DBG("setting category to: %s", category);
1376    request->setup.flags.category = 1;
1377    request->setup.category = eina_stringshare_add(category);
1378
1379    return 1;
1380 }
1381
1382 static int
1383 _ethumb_dbus_video_time_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1384 {
1385    int type;
1386    double video_time;
1387
1388    type = dbus_message_iter_get_arg_type(iter);
1389    if (type != DBUS_TYPE_DOUBLE)
1390      {
1391         ERR("invalid param for video_time_set.");
1392         return 0;
1393      }
1394
1395    dbus_message_iter_get_basic(iter, &video_time);
1396    DBG("setting video_time to: %3.2f", video_time);
1397    request->setup.flags.video_time = 1;
1398    request->setup.video_time = video_time;
1399
1400    return 1;
1401 }
1402
1403 static int
1404 _ethumb_dbus_video_start_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1405 {
1406    int type;
1407    double video_start;
1408
1409    type = dbus_message_iter_get_arg_type(iter);
1410    if (type != DBUS_TYPE_DOUBLE)
1411      {
1412         ERR("invalid param for video_start_set.");
1413         return 0;
1414      }
1415
1416    dbus_message_iter_get_basic(iter, &video_start);
1417    DBG("setting video_start to: %3.2f", video_start);
1418    request->setup.flags.video_start = 1;
1419    request->setup.video_start = video_start;
1420
1421    return 1;
1422 }
1423
1424 static int
1425 _ethumb_dbus_video_interval_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1426 {
1427    int type;
1428    double video_interval;
1429
1430    type = dbus_message_iter_get_arg_type(iter);
1431    if (type != DBUS_TYPE_DOUBLE)
1432      {
1433         ERR("invalid param for video_interval_set.");
1434         return 0;
1435      }
1436
1437    dbus_message_iter_get_basic(iter, &video_interval);
1438    DBG("setting video_interval to: %3.2f", video_interval);
1439    request->setup.flags.video_interval = 1;
1440    request->setup.video_interval = video_interval;
1441
1442    return 1;
1443 }
1444
1445 static int
1446 _ethumb_dbus_video_ntimes_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1447 {
1448    int type;
1449    unsigned int video_ntimes;
1450
1451    type = dbus_message_iter_get_arg_type(iter);
1452    if (type != DBUS_TYPE_UINT32)
1453      {
1454         ERR("invalid param for video_ntimes_set.");
1455         return 0;
1456      }
1457
1458    dbus_message_iter_get_basic(iter, &video_ntimes);
1459    DBG("setting video_ntimes to: %3.2d", video_ntimes);
1460    request->setup.flags.video_ntimes = 1;
1461    request->setup.video_ntimes = video_ntimes;
1462
1463    return 1;
1464 }
1465
1466 static int
1467 _ethumb_dbus_video_fps_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1468 {
1469    int type;
1470    unsigned int video_fps;
1471
1472    type = dbus_message_iter_get_arg_type(iter);
1473    if (type != DBUS_TYPE_UINT32)
1474      {
1475         ERR("invalid param for video_fps_set.");
1476         return 0;
1477      }
1478
1479    dbus_message_iter_get_basic(iter, &video_fps);
1480    DBG("setting video_fps to: %3.2d", video_fps);
1481    request->setup.flags.video_fps = 1;
1482    request->setup.video_fps = video_fps;
1483
1484    return 1;
1485 }
1486
1487 static int
1488 _ethumb_dbus_document_page_set(struct _Ethumb_Object *eobject __UNUSED__, DBusMessageIter *iter, struct _Ethumb_Request *request)
1489 {
1490    int type;
1491    unsigned int document_page;
1492
1493    type = dbus_message_iter_get_arg_type(iter);
1494    if (type != DBUS_TYPE_UINT32)
1495      {
1496         ERR("invalid param for document_page_set.");
1497         return 0;
1498      }
1499
1500    dbus_message_iter_get_basic(iter, &document_page);
1501    DBG("setting document_page to: %d", document_page);
1502    request->setup.flags.document_page = 1;
1503    request->setup.document_page = document_page;
1504
1505    return 1;
1506 }
1507
1508 static struct
1509 {
1510    const char *option;
1511    int (*setup_func)(struct _Ethumb_Object *eobject, DBusMessageIter *iter, struct _Ethumb_Request *request);
1512 } _option_cbs[] = {
1513   {"fdo", _ethumb_dbus_fdo_set},
1514   {"size", _ethumb_dbus_size_set},
1515   {"format", _ethumb_dbus_format_set},
1516   {"aspect", _ethumb_dbus_aspect_set},
1517   {"crop", _ethumb_dbus_crop_set},
1518   {"quality", _ethumb_dbus_quality_set},
1519   {"compress", _ethumb_dbus_compress_set},
1520   {"frame", _ethumb_dbus_frame_set},
1521   {"directory", _ethumb_dbus_directory_set},
1522   {"category", _ethumb_dbus_category_set},
1523   {"video_time", _ethumb_dbus_video_time_set},
1524   {"video_start", _ethumb_dbus_video_start_set},
1525   {"video_interval", _ethumb_dbus_video_interval_set},
1526   {"video_ntimes", _ethumb_dbus_video_ntimes_set},
1527   {"video_fps", _ethumb_dbus_video_fps_set},
1528   {"document_page", _ethumb_dbus_document_page_set},
1529   {NULL, NULL}
1530 };
1531
1532 static int
1533 _ethumb_dbus_ethumb_setup_parse_element(struct _Ethumb_Object *eobject, DBusMessageIter *iter, struct _Ethumb_Request *request)
1534 {
1535    DBusMessageIter viter, diter;
1536    const char *option;
1537    int i, r;
1538
1539    dbus_message_iter_recurse(iter, &diter);
1540    dbus_message_iter_get_basic(&diter, &option);
1541    dbus_message_iter_next(&diter);
1542
1543    r = 0;
1544    for (i = 0; _option_cbs[i].option; i++)
1545      if (!strcmp(_option_cbs[i].option, option))
1546        {
1547           r = 1;
1548           break;
1549        }
1550
1551    if (!r)
1552      {
1553         ERR("ethumb_setup invalid option: %s", option);
1554         return 0;
1555      }
1556
1557    dbus_message_iter_recurse(&diter, &viter);
1558    return _option_cbs[i].setup_func(eobject, &viter, request);
1559 }
1560
1561 DBusMessage *
1562 _ethumb_dbus_ethumb_setup_cb(E_DBus_Object *object, DBusMessage *msg)
1563 {
1564    DBusMessage *reply;
1565    DBusMessageIter iter, aiter;
1566    struct _Ethumb_Object_Data *odata;
1567    struct _Ethumbd *ed;
1568    struct _Ethumb_Object *eobject;
1569    struct _Ethumb_Request *request;
1570    dbus_bool_t r = 0;
1571    int atype;
1572
1573    dbus_message_iter_init(msg, &iter);
1574    if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
1575      {
1576         ERR("wrong parameters.");
1577         goto end;
1578      }
1579
1580    odata = e_dbus_object_data_get(object);
1581    if (!odata)
1582      {
1583         ERR("could not get dbus_object data for setup_cb.");
1584         goto end;
1585      }
1586
1587    ed = odata->ed;
1588    eobject = &ed->queue.table[odata->index];
1589
1590    request = calloc(1, sizeof(*request));
1591    request->id = -1;
1592    dbus_message_iter_recurse(&iter, &aiter);
1593    atype = dbus_message_iter_get_arg_type(&aiter);
1594
1595    r = 1;
1596    while (atype != DBUS_TYPE_INVALID)
1597      {
1598         if (!_ethumb_dbus_ethumb_setup_parse_element(eobject, &aiter, request))
1599           r = 0;
1600         dbus_message_iter_next(&aiter);
1601         atype = dbus_message_iter_get_arg_type(&aiter);
1602      }
1603
1604    eobject->queue = eina_list_append(eobject->queue, request);
1605    eobject->nqueue++;
1606    ed->queue.nqueue++;
1607
1608  end:
1609    reply = dbus_message_new_method_return(msg);
1610    dbus_message_iter_init_append(reply, &iter);
1611    dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &r);
1612
1613    return reply;
1614 }
1615
1616 static void
1617 _ethumb_dbus_generated_signal(struct _Ethumbd *ed, int *id, const char *thumb_path, const char *thumb_key, Eina_Bool success)
1618 {
1619    DBusMessage *signal;
1620    int current;
1621    const char *opath;
1622    DBusMessageIter iter;
1623    dbus_bool_t value;
1624    dbus_int32_t id32;
1625
1626    value = success;
1627    id32 = *id;
1628
1629    current = ed->queue.current;
1630    opath = ed->queue.table[current].path;
1631    signal = dbus_message_new_signal
1632      (opath, _ethumb_dbus_objects_interface, "generated");
1633
1634    dbus_message_iter_init_append(signal, &iter);
1635    dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &id32);
1636    _ethumb_dbus_append_bytearray(&iter, thumb_path);
1637    _ethumb_dbus_append_bytearray(&iter, thumb_key);
1638    dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &value);
1639
1640    e_dbus_message_send(ed->conn, signal, NULL, -1, NULL);
1641    dbus_message_unref(signal);
1642 }
1643
1644 static struct _Ethumb_DBus_Method_Table _ethumb_dbus_objects_methods[] = {
1645   {"queue_add", "iayayayay", "i", _ethumb_dbus_queue_add_cb},
1646   {"queue_remove", "i", "b", _ethumb_dbus_queue_remove_cb},
1647   {"clear_queue", "", "", _ethumb_dbus_queue_clear_cb},
1648   {"ethumb_setup", "a{sv}", "b", _ethumb_dbus_ethumb_setup_cb},
1649   {"delete", "", "", _ethumb_dbus_delete_cb},
1650   {NULL, NULL, NULL, NULL}
1651 };
1652
1653 static struct _Ethumb_DBus_Signal_Table _ethumb_dbus_objects_signals[] = {
1654   {"generated", "iayayb"},
1655   {NULL, NULL}
1656 };
1657
1658 static int
1659 _ethumb_dbus_interface_elements_add(E_DBus_Interface *iface, struct _Ethumb_DBus_Method_Table *mtable, struct _Ethumb_DBus_Signal_Table *stable)
1660 {
1661    int i = -1;
1662    while (mtable && mtable[++i].name != NULL)
1663      if (!e_dbus_interface_method_add(iface,
1664                                       mtable[i].name,
1665                                       mtable[i].signature,
1666                                       mtable[i].reply,
1667                                       mtable[i].function))
1668        return 0;
1669
1670    i = -1;
1671    while (stable && stable[++i].name != NULL)
1672      if (!e_dbus_interface_signal_add(iface,
1673                                       stable[i].name,
1674                                       stable[i].signature))
1675        return 0;
1676    return 1;
1677 }
1678
1679 static void
1680 _ethumb_dbus_request_name_cb(void *data, DBusMessage *msg __UNUSED__, DBusError *err)
1681 {
1682    E_DBus_Object *dbus_object;
1683    struct _Ethumbd *ed = data;
1684    int r;
1685
1686    if (dbus_error_is_set(err))
1687      {
1688         ERR("request name error: %s", err->message);
1689         dbus_error_free(err);
1690         e_dbus_connection_close(ed->conn);
1691         return;
1692      }
1693
1694    dbus_object = e_dbus_object_add(ed->conn, _ethumb_dbus_path, ed);
1695    if (!dbus_object)
1696      return;
1697    ed->dbus_obj = dbus_object;
1698    ed->eiface = e_dbus_interface_new(_ethumb_dbus_interface);
1699    if (!ed->eiface)
1700      {
1701         ERR("could not create interface.");
1702         return;
1703      }
1704    r = _ethumb_dbus_interface_elements_add(ed->eiface,
1705                                            _ethumb_dbus_methods, NULL);
1706    if (!r)
1707      {
1708         ERR("could not add methods to the interface.");
1709         e_dbus_interface_unref(ed->eiface);
1710         return;
1711      }
1712    e_dbus_object_interface_attach(dbus_object, ed->eiface);
1713
1714    ed->objects_iface = e_dbus_interface_new(_ethumb_dbus_objects_interface);
1715    if (!ed->objects_iface)
1716      {
1717         ERR("could not create interface.");
1718         return;
1719      }
1720
1721    r = _ethumb_dbus_interface_elements_add(ed->objects_iface,
1722                                            _ethumb_dbus_objects_methods,
1723                                            _ethumb_dbus_objects_signals);
1724    if (!r)
1725      {
1726         ERR("ERROR: could not setup objects interface methods.");
1727         e_dbus_interface_unref(ed->objects_iface);
1728         return;
1729      }
1730
1731    _ethumb_dbus_add_name_owner_changed_cb(ed);
1732
1733    _ethumbd_timeout_start(ed);
1734 }
1735
1736 static int
1737 _ethumb_dbus_setup(struct _Ethumbd *ed)
1738 {
1739    e_dbus_request_name
1740      (ed->conn, _ethumb_dbus_bus_name, 0, _ethumb_dbus_request_name_cb, ed);
1741
1742    return 1;
1743 }
1744
1745 static void
1746 _ethumb_dbus_finish(struct _Ethumbd *ed)
1747 {
1748    _process_queue_stop(ed);
1749    _ethumb_table_clear(ed);
1750    e_dbus_signal_handler_del(ed->conn, ed->name_owner_changed_handler);
1751    e_dbus_interface_unref(ed->objects_iface);
1752    e_dbus_interface_unref(ed->eiface);
1753    e_dbus_object_free(ed->dbus_obj);
1754    free(ed->queue.table);
1755    free(ed->queue.list);
1756 }
1757
1758 static int
1759 _ethumbd_slave_spawn(struct _Ethumbd *ed)
1760 {
1761    ed->slave.data_cb = ecore_event_handler_add(
1762       ECORE_EXE_EVENT_DATA, _ethumbd_slave_data_read_cb, ed);
1763    ed->slave.del_cb = ecore_event_handler_add(
1764       ECORE_EXE_EVENT_DEL, _ethumbd_slave_del_cb, ed);
1765
1766    ed->slave.bufcmd = NULL;
1767    ed->slave.scmd = 0;
1768
1769    ed->slave.exe = ecore_exe_pipe_run(
1770       ETHUMB_LIBEXEC_DIR"/ethumbd_slave",
1771       ECORE_EXE_PIPE_READ | ECORE_EXE_PIPE_WRITE, ed);
1772    if (!ed->slave.exe)
1773      {
1774         ERR("could not create slave.");
1775         return 0;
1776      }
1777
1778    return 1;
1779 }
1780
1781 int
1782 main(int argc, char *argv[])
1783 {
1784    Eina_Bool quit_option = 0;
1785    int exit_value = 0;
1786    int arg_index;
1787    struct _Ethumbd ed;
1788    int child;
1789    double timeout = -1;
1790
1791    memset(&ed, 0, sizeof(ed));
1792    ecore_init();
1793    eina_init();
1794
1795    ethumb_init();
1796
1797    if (_log_domain < 0)
1798      {
1799         _log_domain = eina_log_domain_register("ethumbd", NULL);
1800         if (_log_domain < 0)
1801           {
1802              EINA_LOG_CRIT("could not register log domain 'ethumbd'");
1803              exit_value = -7;
1804              goto finish;
1805           }
1806      }
1807
1808    child = _ethumbd_slave_spawn(&ed);
1809    if (!child)
1810      {
1811         exit_value = -6;
1812         goto finish;
1813      }
1814
1815    if (child == 2)
1816      {
1817         exit_value = 0;
1818         goto finish;
1819      }
1820
1821    if (!e_dbus_init())
1822      {
1823         ERR("could not init e_dbus.");
1824         exit_value = -1;
1825         goto finish;
1826      }
1827
1828    Ecore_Getopt_Value values[] = {
1829      ECORE_GETOPT_VALUE_DOUBLE(timeout),
1830      ECORE_GETOPT_VALUE_BOOL(quit_option),
1831      ECORE_GETOPT_VALUE_BOOL(quit_option),
1832      ECORE_GETOPT_VALUE_BOOL(quit_option),
1833      ECORE_GETOPT_VALUE_BOOL(quit_option),
1834      ECORE_GETOPT_VALUE_NONE
1835    };
1836
1837    arg_index = ecore_getopt_parse(&optdesc, values, argc, argv);
1838    if (arg_index < 0)
1839      {
1840         ERR("Could not parse arguments.");
1841         exit_value = -2;
1842         goto finish;
1843      }
1844
1845    if (quit_option)
1846      goto finish;
1847
1848    ed.conn = e_dbus_bus_get(DBUS_BUS_SESSION);
1849    if (!ed.conn)
1850      {
1851         ERR("could not connect to session bus.");
1852         exit_value = -3;
1853         goto finish_edbus;
1854      }
1855
1856    ed.timeout = timeout;
1857
1858    if (!_ethumb_dbus_setup(&ed))
1859      {
1860         e_dbus_connection_close(ed.conn);
1861         ERR("could not setup dbus connection.");
1862         exit_value = -5;
1863         goto finish_edbus;
1864      }
1865
1866    ecore_main_loop_begin();
1867    _ethumb_dbus_finish(&ed);
1868
1869  finish_edbus:
1870    if (_log_domain >= 0)
1871      {
1872         eina_log_domain_unregister(_log_domain);
1873         _log_domain = -1;
1874      }
1875
1876    e_dbus_shutdown();
1877  finish:
1878    if (ed.slave.exe)
1879      ecore_exe_quit(ed.slave.exe);
1880    ethumb_shutdown();
1881    eina_init();
1882    ecore_shutdown();
1883    return exit_value;
1884 }