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