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