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