c25917bb6829d071a75147a3fc8323ec3afd9864
[asterisk/asterisk.git] / res / stasis_http / resource_channels.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2012 - 2013, Digium, Inc.
5  *
6  * David M. Lee, II <dlee@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Implementation for stasis-http stubs.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 /*** MODULEINFO
27         <depend type="module">res_stasis_app_playback</depend>
28         <support_level>core</support_level>
29  ***/
30
31 #include "asterisk.h"
32
33 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
34
35 #include "asterisk/file.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/dial.h"
38 #include "asterisk/bridging.h"
39 #include "asterisk/callerid.h"
40 #include "asterisk/stasis_app.h"
41 #include "asterisk/stasis_app_playback.h"
42 #include "asterisk/stasis_app_recording.h"
43 #include "asterisk/stasis_channels.h"
44 #include "resource_channels.h"
45
46 #include <limits.h>
47
48 /*!
49  * \brief Finds the control object for a channel, filling the response with an
50  * error, if appropriate.
51  * \param[out] response Response to fill with an error if control is not found.
52  * \param channel_id ID of the channel to lookup.
53  * \return Channel control object.
54  * \return \c NULL if control object does not exist.
55  */
56 static struct stasis_app_control *find_control(
57         struct stasis_http_response *response,
58         const char *channel_id)
59 {
60         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
61
62         ast_assert(response != NULL);
63
64         control = stasis_app_control_find_by_channel_id(channel_id);
65         if (control == NULL) {
66                 /* Distinguish between 404 and 409 errors */
67                 RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
68                 chan = ast_channel_get_by_name(channel_id);
69                 if (chan == NULL) {
70                         stasis_http_response_error(response, 404, "Not Found",
71                                    "Channel not found");
72                         return NULL;
73                 }
74
75                 stasis_http_response_error(response, 409, "Conflict",
76                            "Channel not in Stasis application");
77                 return NULL;
78         }
79
80         ao2_ref(control, +1);
81         return control;
82 }
83
84 void stasis_http_dial(struct ast_variable *headers, struct ast_dial_args *args, struct stasis_http_response *response)
85 {
86         struct stasis_app_control *control;
87
88         control = find_control(response, args->channel_id);
89         if (control == NULL) {
90                 return;
91         }
92
93         if (stasis_app_control_dial(control, args->endpoint, args->timeout)) {
94                 stasis_http_response_alloc_failed(response);
95                 return;
96         }
97
98         stasis_http_response_no_content(response);
99 }
100
101 void stasis_http_continue_in_dialplan(
102         struct ast_variable *headers,
103         struct ast_continue_in_dialplan_args *args,
104         struct stasis_http_response *response)
105 {
106         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
107
108         ast_assert(response != NULL);
109
110         control = find_control(response, args->channel_id);
111         if (control == NULL) {
112                 return;
113         }
114
115         if (stasis_app_control_continue(control, args->context, args->extension, args->priority)) {
116                 stasis_http_response_alloc_failed(response);
117                 return;
118         }
119
120         stasis_http_response_no_content(response);
121 }
122
123 void stasis_http_answer_channel(struct ast_variable *headers,
124                                 struct ast_answer_channel_args *args,
125                                 struct stasis_http_response *response)
126 {
127         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
128
129         control = find_control(response, args->channel_id);
130         if (control == NULL) {
131                 return;
132         }
133
134         if (stasis_app_control_answer(control) != 0) {
135                 stasis_http_response_error(
136                         response, 500, "Internal Server Error",
137                         "Failed to answer channel");
138                 return;
139         }
140
141         stasis_http_response_no_content(response);
142 }
143
144 void stasis_http_mute_channel(struct ast_variable *headers, struct ast_mute_channel_args *args, struct stasis_http_response *response)
145 {
146         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
147         unsigned int direction = 0;
148         enum ast_frame_type frametype = AST_FRAME_VOICE;
149
150         control = find_control(response, args->channel_id);
151         if (control == NULL) {
152                 return;
153         }
154
155         if (!strcmp(args->direction, "in")) {
156                 direction = AST_MUTE_DIRECTION_READ;
157         } else if (!strcmp(args->direction, "out")) {
158                 direction = AST_MUTE_DIRECTION_WRITE;
159         } else if (!strcmp(args->direction, "both")) {
160                 direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE;
161         } else {
162                 stasis_http_response_error(
163                         response, 400, "Bad Request",
164                         "Invalid direction specified");
165                 return;
166         }
167
168         stasis_app_control_mute(control, direction, frametype);
169
170         stasis_http_response_no_content(response);
171 }
172
173 void stasis_http_unmute_channel(struct ast_variable *headers, struct ast_unmute_channel_args *args, struct stasis_http_response *response)
174 {
175         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
176         unsigned int direction = 0;
177         enum ast_frame_type frametype = AST_FRAME_VOICE;
178
179         control = find_control(response, args->channel_id);
180         if (control == NULL) {
181                 return;
182         }
183
184         if (!strcmp(args->direction, "in")) {
185                 direction = AST_MUTE_DIRECTION_READ;
186         } else if (!strcmp(args->direction, "out")) {
187                 direction = AST_MUTE_DIRECTION_WRITE;
188         } else if (!strcmp(args->direction, "both")) {
189                 direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE;
190         } else {
191                 stasis_http_response_error(
192                         response, 400, "Bad Request",
193                         "Invalid direction specified");
194                 return;
195         }
196
197         stasis_app_control_unmute(control, direction, frametype);
198
199         stasis_http_response_no_content(response);
200 }
201
202 void stasis_http_hold_channel(struct ast_variable *headers, struct ast_hold_channel_args *args, struct stasis_http_response *response)
203 {
204         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
205
206         control = find_control(response, args->channel_id);
207         if (control == NULL) {
208                 /* Response filled in by find_control */
209                 return;
210         }
211
212         stasis_app_control_hold(control);
213
214         stasis_http_response_no_content(response);
215 }
216
217 void stasis_http_unhold_channel(struct ast_variable *headers, struct ast_unhold_channel_args *args, struct stasis_http_response *response)
218 {
219         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
220
221         control = find_control(response, args->channel_id);
222         if (control == NULL) {
223                 /* Response filled in by find_control */
224                 return;
225         }
226
227         stasis_app_control_unhold(control);
228
229         stasis_http_response_no_content(response);
230 }
231
232 void stasis_http_play_on_channel(struct ast_variable *headers,
233         struct ast_play_on_channel_args *args,
234         struct stasis_http_response *response)
235 {
236         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
237         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
238         RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
239         RAII_VAR(char *, playback_url, NULL, ast_free);
240         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
241         const char *language;
242
243         ast_assert(response != NULL);
244
245         control = find_control(response, args->channel_id);
246         if (control == NULL) {
247                 /* Response filled in by find_control */
248                 return;
249         }
250
251         snapshot = stasis_app_control_get_snapshot(control);
252         if (!snapshot) {
253                 stasis_http_response_error(
254                         response, 404, "Not Found",
255                         "Channel not found");
256                 return;
257         }
258
259         if (args->skipms < 0) {
260                 stasis_http_response_error(
261                         response, 400, "Bad Request",
262                         "skipms cannot be negative");
263                 return;
264         }
265
266         if (args->offsetms < 0) {
267                 stasis_http_response_error(
268                         response, 400, "Bad Request",
269                         "offsetms cannot be negative");
270                 return;
271         }
272
273         language = S_OR(args->lang, snapshot->language);
274
275         playback = stasis_app_control_play_uri(control, args->media, language,
276                 args->channel_id, STASIS_PLAYBACK_TARGET_CHANNEL, args->skipms, args->offsetms);
277         if (!playback) {
278                 stasis_http_response_error(
279                         response, 500, "Internal Server Error",
280                         "Failed to queue media for playback");
281                 return;
282         }
283
284         ast_asprintf(&playback_url, "/playback/%s",
285                 stasis_app_playback_get_id(playback));
286         if (!playback_url) {
287                 stasis_http_response_error(
288                         response, 500, "Internal Server Error",
289                         "Out of memory");
290                 return;
291         }
292
293         json = stasis_app_playback_to_json(playback);
294         if (!json) {
295                 stasis_http_response_error(
296                         response, 500, "Internal Server Error",
297                         "Out of memory");
298                 return;
299         }
300
301         stasis_http_response_created(response, playback_url, json);
302 }
303
304 void stasis_http_record_channel(struct ast_variable *headers,
305         struct ast_record_channel_args *args,
306         struct stasis_http_response *response)
307 {
308         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
309         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
310         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
311         RAII_VAR(char *, recording_url, NULL, ast_free);
312         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
313         RAII_VAR(struct stasis_app_recording_options *, options, NULL,
314                 ao2_cleanup);
315         RAII_VAR(char *, uri_encoded_name, NULL, ast_free);
316         size_t uri_name_maxlen;
317
318         ast_assert(response != NULL);
319
320         if (args->max_duration_seconds < 0) {
321                 stasis_http_response_error(
322                         response, 400, "Bad Request",
323                         "max_duration_seconds cannot be negative");
324                 return;
325         }
326
327         if (args->max_silence_seconds < 0) {
328                 stasis_http_response_error(
329                         response, 400, "Bad Request",
330                         "max_silence_seconds cannot be negative");
331                 return;
332         }
333
334         control = find_control(response, args->channel_id);
335         if (control == NULL) {
336                 /* Response filled in by find_control */
337                 return;
338         }
339
340         options = stasis_app_recording_options_create(args->name, args->format);
341         if (options == NULL) {
342                 stasis_http_response_error(
343                         response, 500, "Internal Server Error",
344                         "Out of memory");
345         }
346         options->max_silence_seconds = args->max_silence_seconds;
347         options->max_duration_seconds = args->max_duration_seconds;
348         options->terminate_on =
349                 stasis_app_recording_termination_parse(args->terminate_on);
350         options->if_exists =
351                 stasis_app_recording_if_exists_parse(args->if_exists);
352         options->beep = args->beep;
353
354         if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) {
355                 stasis_http_response_error(
356                         response, 400, "Bad Request",
357                         "terminateOn invalid");
358                 return;
359         }
360
361         if (options->if_exists == -1) {
362                 stasis_http_response_error(
363                         response, 400, "Bad Request",
364                         "ifExists invalid");
365                 return;
366         }
367
368         recording = stasis_app_control_record(control, options);
369         if (recording == NULL) {
370                 switch(errno) {
371                 case EINVAL:
372                         /* While the arguments are invalid, we should have
373                          * caught them prior to calling record.
374                          */
375                         stasis_http_response_error(
376                                 response, 500, "Internal Server Error",
377                                 "Error parsing request");
378                         break;
379                 case EEXIST:
380                         stasis_http_response_error(response, 409, "Conflict",
381                                 "Recording '%s' already in progress",
382                                 args->name);
383                         break;
384                 case ENOMEM:
385                         stasis_http_response_error(
386                                 response, 500, "Internal Server Error",
387                                 "Out of memory");
388                         break;
389                 case EPERM:
390                         stasis_http_response_error(
391                                 response, 400, "Bad Request",
392                                 "Recording name invalid");
393                         break;
394                 default:
395                         ast_log(LOG_WARNING,
396                                 "Unrecognized recording error: %s\n",
397                                 strerror(errno));
398                         stasis_http_response_error(
399                                 response, 500, "Internal Server Error",
400                                 "Internal Server Error");
401                         break;
402                 }
403                 return;
404         }
405
406         uri_name_maxlen = strlen(args->name) * 3;
407         uri_encoded_name = ast_malloc(uri_name_maxlen);
408         if (!uri_encoded_name) {
409                 stasis_http_response_error(
410                         response, 500, "Internal Server Error",
411                         "Out of memory");
412                 return;
413         }
414         ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen,
415                 ast_uri_http);
416
417         ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name);
418         if (!recording_url) {
419                 stasis_http_response_error(
420                         response, 500, "Internal Server Error",
421                         "Out of memory");
422                 return;
423         }
424
425         json = stasis_app_recording_to_json(recording);
426         if (!json) {
427                 stasis_http_response_error(
428                         response, 500, "Internal Server Error",
429                         "Out of memory");
430                 return;
431         }
432
433         stasis_http_response_created(response, recording_url, json);
434 }
435
436 void stasis_http_get_channel(struct ast_variable *headers,
437                              struct ast_get_channel_args *args,
438                              struct stasis_http_response *response)
439 {
440         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
441         struct stasis_caching_topic *caching_topic;
442         struct ast_channel_snapshot *snapshot;
443
444         caching_topic = ast_channel_topic_all_cached();
445         if (!caching_topic) {
446                 stasis_http_response_error(
447                         response, 500, "Internal Server Error",
448                         "Message bus not initialized");
449                 return;
450         }
451
452         msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
453                                args->channel_id);
454         if (!msg) {
455                 stasis_http_response_error(
456                         response, 404, "Not Found",
457                         "Channel not found");
458                 return;
459         }
460
461         snapshot = stasis_message_data(msg);
462         ast_assert(snapshot != NULL);
463
464         stasis_http_response_ok(response,
465                                 ast_channel_snapshot_to_json(snapshot));
466 }
467
468 void stasis_http_delete_channel(struct ast_variable *headers,
469                                 struct ast_delete_channel_args *args,
470                                 struct stasis_http_response *response)
471 {
472         RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
473
474         chan = ast_channel_get_by_name(args->channel_id);
475         if (chan == NULL) {
476                 stasis_http_response_error(
477                         response, 404, "Not Found",
478                         "Channel not found");
479                 return;
480         }
481
482         ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
483
484         stasis_http_response_no_content(response);
485 }
486
487 void stasis_http_get_channels(struct ast_variable *headers,
488                               struct ast_get_channels_args *args,
489                               struct stasis_http_response *response)
490 {
491         RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup);
492         RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
493         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
494         struct ao2_iterator i;
495         void *obj;
496
497         caching_topic = ast_channel_topic_all_cached();
498         if (!caching_topic) {
499                 stasis_http_response_error(
500                         response, 500, "Internal Server Error",
501                         "Message bus not initialized");
502                 return;
503         }
504         ao2_ref(caching_topic, +1);
505
506         snapshots = stasis_cache_dump(caching_topic, ast_channel_snapshot_type());
507         if (!snapshots) {
508                 stasis_http_response_alloc_failed(response);
509                 return;
510         }
511
512         json = ast_json_array_create();
513         if (!json) {
514                 stasis_http_response_alloc_failed(response);
515                 return;
516         }
517
518         i = ao2_iterator_init(snapshots, 0);
519         while ((obj = ao2_iterator_next(&i))) {
520                 RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
521                 struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
522                 int r = ast_json_array_append(
523                         json, ast_channel_snapshot_to_json(snapshot));
524                 if (r != 0) {
525                         stasis_http_response_alloc_failed(response);
526                         return;
527                 }
528         }
529         ao2_iterator_destroy(&i);
530
531         stasis_http_response_ok(response, ast_json_ref(json));
532 }
533
534 void stasis_http_originate(struct ast_variable *headers,
535                            struct ast_originate_args *args,
536                            struct stasis_http_response *response)
537 {
538         char *dialtech;
539         char dialdevice[AST_CHANNEL_NAME];
540         char *caller_id = NULL;
541         char *cid_num = NULL;
542         char *cid_name = NULL;
543         int timeout = 30000;
544
545         char *stuff;
546
547         if (ast_strlen_zero(args->endpoint)) {
548                 stasis_http_response_error(response, 400, "Bad Request",
549                         "Endpoint must be specified");
550                 return;
551         }
552
553         dialtech = ast_strdupa(args->endpoint);
554         if ((stuff = strchr(dialtech, '/'))) {
555                 *stuff++ = '\0';
556                 ast_copy_string(dialdevice, stuff, sizeof(dialdevice));
557         }
558
559         if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) {
560                 stasis_http_response_error(response, 400, "Bad Request",
561                         "Invalid endpoint specified");
562                 return;
563         }
564
565         if (args->timeout > 0) {
566                 timeout = args->timeout * 1000;
567         } else if (args->timeout == -1) {
568                 timeout = -1;
569         }
570
571         if (!ast_strlen_zero(args->caller_id)) {
572                 caller_id = ast_strdupa(args->caller_id);
573                 ast_callerid_parse(caller_id, &cid_name, &cid_num);
574
575                 if (ast_is_shrinkable_phonenumber(cid_num)) {
576                         ast_shrink_phone_number(cid_num);
577                 }
578         }
579
580         if (!ast_strlen_zero(args->app)) {
581                 const char *app = "Stasis";
582
583                 RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);
584
585                 if (!appdata) {
586                         stasis_http_response_alloc_failed(response);
587                         return;
588                 }
589
590                 ast_str_set(&appdata, 0, "%s", args->app);
591                 if (!ast_strlen_zero(args->app_args)) {
592                         ast_str_append(&appdata, 0, ",%s", args->app_args);
593                 }
594
595                 /* originate a channel, putting it into an application */
596                 if (ast_pbx_outgoing_app(dialtech, NULL, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, NULL, NULL, NULL)) {
597                         stasis_http_response_alloc_failed(response);
598                         return;
599                 }
600         } else if (!ast_strlen_zero(args->extension)) {
601                 /* originate a channel, sending it to an extension */
602                 if (ast_pbx_outgoing_exten(dialtech, NULL, dialdevice, timeout, S_OR(args->context, "default"), args->extension, args->priority ? args->priority : 1, NULL, 0, cid_num, cid_name, NULL, NULL, NULL, 0)) {
603                         stasis_http_response_alloc_failed(response);
604                         return;
605                 }
606         } else {
607                 stasis_http_response_error(response, 400, "Bad Request",
608                         "Application or extension must be specified");
609                 return;
610         }
611
612         stasis_http_response_no_content(response);
613 }
614
615 void stasis_http_get_channel_var(struct ast_variable *headers, struct ast_get_channel_var_args *args, struct stasis_http_response *response)
616 {
617         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
618         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
619         RAII_VAR(char *, value, NULL, ast_free);
620
621         ast_assert(response != NULL);
622
623         control = find_control(response, args->channel_id);
624         if (control == NULL) {
625                 return;
626         }
627
628         value = stasis_app_control_get_channel_var(control, args->variable);
629
630         if (!(json = ast_json_pack("{s: s}", "value", S_OR(value, "")))) {
631                 stasis_http_response_alloc_failed(response);
632                 return;
633         }
634
635         stasis_http_response_ok(response, ast_json_ref(json));
636 }
637
638 void stasis_http_set_channel_var(struct ast_variable *headers, struct ast_set_channel_var_args *args, struct stasis_http_response *response)
639 {
640         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
641
642         ast_assert(response != NULL);
643
644         control = find_control(response, args->channel_id);
645         if (control == NULL) {
646                 return;
647         }
648
649         if (ast_strlen_zero(args->variable)) {
650                 stasis_http_response_error(
651                         response, 400, "Bad Request",
652                         "Variable name is required");
653                 return;
654         }
655
656         if (stasis_app_control_set_channel_var(control, args->variable, args->value)) {
657                 stasis_http_response_error(
658                         response, 400, "Bad Request",
659                         "Failed to execute function");
660                 return;
661         }
662
663         stasis_http_response_no_content(response);
664 }
665