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