config: api cleanup
[profile/ivi/speech-recognition.git] / src / plugins / search-client / search-plugin.c
1 /*
2  * Copyright (c) 2012, 2013, 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 <errno.h>
31
32 #include <murphy/common/debug.h>
33
34 #include "srs/daemon/plugin.h"
35 #include "srs/daemon/client.h"
36
37 #define SEARCH_NAME    "search-client"
38 #define SEARCH_DESCR   "A trivial search plugin for SRS."
39 #define SEARCH_AUTHORS "Krisztian Litkey <kli@iki.fi>"
40 #define SEARCH_VERSION "0.0.1"
41
42 #define DICTIONARY     "general"
43 #define COMMAND        "google-chrome \"http://google.com/search?q=__url__\""
44
45 typedef struct {
46     srs_plugin_t *self;                  /* us */
47     srs_client_t *c;                     /* our client instance */
48     const char   *dict;                  /* dictionary to use */
49     const char   *cmd;                   /* search command template */
50 } search_t;
51
52
53
54 static int focus_cb(srs_client_t *c, srs_voice_focus_t focus)
55 {
56     srs_context_t *srs = c->srs;
57     search_t      *sch = c->user_data;
58     const char    *state;
59
60     MRP_UNUSED(srs);
61     MRP_UNUSED(sch);
62
63     switch (focus) {
64     case SRS_VOICE_FOCUS_NONE:      state = "none";      break;
65     case SRS_VOICE_FOCUS_SHARED:    state = "shared";    break;
66     case SRS_VOICE_FOCUS_EXCLUSIVE: state = "exclusive"; break;
67     default:                        state = "unknown";
68     }
69
70     mrp_debug("search plugin focus is now: %s", state);
71
72     return TRUE;
73 }
74
75
76 static int url_encode(char *buf, size_t size, int ntoken, char **tokens)
77 {
78     unsigned char *p, *s, L, H;
79     int            l, i;
80
81     p = (unsigned char *)buf;
82     l = size;
83
84     for (i = 0; i < ntoken; i++) {
85         if (l <= 0) {
86         overflow:
87             errno = EOVERFLOW;
88             return -1;
89         }
90
91         if (i != 0) {
92             *p++ = '+';
93             l--;
94         }
95
96         s = (unsigned char *)tokens[i];
97
98         while (*s && l > 0) {
99             switch (*s) {
100             case '0'...'9':
101             case 'A'...'Z':
102             case 'a'...'z':
103             case '-':
104             case '_':
105             case '.':
106             case '~':
107                 *p++ = *s++;
108                 l--;
109                 break;
110             default:
111                 if (l <= 3)
112                     goto overflow;
113
114                 H = (*s & 0xf0) >> 4;
115                 L =  *s & 0x0f;
116
117                 if (/*0 <= H && */H <= 9) H += '0';
118                 else                      H += 'A' - 10;
119                 if (/*0 <= L && */L <= 9) L += '0';
120                 else                      L += 'A' - 10;
121
122                 p[0] = '%';
123                 p[1] = H;
124                 p[2] = L;
125
126                 p += 3;
127                 l -= 3;
128                 s++;
129             }
130         }
131     }
132
133     if (l <= 0)
134         goto overflow;
135
136     *p = '\0';
137
138     return size - l;
139 }
140
141
142
143 static int command_cb(srs_client_t *c, int idx, int ntoken, char **tokens,
144                       uint32_t *start, uint32_t *end, srs_audiobuf_t *audio)
145 {
146     search_t *sch = (search_t *)c->user_data;
147     char      qry[1024], cmd[8192];
148     int       l;
149
150     MRP_UNUSED(sch);
151
152     MRP_UNUSED(idx);
153     MRP_UNUSED(start);
154     MRP_UNUSED(end);
155     MRP_UNUSED(audio);
156
157     mrp_debug("got search plugin command...");
158
159     tokens += 2;
160     ntoken -= 2;
161
162     if (url_encode(qry, sizeof(qry), ntoken, tokens) > 0) {
163         mrp_log_info("search-client got query: '%s'", qry);
164
165         l = snprintf(cmd, sizeof(cmd), sch->cmd, qry);
166
167         if (l < (int)sizeof(cmd)) {
168             mrp_log_info("search-client executing '%s'", cmd);
169             system(cmd);
170         }
171     }
172     else
173         mrp_log_error("search-client: URL encoding failed");
174
175     return TRUE;
176 }
177
178 static int create_search(srs_plugin_t *plugin)
179 {
180     search_t *sch;
181
182     mrp_debug("creating search plugin");
183
184     sch = mrp_allocz(sizeof(*sch));
185
186     if (sch != NULL) {
187         plugin->plugin_data = sch;
188         return TRUE;
189     }
190     else
191         return FALSE;
192 }
193
194
195 static int config_search(srs_plugin_t *plugin, srs_cfg_t *settings)
196 {
197     search_t    *sch = (search_t *)plugin->plugin_data;
198     static char  cmdbuf[1024];
199     const char  *dict, *cmd;
200     char        *p, *q;
201     int          l, n, nobg;
202
203     mrp_debug("configure search plugin");
204
205     dict = srs_config_get_string(settings, "search.dictionary", DICTIONARY);
206     cmd  = srs_config_get_string(settings, "search.command"   , COMMAND);
207
208     q = (char *)cmd + strlen(cmd);
209     while (q > cmd && (*q == ' ' || *q == '\t'))
210         q--;
211     if (*q != '&')
212         nobg = TRUE;
213     else
214         nobg = FALSE;
215
216     if ((q = strstr(cmd, "__url__")) == NULL) {
217         mrp_log_error("Invalid search command '%s', has no __url__ tag.", cmd);
218         return FALSE;
219     }
220
221     p = cmdbuf;
222     l = sizeof(cmdbuf);
223     n = q - cmd;
224
225     n  = snprintf(p, l, "%*.*s", n, n, cmd);
226     p += n;
227     l -= n;
228
229     if (l <= 0) {
230     overflow:
231         mrp_log_error("Invalid search command '%s', too long.", cmd);
232         return FALSE;
233     }
234
235     n  = snprintf(p, l, "%%s");
236     p += n;
237     l -= n;
238
239     if (l <= 0)
240         goto overflow;
241
242     q += strlen("__url__");
243     n  = snprintf(p, l, "%s%s", q, nobg ? "&" : "");
244     p += n;
245     l -= n;
246
247     if (l <= 0)
248         goto overflow;
249
250     sch->cmd  = cmdbuf;
251     sch->dict = dict;
252
253     mrp_log_info("search plugin dictionary: '%s'", sch->dict);
254     mrp_log_info("search plugin command: '%s'", sch->cmd);
255
256     return TRUE;
257 }
258
259
260 static int start_search(srs_plugin_t *plugin)
261 {
262     static srs_client_ops_t ops = {
263         .notify_focus   = focus_cb,
264         .notify_command = command_cb
265     };
266
267     char           cmd1[1024], cmd2[1024];
268     search_t      *sch    = plugin->plugin_data;
269     char          *cmds[2]= { &cmd1[0], &cmd2[0] };
270     int            ncmd   = (int)MRP_ARRAY_SIZE(cmds);
271     srs_context_t *srs    = plugin->srs;
272
273     srs_client_t  *c;
274     const char    *name, *appcls, *id;
275
276     mrp_debug("starting search plugin");
277
278     name   = "search";
279     appcls = "player";
280     id     = "search";
281
282     snprintf(cmd1, sizeof(cmd1), "search for __push_dict__(%s) *", sch->dict);
283     snprintf(cmd2, sizeof(cmd2), "google for __push_dict__(%s) *", sch->dict);
284
285     c = client_create(srs, SRS_CLIENT_TYPE_BUILTIN,
286                       name, appcls, cmds, ncmd, id, &ops, sch);
287
288     if (c == NULL)
289         return FALSE;
290
291     sch->self = plugin;
292     sch->c    = c;
293
294     client_request_focus(c, SRS_VOICE_FOCUS_SHARED);
295
296     return TRUE;
297 }
298
299
300 static void stop_search(srs_plugin_t *plugin)
301 {
302     search_t *sch = (search_t *)plugin->plugin_data;
303
304     mrp_debug("stop search plugin");
305
306     client_destroy(sch->c);
307     sch->c = NULL;
308
309     return;
310 }
311
312
313 static void destroy_search(srs_plugin_t *plugin)
314 {
315     srs_context_t *srs = plugin->srs;
316     search_t      *sch = (search_t *)plugin->plugin_data;
317
318     MRP_UNUSED(srs);
319
320     mrp_debug("destroy search plugin");
321
322     mrp_free(sch);
323 }
324
325
326 SRS_DECLARE_PLUGIN(SEARCH_NAME, SEARCH_DESCR, SEARCH_AUTHORS, SEARCH_VERSION,
327                    create_search, config_search, start_search, stop_search,
328                    destroy_search)