95f471a3a2d43069266c83d34fd68b2ee7fc009a
[profile/ivi/speech-recognition.git] / src / plugins / client-api / dbus / dbus-client.c
1 /*
2  * Copyright (c) 2012, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *   * Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *   * Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  *   * Neither the name of Intel Corporation nor the names of its contributors
14  *     may be used to endorse or promote products derived from this software
15  *     without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdlib.h>
31 #include <errno.h>
32
33 #include <murphy/common/log.h>
34 #include <murphy/common/debug.h>
35 #include <murphy/common/dbus-libdbus.h>
36
37 #include "srs/daemon/plugin.h"
38 #include "srs/daemon/client.h"
39
40 #include "dbus-config.h"
41
42 #define PLUGIN_NAME    "dbus-client"
43 #define PLUGIN_DESCR   "A D-Bus client plugin for SRS."
44 #define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
45 #define PLUGIN_VERSION "0.0.1"
46
47 #define BUS_CONFIG  "dbus.address"
48 #define BUS_DEFAULT "session"
49
50 #define MAX_COMMANDS 256
51
52 static int register_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
53 static int unregister_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
54                           void *user_data);
55 static int focus_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
56 static int render_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
57                             void *user_data);
58 static int cancel_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
59                             void *user_data);
60 static int query_voices_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
61                             void *user_data);
62
63 static int focus_notify(srs_client_t *c, srs_voice_focus_t focus);
64 static int command_notify(srs_client_t *c, int idx, int ntoken, char **tokens,
65                           uint32_t *start, uint32_t *end,
66                           srs_audiobuf_t *audio);
67 static int voice_notify(srs_client_t *c, srs_voice_event_t *event);
68
69 #define reply_error      simple_reply
70 #define reply_register   simple_reply
71 #define reply_unregister simple_reply
72 #define reply_focus      simple_reply
73 #define reply_cancel     simple_reply
74
75 typedef struct {
76     srs_plugin_t *self;                  /* our plugin instance */
77     const char   *address;               /* bus address */
78     mrp_dbus_t   *dbus;                  /* bus we're on */
79 } dbusif_t;
80
81
82 static void dbusif_cleanup(dbusif_t *bus);
83
84
85 static int dbusif_setup(dbusif_t *bus)
86 {
87     srs_context_t  *srs = bus->self->srs;
88     const char     *path, *iface, *method;
89     int           (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
90
91     mrp_debug("setting up client D-BUS interface (%s)", bus->address);
92
93     bus->dbus = mrp_dbus_get(srs->ml, bus->address, NULL);
94
95     if (bus->dbus != NULL) {
96         path   = SRS_CLIENT_PATH;
97         iface  = SRS_CLIENT_INTERFACE;
98
99         method = SRS_CLIENT_REGISTER;
100         cb     = register_req;
101         if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
102             mrp_log_error("Failed to register D-BUS '%s' method.", method);
103             goto fail;
104         }
105
106         method = SRS_CLIENT_UNREGISTER;
107         cb     = unregister_req;
108         if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
109             mrp_log_error("Failed to register D-BUS '%s' method.", method);
110             goto fail;
111         }
112
113         method = SRS_CLIENT_REQUEST_FOCUS;
114         cb     = focus_req;
115         if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
116             mrp_log_error("Failed to register D-BUS '%s' method.", method);
117             goto fail;
118         }
119
120         method = SRS_CLIENT_RENDER_VOICE;
121         cb     = render_voice_req;
122         if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
123             mrp_log_error("Failed to register D-BUS '%s' method.", method);
124             goto fail;
125         }
126
127         method = SRS_CLIENT_CANCEL_VOICE;
128         cb     = cancel_voice_req;
129         if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
130             mrp_log_error("Failed to register D-BUS '%s' method.", method);
131             goto fail;
132         }
133
134         method = SRS_CLIENT_QUERY_VOICES;
135         cb     = query_voices_req;
136         if (!mrp_dbus_export_method(bus->dbus, path, iface, method, cb, bus)) {
137             mrp_log_error("Failed to register D-BUS '%s' method.", method);
138             goto fail;
139         }
140
141         if (!mrp_dbus_acquire_name(bus->dbus, SRS_CLIENT_SERVICE, NULL)) {
142             mrp_log_error("Failed to acquire D-BUS name '%s'.",
143                           SRS_CLIENT_SERVICE);
144             goto fail;
145         }
146
147     }
148     else {
149         mrp_log_error("Failed to connect to D-BUS (%s).", bus->address);
150         goto fail;
151     }
152
153     return TRUE;
154
155  fail:
156     dbusif_cleanup(bus);
157     return FALSE;
158 }
159
160
161 static void dbusif_cleanup(dbusif_t *bus)
162 {
163     srs_context_t  *srs = bus->self->srs;
164     const char     *path, *iface, *method;
165     int           (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
166
167     mrp_debug("cleaning up client D-BUS interface");
168
169     if (bus->dbus != NULL) {
170         mrp_dbus_release_name(bus->dbus, SRS_CLIENT_SERVICE, NULL);
171
172         path   = SRS_CLIENT_PATH;
173         iface  = SRS_CLIENT_INTERFACE;
174
175         method = SRS_CLIENT_REGISTER;
176         cb     = register_req;
177         mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
178
179         method = SRS_CLIENT_UNREGISTER;
180         cb     = unregister_req;
181         mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
182
183         method = SRS_CLIENT_REQUEST_FOCUS;
184         cb     = focus_req;
185         mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
186
187         method = SRS_CLIENT_RENDER_VOICE;
188         cb     = render_voice_req;
189         mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
190
191         method = SRS_CLIENT_CANCEL_VOICE;
192         cb     = cancel_voice_req;
193         mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
194
195         method = SRS_CLIENT_QUERY_VOICES;
196         cb     = query_voices_req;
197         mrp_dbus_remove_method(bus->dbus, path, iface, method, cb, bus);
198
199         mrp_dbus_unref(bus->dbus);
200         bus->dbus = NULL;
201     }
202 }
203
204
205 static void name_change_cb(mrp_dbus_t *dbus, const char *name, int running,
206                            const char *owner, void *user_data)
207 {
208     dbusif_t      *bus = (dbusif_t *)user_data;
209     srs_context_t *srs = bus->self->srs;
210     srs_client_t  *c;
211
212     MRP_UNUSED(owner);
213
214     mrp_debug("D-BUS client %s %s", name, running ? "up" : "down");
215
216     if (!running) {
217         c = client_lookup_by_id(srs, name);
218
219         if (c != NULL) {
220             mrp_log_info("client %s disconnected from D-BUS", name);
221             client_destroy(c);
222             mrp_dbus_forget_name(dbus, name, name_change_cb, bus);
223         }
224     }
225 }
226
227
228 static void simple_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, int errcode,
229                          const char *errmsg)
230 {
231     int32_t error;
232
233     if (!errcode)
234         mrp_dbus_reply(dbus, req, MRP_DBUS_TYPE_INVALID);
235     else {
236         error = errcode;
237         mrp_dbus_reply_error(dbus, req, MRP_DBUS_ERROR_FAILED, errmsg,
238                              MRP_DBUS_TYPE_INT32, &error,
239                              MRP_DBUS_TYPE_INVALID);
240     }
241 }
242
243
244 static void reply_render(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, uint32_t id)
245 {
246     mrp_dbus_reply(dbus, req, MRP_DBUS_TYPE_UINT32, &id,
247                    MRP_DBUS_TYPE_INVALID);
248 }
249
250
251 static char *clear_non_us_ascii(char *s)
252 {
253     char *p;
254
255     for (p = s; *p; p++) {
256         if (*p & 0x80)
257             *p = '?';
258     }
259
260     return s;
261 }
262
263
264 static void reply_voice_query(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, int nactor,
265                               srs_voice_actor_t *actors)
266 {
267     srs_voice_actor_t *a;
268     char              *voices[nactor], **v;
269     char              *lang[nactor], **ml;
270     char              *dialect[nactor], **sl;
271     char              *gender[nactor], **g;
272     char              *description[nactor], **d;
273     uint32_t           n;
274     int                i;
275
276     a  = actors;
277     v  = voices;
278     ml = lang;
279     sl = dialect;
280     g  = gender;
281     d  = description;
282     for (i = 0; i < nactor; i++, a++, v++, ml++, sl++, g++, d++) {
283         *v  = a->name;
284         *ml = a->lang;
285         *sl = a->dialect ? a->dialect : "";
286         *g  = a->gender == SRS_VOICE_GENDER_MALE ? "male" : "female";
287
288         /*
289          * XXX TODO: this is a hack is currently needed for festival
290          * which can feed us voice descriptions that are not UTF-8
291          * (and consequently not 7-bit ASCII either).
292          */
293         *d  = clear_non_us_ascii(a->description);
294
295         printf("* description: %s\n", *d);
296     }
297
298     n = nactor;
299     v  = voices;
300     ml = lang;
301     sl = dialect;
302     g  = gender;
303     d  = description;
304     mrp_dbus_reply(dbus, req,
305                    MRP_DBUS_TYPE_UINT32, &n,
306                    MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &v , n,
307                    MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &ml, n,
308                    MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &sl, n,
309                    MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &g , n,
310                    MRP_DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &d , n,
311                    MRP_DBUS_TYPE_INVALID);
312 }
313
314
315 static int parse_commands(mrp_dbus_msg_t *msg, char **commands, int ncommand)
316 {
317     int n;
318
319     n = 0;
320     while (n < ncommand - 1) {
321         if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, commands + n))
322             n++;
323         else
324             return -1;
325     }
326
327     return n;
328 }
329
330
331 static int parse_register(mrp_dbus_msg_t *req, const char **id,
332                           const char **name, const char **appclass,
333                           char ***commands, int *ncommand, const char **errmsg)
334 {
335     void   *cmds;
336     size_t  ncmd;
337
338     *id = mrp_dbus_msg_sender(req);
339
340     if (*id == NULL) {
341         *errmsg = "failed to parse register message";
342         return EINVAL;
343     }
344
345     if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, name))
346         goto malformed;
347
348     if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, appclass))
349         goto malformed;
350
351     if (mrp_dbus_msg_read_array(req, MRP_DBUS_TYPE_STRING, &cmds, &ncmd)) {
352         if (ncmd > 0) {
353             *commands = cmds;
354             *ncommand = (int)ncmd;
355
356             return 0;
357         }
358     }
359
360  malformed:
361     *errmsg = "malformed register message";
362     return EINVAL;
363 }
364
365
366 static int register_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, void *user_data)
367 {
368     static srs_client_ops_t ops = {
369         .notify_focus   = focus_notify,
370         .notify_command = command_notify,
371         .notify_render  = voice_notify,
372     };
373
374     dbusif_t        *bus = (dbusif_t *)user_data;
375     srs_context_t   *srs = bus->self->srs;
376     const char      *id, *name, *appcls, *errmsg;
377     char           **cmds;
378     int              ncmd, err;
379     srs_client_t    *c;
380
381     ncmd = MRP_ARRAY_SIZE(cmds);
382     err  = parse_register(req, &id, &name, &appcls, &cmds, &ncmd, &errmsg);
383
384     if (err) {
385         reply_register(dbus, req, err, errmsg);
386
387         return TRUE;
388     }
389
390     mrp_debug("got register request from %s", id);
391
392     c = client_create(srs, SRS_CLIENT_TYPE_EXTERNAL, name, appcls, cmds, ncmd,
393                       id, &ops, bus);
394
395     if (c != NULL) {
396         if (mrp_dbus_follow_name(dbus, id, name_change_cb, bus)) {
397             err    = 0;
398             errmsg = NULL;
399         }
400         else {
401             client_destroy(c);
402             err    = EINVAL;
403             errmsg = "failed to track DBUS name";
404         }
405     }
406     else {
407         err    = EINVAL;
408         errmsg = "failed to register client";
409     }
410
411     reply_register(dbus, req, err, errmsg);
412     return TRUE;
413 }
414
415
416 static int parse_unregister(mrp_dbus_msg_t *req, const char **id,
417                             const char **errmsg)
418 {
419     *id = mrp_dbus_msg_sender(req);
420
421     if (*id != NULL)
422         return 0;
423     else {
424         *errmsg = "failed to determine client id";
425         return EINVAL;
426     }
427 }
428
429
430 static int unregister_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
431                           void *user_data)
432 {
433     dbusif_t      *bus = (dbusif_t *)user_data;
434     srs_context_t *srs = bus->self->srs;
435     const char    *id, *errmsg;
436     srs_client_t  *c;
437     int            err;
438
439     err = parse_unregister(req, &id, &errmsg);
440
441     if (!err) {
442         mrp_debug("got unregister request from %s", id);
443
444         c = client_lookup_by_id(srs, id);
445
446         if (c != NULL) {
447             mrp_dbus_forget_name(dbus, c->id, name_change_cb, bus);
448             client_destroy(c);
449             reply_unregister(dbus, req, 0, NULL);
450         }
451         else
452             reply_unregister(dbus, req, 1, "you don't exist, go away");
453     }
454     else
455         reply_unregister(dbus, req, err, errmsg);
456
457     return TRUE;
458 }
459
460
461 static int parse_focus(mrp_dbus_msg_t *req, const char **id, int *focus,
462                        const char **errmsg)
463 {
464     const char *type;
465
466     *id = mrp_dbus_msg_sender(req);
467
468     if (*id != NULL) {
469         if (mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, &type)) {
470             if (!strcmp(type, "none"))
471                 *focus = SRS_VOICE_FOCUS_NONE;
472             else if (!strcmp(type, "shared"   ))
473                 *focus = SRS_VOICE_FOCUS_SHARED;
474             else if (!strcmp(type, "exclusive"))
475                 *focus = SRS_VOICE_FOCUS_EXCLUSIVE;
476             else {
477                 *errmsg = "invalid voice focus requested";
478                 return EINVAL;
479             }
480
481             return 0;
482         }
483         else
484             *errmsg = "malformed voice focus request";
485     }
486     else
487         *errmsg = "failed to determine client id";
488
489     return EINVAL;
490 }
491
492
493 static int focus_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req, void *user_data)
494 {
495     dbusif_t      *bus = (dbusif_t *)user_data;
496     srs_context_t *srs = bus->self->srs;
497     const char    *id, *errmsg;
498     int            focus, err;
499     srs_client_t  *c;
500
501     err = parse_focus(req, &id, &focus, &errmsg);
502
503     if (err == 0) {
504         mrp_debug("got 0x%x focus request from %s", focus, id);
505
506         c = client_lookup_by_id(srs, id);
507
508         if (c != NULL) {
509             if (client_request_focus(c, focus))
510                 reply_focus(dbus, req, 0, NULL);
511             else
512                 reply_focus(dbus, req, 1, "focus request failed");
513         }
514         else
515             reply_focus(dbus, req, 1, "you don't exist, go away");
516     }
517     else
518         reply_focus(dbus, req, err, errmsg);
519
520     return TRUE;
521 }
522
523
524 static int focus_notify(srs_client_t *c, srs_voice_focus_t focus)
525 {
526     dbusif_t      *bus   = (dbusif_t *)c->user_data;
527     srs_context_t *srs   = c->srs;
528     const char    *dest  = c->id;
529     const char    *path  = SRS_CLIENT_PATH;
530     const char    *iface = SRS_CLIENT_INTERFACE;
531     const char    *sig   = SRS_CLIENT_NOTIFY_FOCUS;
532     const char    *state;
533
534     switch (focus) {
535     case SRS_VOICE_FOCUS_NONE:      state = "none";      break;
536     case SRS_VOICE_FOCUS_SHARED:    state = "shared";    break;
537     case SRS_VOICE_FOCUS_EXCLUSIVE: state = "exclusive"; break;
538     default:                        return FALSE;
539     }
540
541     return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
542                            MRP_DBUS_TYPE_STRING, state, MRP_DBUS_TYPE_INVALID);
543 }
544
545
546 static int command_notify(srs_client_t *c, int idx, int ntoken, char **tokens,
547                           uint32_t *start, uint32_t *end, srs_audiobuf_t *audio)
548 {
549     dbusif_t      *bus   = (dbusif_t *)c->user_data;
550     srs_context_t *srs   = c->srs;
551     const char    *dest  = c->id;
552     const char    *path  = SRS_CLIENT_PATH;
553     const char    *iface = SRS_CLIENT_INTERFACE;
554     const char    *sig   = SRS_CLIENT_NOTIFY_COMMAND;
555
556     char           buf[1024], *cmd, *p, *t;
557     int            i, n, l;
558
559     MRP_UNUSED(idx);
560     MRP_UNUSED(start);
561     MRP_UNUSED(end);
562     MRP_UNUSED(audio);
563
564     p = cmd = buf;
565     l = sizeof(buf) - 1;
566     t = "";
567
568     for (i = 0; i < ntoken; i++) {
569         n = snprintf(p, l, "%s%s", t, tokens[i]);
570
571         if (n >= l)
572             return FALSE;
573
574         p += n;
575         l -= n;
576         t  = " ";
577     }
578
579     return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
580                            MRP_DBUS_TYPE_STRING, cmd, MRP_DBUS_TYPE_INVALID);
581 }
582
583
584 static int voice_notify(srs_client_t *c, srs_voice_event_t *event)
585 {
586     dbusif_t      *bus   = (dbusif_t *)c->user_data;
587     srs_context_t *srs   = c->srs;
588     const char    *dest  = c->id;
589     const char    *path  = SRS_CLIENT_PATH;
590     const char    *iface = SRS_CLIENT_INTERFACE;
591     const char    *sig   = SRS_CLIENT_NOTIFY_VOICE;
592     const char    *type;
593     double         pcnt;
594     uint32_t       msec;
595
596
597     switch (event->type) {
598     case SRS_VOICE_EVENT_STARTED:   type = "started"  ; goto send;
599     case SRS_VOICE_EVENT_COMPLETED: type = "completed"; goto send;
600     case SRS_VOICE_EVENT_TIMEOUT:   type = "timeout"  ; goto send;
601     case SRS_VOICE_EVENT_ABORTED:   type = "aborted"  ; goto send;
602     send:
603         return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
604                                MRP_DBUS_TYPE_UINT32, &event->id,
605                                MRP_DBUS_TYPE_STRING, type,
606                                MRP_DBUS_TYPE_INVALID);
607
608     case SRS_VOICE_EVENT_PROGRESS:
609         type = "progress";
610         pcnt = event->data.progress.pcnt;
611         msec = event->data.progress.msec;
612
613         return mrp_dbus_signal(bus->dbus, dest, path, iface, sig,
614                                MRP_DBUS_TYPE_UINT32, &event->id,
615                                MRP_DBUS_TYPE_STRING, type,
616                                MRP_DBUS_TYPE_DOUBLE, &pcnt,
617                                MRP_DBUS_TYPE_UINT32, &msec,
618                                MRP_DBUS_TYPE_INVALID);
619
620     default:
621         return TRUE;
622     }
623 }
624
625
626 static int parse_render_voice(mrp_dbus_msg_t *req, const char **id,
627                               const char **msg, const char **voice,
628                               int *timeout, int *notify_events,
629                               const char **errmsg)
630 {
631     char    **events, *e;
632     int       i;
633     size_t    nevent;
634     int32_t   to;
635
636     *id = mrp_dbus_msg_sender(req);
637
638     if (*id == NULL)
639         return EINVAL;
640
641     if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, msg) ||
642         !mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, voice) ||
643         !mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_INT32 , &to) ||
644         !mrp_dbus_msg_read_array(req, MRP_DBUS_TYPE_STRING,
645                                  (void **)&events, &nevent)) {
646         *errmsg = "malformed voice render message";
647
648         return EINVAL;
649     }
650
651     *timeout       = to;
652     *notify_events = 0;
653
654     for (i = 0; i < nevent; i++) {
655         e = events[i];
656
657         if (!strcmp(e, SRS_CLIENT_VOICE_STARTED))
658             *notify_events |= SRS_VOICE_MASK_STARTED;
659         else if (!strcmp(e, SRS_CLIENT_VOICE_PROGRESS))
660             *notify_events |= SRS_VOICE_MASK_PROGRESS;
661         else if (!strcmp(e, SRS_CLIENT_VOICE_COMPLETED))
662             *notify_events |= SRS_VOICE_MASK_COMPLETED;
663         else if (!strcmp(e, SRS_CLIENT_VOICE_TIMEOUT))
664             *notify_events |= SRS_VOICE_MASK_TIMEOUT;
665         else if (!strcmp(e, SRS_CLIENT_VOICE_ABORTED))
666             *notify_events |= SRS_VOICE_MASK_ABORTED;
667         else {
668             *errmsg = "invalid event";
669
670             return EINVAL;
671         }
672     }
673
674     return 0;
675 }
676
677
678 static int render_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
679                             void *user_data)
680 {
681     dbusif_t      *bus = (dbusif_t *)user_data;
682     srs_context_t *srs = bus->self->srs;
683     const char    *id, *msg, *voice, *errmsg;
684     int            timeout, events, err;
685     uint32_t       reqid;
686     srs_client_t  *c;
687
688     err = parse_render_voice(req, &id, &msg, &voice, &timeout, &events,
689                              &errmsg);
690
691     if (err != 0) {
692         reply_error(dbus, req, err, errmsg);
693
694         return TRUE;
695     }
696
697     c = client_lookup_by_id(srs, id);
698
699     if (c == NULL) {
700         reply_error(dbus, req, 1, "you don't exists, go away");
701
702         return TRUE;
703     }
704
705     reqid = client_render_voice(c, msg, voice, timeout, events);
706
707     if (reqid != SRS_VOICE_INVALID)
708         reply_render(dbus, req, reqid);
709     else
710         reply_error(dbus, req, 1, "voice render request failed");
711
712     return TRUE;
713 }
714
715
716 static int parse_cancel_voice(mrp_dbus_msg_t *req, const char **id,
717                               uint32_t *reqid, const char **errmsg)
718 {
719
720     *id = mrp_dbus_msg_sender(req);
721
722     if (*id == NULL)
723         return EINVAL;
724
725     if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_UINT32, &reqid)) {
726         *errmsg = "malformed voice render message";
727
728         return EINVAL;
729     }
730
731     return 0;
732 }
733
734
735 static int cancel_voice_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
736                             void *user_data)
737 {
738     dbusif_t      *bus = (dbusif_t *)user_data;
739     srs_context_t *srs = bus->self->srs;
740     const char    *id, *errmsg;
741     uint32_t       reqid;
742     int            err;
743     srs_client_t  *c;
744
745     err = parse_cancel_voice(req, &id, &reqid, &errmsg);
746
747     if (err != 0) {
748         reply_cancel(dbus, req, err, errmsg);
749
750         return TRUE;
751     }
752
753     c = client_lookup_by_id(srs, id);
754
755     if (c == NULL) {
756         reply_cancel(dbus, req, 1, "you don't exists, go away");
757
758         return TRUE;
759     }
760
761     client_cancel_voice(c, reqid);
762     reply_cancel(dbus, req, 0, NULL);
763
764     return TRUE;
765 }
766
767
768 static int parse_voice_query(mrp_dbus_msg_t *req, const char **id,
769                              const char **lang)
770 {
771     *id = mrp_dbus_msg_sender(req);
772
773     if (*id == NULL)
774         return EINVAL;
775
776     if (!mrp_dbus_msg_read_basic(req, MRP_DBUS_TYPE_STRING, lang))
777         lang = NULL;
778
779     return 0;
780 }
781
782
783 static int query_voices_req(mrp_dbus_t *dbus, mrp_dbus_msg_t *req,
784                             void *user_data)
785 {
786     dbusif_t          *bus = (dbusif_t *)user_data;
787     srs_context_t     *srs = bus->self->srs;
788     const char        *lang;
789     const char        *id;
790     int                err;
791     srs_client_t      *c;
792     srs_voice_actor_t *actors;
793     int                nactor;
794
795     err = parse_voice_query(req, &id, &lang);
796
797     if (err != 0) {
798         reply_cancel(dbus, req, err, "internal error");
799
800         return TRUE;
801     }
802
803     c = client_lookup_by_id(srs, id);
804
805     if (c == NULL) {
806         reply_error(dbus, req, 1, "you don't exists, go away");
807
808         return TRUE;
809     }
810
811     nactor = client_query_voices(c, lang, &actors);
812
813     if (nactor < 0)
814         reply_error(dbus, req, 1, "voice actor query failed");
815     else
816         reply_voice_query(dbus, req, nactor, actors);
817
818     client_free_queried_voices(actors);
819
820     return TRUE;
821 }
822
823
824 static int create_dbusif(srs_plugin_t *plugin)
825 {
826     dbusif_t *bus;
827
828     mrp_debug("creating D-Bus client interface plugin");
829
830     bus = mrp_allocz(sizeof(*bus));
831
832     if (bus != NULL) {
833         bus->self = plugin;
834         plugin->plugin_data = bus;
835         return TRUE;
836     }
837     else
838         return FALSE;
839 }
840
841
842 static int config_dbusif(srs_plugin_t *plugin, srs_cfg_t *settings)
843 {
844     dbusif_t *bus = (dbusif_t *)plugin->plugin_data;
845
846     MRP_UNUSED(settings);
847
848     mrp_debug("configure D-Bus client interface plugin");
849
850     bus->address = srs_get_string_config(settings, BUS_CONFIG, BUS_DEFAULT);
851
852     mrp_log_info("Client interface D-Bus address: '%s'", bus->address);
853
854     return dbusif_setup(bus);
855 }
856
857
858 static int start_dbusif(srs_plugin_t *plugin)
859 {
860     dbusif_t *bus = (dbusif_t *)plugin->plugin_data;
861
862     MRP_UNUSED(bus);
863
864     mrp_debug("start D-Bus client interface plugin");
865
866     return TRUE;
867 }
868
869
870 static void stop_dbusif(srs_plugin_t *plugin)
871 {
872     dbusif_t *bus = (dbusif_t *)plugin->plugin_data;
873
874     mrp_debug("stop D-Bus client interface plugin");
875
876     return;
877 }
878
879
880 static void destroy_dbusif(srs_plugin_t *plugin)
881 {
882     srs_context_t *srs = plugin->srs;
883     dbusif_t      *dbus = (dbusif_t *)plugin->plugin_data;
884
885     MRP_UNUSED(srs);
886
887     mrp_debug("destroy D-Bus client interface plugin");
888
889     dbusif_cleanup(dbus);
890     mrp_free(dbus);
891 }
892
893
894 SRS_DECLARE_PLUGIN(PLUGIN_NAME, PLUGIN_DESCR, PLUGIN_AUTHORS, PLUGIN_VERSION,
895                    create_dbusif, config_dbusif, start_dbusif, stop_dbusif,
896                    destroy_dbusif)