ARI: Fix crash if integer values used in JSON payload 'variables' object.
[asterisk/asterisk.git] / res / ari / 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 ARI 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/bridge.h"
38 #include "asterisk/callerid.h"
39 #include "asterisk/stasis_app.h"
40 #include "asterisk/stasis_app_playback.h"
41 #include "asterisk/stasis_app_recording.h"
42 #include "asterisk/stasis_app_snoop.h"
43 #include "asterisk/stasis_channels.h"
44 #include "asterisk/causes.h"
45 #include "asterisk/format_cache.h"
46 #include "asterisk/core_local.h"
47 #include "asterisk/dial.h"
48 #include "resource_channels.h"
49
50 #include <limits.h>
51
52 /*!
53  * \brief Finds the control object for a channel, filling the response with an
54  * error, if appropriate.
55  * \param[out] response Response to fill with an error if control is not found.
56  * \param channel_id ID of the channel to lookup.
57  * \return Channel control object.
58  * \return \c NULL if control object does not exist.
59  */
60 static struct stasis_app_control *find_control(
61         struct ast_ari_response *response,
62         const char *channel_id)
63 {
64         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
65
66         ast_assert(response != NULL);
67
68         control = stasis_app_control_find_by_channel_id(channel_id);
69         if (control == NULL) {
70                 /* Distinguish between 404 and 409 errors */
71                 RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
72                 chan = ast_channel_get_by_name(channel_id);
73                 if (chan == NULL) {
74                         ast_ari_response_error(response, 404, "Not Found",
75                                    "Channel not found");
76                         return NULL;
77                 }
78
79                 ast_ari_response_error(response, 409, "Conflict",
80                            "Channel not in Stasis application");
81                 return NULL;
82         }
83
84         ao2_ref(control, +1);
85         return control;
86 }
87
88 void ast_ari_channels_continue_in_dialplan(
89         struct ast_variable *headers,
90         struct ast_ari_channels_continue_in_dialplan_args *args,
91         struct ast_ari_response *response)
92 {
93         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
94         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
95         int ipri;
96         const char *context;
97         const char *exten;
98
99         ast_assert(response != NULL);
100
101         control = find_control(response, args->channel_id);
102         if (control == NULL) {
103                 return;
104         }
105
106         snapshot = stasis_app_control_get_snapshot(control);
107         if (!snapshot) {
108                 return;
109         }
110
111         if (ast_strlen_zero(args->context)) {
112                 context = snapshot->context;
113                 exten = S_OR(args->extension, snapshot->exten);
114         } else {
115                 context = args->context;
116                 exten = S_OR(args->extension, "s");
117         }
118
119         if (!ast_strlen_zero(args->label)) {
120                 /* A label was provided in the request, use that */
121
122                 if (sscanf(args->label, "%30d", &ipri) != 1) {
123                         ipri = ast_findlabel_extension(NULL, context, exten, args->label, NULL);
124                         if (ipri == -1) {
125                                 ast_log(AST_LOG_ERROR, "Requested label: %s can not be found in context: %s\n", args->label, context);
126                                 ast_ari_response_error(response, 404, "Not Found", "Requested label can not be found");
127                                 return;
128                         }
129                 } else {
130                         ast_debug(3, "Numeric value provided for label, jumping to that priority\n");
131                 }
132
133                 if (ipri == 0) {
134                         ast_log(AST_LOG_ERROR, "Invalid priority label '%s' specified for extension %s in context: %s\n",
135                                         args->label, exten, context);
136                         ast_ari_response_error(response, 400, "Bad Request", "Requested priority is illegal");
137                         return;
138                 }
139
140         } else if (args->priority) {
141                 /* No label provided, use provided priority */
142                 ipri = args->priority;
143         } else if (ast_strlen_zero(args->context) && ast_strlen_zero(args->extension)) {
144                 /* Special case. No exten, context, or priority provided, then move on to the next priority */
145                 ipri = snapshot->priority + 1;
146         } else {
147                 ipri = 1;
148         }
149
150
151         if (stasis_app_control_continue(control, context, exten, ipri)) {
152                 ast_ari_response_alloc_failed(response);
153                 return;
154         }
155
156         ast_ari_response_no_content(response);
157 }
158
159 void ast_ari_channels_redirect(struct ast_variable *headers,
160         struct ast_ari_channels_redirect_args *args,
161         struct ast_ari_response *response)
162 {
163         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
164         RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup);
165         char *tech;
166         char *resource;
167         int tech_len;
168
169         control = find_control(response, args->channel_id);
170         if (!control) {
171                 return;
172         }
173
174         if (ast_strlen_zero(args->endpoint)) {
175                 ast_ari_response_error(response, 400, "Not Found",
176                         "Required parameter 'endpoint' not provided.");
177                 return;
178         }
179
180         tech = ast_strdupa(args->endpoint);
181         if (!(resource = strchr(tech, '/')) || !(tech_len = resource - tech)) {
182                 ast_ari_response_error(response, 422, "Unprocessable Entity",
183                         "Endpoint parameter '%s' does not contain tech/resource", args->endpoint);
184                 return;
185         }
186
187         *resource++ = '\0';
188         if (ast_strlen_zero(resource)) {
189                 ast_ari_response_error(response, 422, "Unprocessable Entity",
190                         "No resource provided in endpoint parameter '%s'", args->endpoint);
191                 return;
192         }
193
194         chan_snapshot = ast_channel_snapshot_get_latest(args->channel_id);
195         if (!chan_snapshot) {
196                 ast_ari_response_error(response, 500, "Internal Server Error",
197                         "Unable to find channel snapshot for '%s'", args->channel_id);
198                 return;
199         }
200
201         if (strncasecmp(chan_snapshot->type, tech, tech_len)) {
202                 ast_ari_response_error(response, 422, "Unprocessable Entity",
203                         "Endpoint technology '%s' does not match channel technology '%s'",
204                         tech, chan_snapshot->type);
205                 return;
206         }
207
208         if (stasis_app_control_redirect(control, resource)) {
209                 ast_ari_response_error(response, 500, "Internal Server Error",
210                         "Failed to redirect channel");
211                 return;
212         }
213
214         ast_ari_response_no_content(response);
215 }
216
217 void ast_ari_channels_answer(struct ast_variable *headers,
218         struct ast_ari_channels_answer_args *args,
219         struct ast_ari_response *response)
220 {
221         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
222
223         control = find_control(response, args->channel_id);
224         if (control == NULL) {
225                 return;
226         }
227
228         if (stasis_app_control_answer(control) != 0) {
229                 ast_ari_response_error(
230                         response, 500, "Internal Server Error",
231                         "Failed to answer channel");
232                 return;
233         }
234
235         ast_ari_response_no_content(response);
236 }
237
238 void ast_ari_channels_ring(struct ast_variable *headers,
239         struct ast_ari_channels_ring_args *args,
240         struct ast_ari_response *response)
241 {
242         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
243
244         control = find_control(response, args->channel_id);
245         if (control == NULL) {
246                 return;
247         }
248
249         stasis_app_control_ring(control);
250
251         ast_ari_response_no_content(response);
252 }
253
254 void ast_ari_channels_ring_stop(struct ast_variable *headers,
255         struct ast_ari_channels_ring_stop_args *args,
256         struct ast_ari_response *response)
257 {
258         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
259
260         control = find_control(response, args->channel_id);
261         if (control == NULL) {
262                 return;
263         }
264
265         stasis_app_control_ring_stop(control);
266
267         ast_ari_response_no_content(response);
268 }
269
270 void ast_ari_channels_mute(struct ast_variable *headers,
271         struct ast_ari_channels_mute_args *args,
272         struct ast_ari_response *response)
273 {
274         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
275         unsigned int direction = 0;
276         enum ast_frame_type frametype = AST_FRAME_VOICE;
277
278         control = find_control(response, args->channel_id);
279         if (control == NULL) {
280                 return;
281         }
282
283         if (ast_strlen_zero(args->direction)) {
284                 ast_ari_response_error(
285                         response, 400, "Bad Request",
286                         "Direction is required");
287                 return;
288         }
289
290         if (!strcmp(args->direction, "in")) {
291                 direction = AST_MUTE_DIRECTION_READ;
292         } else if (!strcmp(args->direction, "out")) {
293                 direction = AST_MUTE_DIRECTION_WRITE;
294         } else if (!strcmp(args->direction, "both")) {
295                 direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE;
296         } else {
297                 ast_ari_response_error(
298                         response, 400, "Bad Request",
299                         "Invalid direction specified");
300                 return;
301         }
302
303         stasis_app_control_mute(control, direction, frametype);
304
305         ast_ari_response_no_content(response);
306 }
307
308 void ast_ari_channels_unmute(struct ast_variable *headers,
309         struct ast_ari_channels_unmute_args *args,
310         struct ast_ari_response *response)
311 {
312         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
313         unsigned int direction = 0;
314         enum ast_frame_type frametype = AST_FRAME_VOICE;
315
316         control = find_control(response, args->channel_id);
317         if (control == NULL) {
318                 return;
319         }
320
321         if (ast_strlen_zero(args->direction)) {
322                 ast_ari_response_error(
323                         response, 400, "Bad Request",
324                         "Direction is required");
325                 return;
326         }
327
328         if (!strcmp(args->direction, "in")) {
329                 direction = AST_MUTE_DIRECTION_READ;
330         } else if (!strcmp(args->direction, "out")) {
331                 direction = AST_MUTE_DIRECTION_WRITE;
332         } else if (!strcmp(args->direction, "both")) {
333                 direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE;
334         } else {
335                 ast_ari_response_error(
336                         response, 400, "Bad Request",
337                         "Invalid direction specified");
338                 return;
339         }
340
341         stasis_app_control_unmute(control, direction, frametype);
342
343         ast_ari_response_no_content(response);
344 }
345
346 void ast_ari_channels_send_dtmf(struct ast_variable *headers,
347         struct ast_ari_channels_send_dtmf_args *args,
348         struct ast_ari_response *response)
349 {
350         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
351
352         control = find_control(response, args->channel_id);
353         if (control == NULL) {
354                 return;
355         }
356
357         if (ast_strlen_zero(args->dtmf)) {
358                 ast_ari_response_error(
359                         response, 400, "Bad Request",
360                         "DTMF is required");
361                 return;
362         }
363
364         stasis_app_control_dtmf(control, args->dtmf, args->before, args->between, args->duration, args->after);
365
366         ast_ari_response_no_content(response);
367 }
368
369 void ast_ari_channels_hold(struct ast_variable *headers,
370         struct ast_ari_channels_hold_args *args,
371         struct ast_ari_response *response)
372 {
373         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
374
375         control = find_control(response, args->channel_id);
376         if (control == NULL) {
377                 /* Response filled in by find_control */
378                 return;
379         }
380
381         stasis_app_control_hold(control);
382
383         ast_ari_response_no_content(response);
384 }
385
386 void ast_ari_channels_unhold(struct ast_variable *headers,
387         struct ast_ari_channels_unhold_args *args,
388         struct ast_ari_response *response)
389 {
390         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
391
392         control = find_control(response, args->channel_id);
393         if (control == NULL) {
394                 /* Response filled in by find_control */
395                 return;
396         }
397
398         stasis_app_control_unhold(control);
399
400         ast_ari_response_no_content(response);
401 }
402
403 void ast_ari_channels_start_moh(struct ast_variable *headers,
404         struct ast_ari_channels_start_moh_args *args,
405         struct ast_ari_response *response)
406 {
407         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
408
409         control = find_control(response, args->channel_id);
410         if (control == NULL) {
411                 /* Response filled in by find_control */
412                 return;
413         }
414
415         stasis_app_control_moh_start(control, args->moh_class);
416         ast_ari_response_no_content(response);
417 }
418
419 void ast_ari_channels_stop_moh(struct ast_variable *headers,
420         struct ast_ari_channels_stop_moh_args *args,
421         struct ast_ari_response *response)
422 {
423         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
424
425         control = find_control(response, args->channel_id);
426         if (control == NULL) {
427                 /* Response filled in by find_control */
428                 return;
429         }
430
431         stasis_app_control_moh_stop(control);
432         ast_ari_response_no_content(response);
433 }
434
435 void ast_ari_channels_start_silence(struct ast_variable *headers,
436         struct ast_ari_channels_start_silence_args *args,
437         struct ast_ari_response *response)
438 {
439         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
440
441         control = find_control(response, args->channel_id);
442         if (control == NULL) {
443                 /* Response filled in by find_control */
444                 return;
445         }
446
447         stasis_app_control_silence_start(control);
448         ast_ari_response_no_content(response);
449 }
450
451 void ast_ari_channels_stop_silence(struct ast_variable *headers,
452         struct ast_ari_channels_stop_silence_args *args,
453         struct ast_ari_response *response)
454 {
455         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
456
457         control = find_control(response, args->channel_id);
458         if (control == NULL) {
459                 /* Response filled in by find_control */
460                 return;
461         }
462
463         stasis_app_control_silence_stop(control);
464         ast_ari_response_no_content(response);
465 }
466
467 static void ari_channels_handle_play(
468         const char *args_channel_id,
469         const char *args_media,
470         const char *args_lang,
471         int args_offsetms,
472         int args_skipms,
473         const char *args_playback_id,
474         struct ast_ari_response *response)
475 {
476         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
477         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
478         RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
479         RAII_VAR(char *, playback_url, NULL, ast_free);
480         struct ast_json *json;
481         const char *language;
482
483         ast_assert(response != NULL);
484
485         control = find_control(response, args_channel_id);
486         if (control == NULL) {
487                 /* Response filled in by find_control */
488                 return;
489         }
490
491         snapshot = stasis_app_control_get_snapshot(control);
492         if (!snapshot) {
493                 ast_ari_response_error(
494                         response, 404, "Not Found",
495                         "Channel not found");
496                 return;
497         }
498
499         if (args_skipms < 0) {
500                 ast_ari_response_error(
501                         response, 400, "Bad Request",
502                         "skipms cannot be negative");
503                 return;
504         }
505
506         if (args_offsetms < 0) {
507                 ast_ari_response_error(
508                         response, 400, "Bad Request",
509                         "offsetms cannot be negative");
510                 return;
511         }
512
513         language = S_OR(args_lang, snapshot->language);
514
515         playback = stasis_app_control_play_uri(control, args_media, language,
516                 args_channel_id, STASIS_PLAYBACK_TARGET_CHANNEL, args_skipms, args_offsetms, args_playback_id);
517         if (!playback) {
518                 ast_ari_response_error(
519                         response, 500, "Internal Server Error",
520                         "Failed to queue media for playback");
521                 return;
522         }
523
524         if (ast_asprintf(&playback_url, "/playback/%s",
525                         stasis_app_playback_get_id(playback)) == -1) {
526                 playback_url = NULL;
527                 ast_ari_response_error(
528                         response, 500, "Internal Server Error",
529                         "Out of memory");
530                 return;
531         }
532
533         json = stasis_app_playback_to_json(playback);
534         if (!json) {
535                 ast_ari_response_error(
536                         response, 500, "Internal Server Error",
537                         "Out of memory");
538                 return;
539         }
540
541         ast_ari_response_created(response, playback_url, json);
542 }
543
544 void ast_ari_channels_play(struct ast_variable *headers,
545         struct ast_ari_channels_play_args *args,
546         struct ast_ari_response *response)
547 {
548         ari_channels_handle_play(
549                 args->channel_id,
550                 args->media,
551                 args->lang,
552                 args->offsetms,
553                 args->skipms,
554                 args->playback_id,
555                 response);
556 }
557
558 void ast_ari_channels_play_with_id(struct ast_variable *headers,
559         struct ast_ari_channels_play_with_id_args *args,
560         struct ast_ari_response *response)
561 {
562         ari_channels_handle_play(
563                 args->channel_id,
564                 args->media,
565                 args->lang,
566                 args->offsetms,
567                 args->skipms,
568                 args->playback_id,
569                 response);
570 }
571
572 void ast_ari_channels_record(struct ast_variable *headers,
573         struct ast_ari_channels_record_args *args,
574         struct ast_ari_response *response)
575 {
576         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
577         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
578         RAII_VAR(char *, recording_url, NULL, ast_free);
579         struct ast_json *json;
580         RAII_VAR(struct stasis_app_recording_options *, options, NULL,
581                 ao2_cleanup);
582         RAII_VAR(char *, uri_encoded_name, NULL, ast_free);
583         size_t uri_name_maxlen;
584
585         ast_assert(response != NULL);
586
587         if (args->max_duration_seconds < 0) {
588                 ast_ari_response_error(
589                         response, 400, "Bad Request",
590                         "max_duration_seconds cannot be negative");
591                 return;
592         }
593
594         if (args->max_silence_seconds < 0) {
595                 ast_ari_response_error(
596                         response, 400, "Bad Request",
597                         "max_silence_seconds cannot be negative");
598                 return;
599         }
600
601         control = find_control(response, args->channel_id);
602         if (control == NULL) {
603                 /* Response filled in by find_control */
604                 return;
605         }
606
607         options = stasis_app_recording_options_create(args->name, args->format);
608         if (options == NULL) {
609                 ast_ari_response_error(
610                         response, 500, "Internal Server Error",
611                         "Out of memory");
612         }
613         ast_string_field_build(options, target, "channel:%s", args->channel_id);
614         options->max_silence_seconds = args->max_silence_seconds;
615         options->max_duration_seconds = args->max_duration_seconds;
616         options->terminate_on =
617                 stasis_app_recording_termination_parse(args->terminate_on);
618         options->if_exists =
619                 stasis_app_recording_if_exists_parse(args->if_exists);
620         options->beep = args->beep;
621
622         if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) {
623                 ast_ari_response_error(
624                         response, 400, "Bad Request",
625                         "terminateOn invalid");
626                 return;
627         }
628
629         if (options->if_exists == -1) {
630                 ast_ari_response_error(
631                         response, 400, "Bad Request",
632                         "ifExists invalid");
633                 return;
634         }
635
636         if (!ast_get_format_for_file_ext(options->format)) {
637                 ast_ari_response_error(
638                         response, 422, "Unprocessable Entity",
639                         "specified format is unknown on this system");
640                 return;
641         }
642
643         recording = stasis_app_control_record(control, options);
644         if (recording == NULL) {
645                 switch(errno) {
646                 case EINVAL:
647                         /* While the arguments are invalid, we should have
648                          * caught them prior to calling record.
649                          */
650                         ast_ari_response_error(
651                                 response, 500, "Internal Server Error",
652                                 "Error parsing request");
653                         break;
654                 case EEXIST:
655                         ast_ari_response_error(response, 409, "Conflict",
656                                 "Recording '%s' already exists and can not be overwritten",
657                                 args->name);
658                         break;
659                 case ENOMEM:
660                         ast_ari_response_error(
661                                 response, 500, "Internal Server Error",
662                                 "Out of memory");
663                         break;
664                 case EPERM:
665                         ast_ari_response_error(
666                                 response, 400, "Bad Request",
667                                 "Recording name invalid");
668                         break;
669                 default:
670                         ast_log(LOG_WARNING,
671                                 "Unrecognized recording error: %s\n",
672                                 strerror(errno));
673                         ast_ari_response_error(
674                                 response, 500, "Internal Server Error",
675                                 "Internal Server Error");
676                         break;
677                 }
678                 return;
679         }
680
681         uri_name_maxlen = strlen(args->name) * 3;
682         uri_encoded_name = ast_malloc(uri_name_maxlen);
683         if (!uri_encoded_name) {
684                 ast_ari_response_error(
685                         response, 500, "Internal Server Error",
686                         "Out of memory");
687                 return;
688         }
689         ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen,
690                 ast_uri_http);
691
692         if (ast_asprintf(&recording_url, "/recordings/live/%s",
693                         uri_encoded_name) == -1) {
694                 recording_url = NULL;
695                 ast_ari_response_error(
696                         response, 500, "Internal Server Error",
697                         "Out of memory");
698                 return;
699         }
700
701         json = stasis_app_recording_to_json(recording);
702         if (!json) {
703                 ast_ari_response_error(
704                         response, 500, "Internal Server Error",
705                         "Out of memory");
706                 return;
707         }
708
709         ast_ari_response_created(response, recording_url, json);
710 }
711
712 void ast_ari_channels_get(struct ast_variable *headers,
713         struct ast_ari_channels_get_args *args,
714         struct ast_ari_response *response)
715 {
716         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
717         struct stasis_cache *cache;
718         struct ast_channel_snapshot *snapshot;
719
720         cache = ast_channel_cache();
721         if (!cache) {
722                 ast_ari_response_error(
723                         response, 500, "Internal Server Error",
724                         "Message bus not initialized");
725                 return;
726         }
727
728         msg = stasis_cache_get(cache, ast_channel_snapshot_type(),
729                                    args->channel_id);
730         if (!msg) {
731                 ast_ari_response_error(
732                         response, 404, "Not Found",
733                         "Channel not found");
734                 return;
735         }
736
737         snapshot = stasis_message_data(msg);
738         ast_assert(snapshot != NULL);
739
740         ast_ari_response_ok(response,
741                                 ast_channel_snapshot_to_json(snapshot, NULL));
742 }
743
744 void ast_ari_channels_hangup(struct ast_variable *headers,
745         struct ast_ari_channels_hangup_args *args,
746         struct ast_ari_response *response)
747 {
748         RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
749         int cause;
750
751         chan = ast_channel_get_by_name(args->channel_id);
752         if (chan == NULL) {
753                 ast_ari_response_error(
754                         response, 404, "Not Found",
755                         "Channel not found");
756                 return;
757         }
758
759         if (ast_strlen_zero(args->reason) || !strcmp(args->reason, "normal")) {
760                 cause = AST_CAUSE_NORMAL;
761         } else if (!strcmp(args->reason, "busy")) {
762                 cause = AST_CAUSE_BUSY;
763         } else if (!strcmp(args->reason, "congestion")) {
764                 cause = AST_CAUSE_CONGESTION;
765         } else if (!strcmp(args->reason, "no_answer")) {
766                 cause = AST_CAUSE_NOANSWER;
767         } else {
768                 ast_ari_response_error(
769                         response, 400, "Invalid Reason",
770                         "Invalid reason for hangup provided");
771                 return;
772         }
773
774         ast_channel_hangupcause_set(chan, cause);
775         ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
776
777         ast_ari_response_no_content(response);
778 }
779
780 void ast_ari_channels_list(struct ast_variable *headers,
781         struct ast_ari_channels_list_args *args,
782         struct ast_ari_response *response)
783 {
784         RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
785         RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
786         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
787         struct ao2_iterator i;
788         void *obj;
789         struct stasis_message_sanitizer *sanitize = stasis_app_get_sanitizer();
790
791         cache = ast_channel_cache();
792         if (!cache) {
793                 ast_ari_response_error(
794                         response, 500, "Internal Server Error",
795                         "Message bus not initialized");
796                 return;
797         }
798         ao2_ref(cache, +1);
799
800         snapshots = stasis_cache_dump(cache, ast_channel_snapshot_type());
801         if (!snapshots) {
802                 ast_ari_response_alloc_failed(response);
803                 return;
804         }
805
806         json = ast_json_array_create();
807         if (!json) {
808                 ast_ari_response_alloc_failed(response);
809                 return;
810         }
811
812         i = ao2_iterator_init(snapshots, 0);
813         while ((obj = ao2_iterator_next(&i))) {
814                 RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
815                 struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
816                 int r;
817
818                 if (sanitize && sanitize->channel_snapshot
819                         && sanitize->channel_snapshot(snapshot)) {
820                         continue;
821                 }
822
823                 r = ast_json_array_append(
824                         json, ast_channel_snapshot_to_json(snapshot, NULL));
825                 if (r != 0) {
826                         ast_ari_response_alloc_failed(response);
827                         ao2_iterator_destroy(&i);
828                         return;
829                 }
830         }
831         ao2_iterator_destroy(&i);
832
833         ast_ari_response_ok(response, ast_json_ref(json));
834 }
835
836 /*! \brief Structure used for origination */
837 struct ari_origination {
838         /*! \brief Dialplan context */
839         char context[AST_MAX_CONTEXT];
840         /*! \brief Dialplan extension */
841         char exten[AST_MAX_EXTENSION];
842         /*! \brief Dialplan priority */
843         int priority;
844         /*! \brief Application data to pass to Stasis application */
845         char appdata[0];
846 };
847
848 /*! \brief Thread which dials and executes upon answer */
849 static void *ari_originate_dial(void *data)
850 {
851         struct ast_dial *dial = data;
852         struct ari_origination *origination = ast_dial_get_user_data(dial);
853         enum ast_dial_result res;
854
855         res = ast_dial_run(dial, NULL, 0);
856         if (res != AST_DIAL_RESULT_ANSWERED) {
857                 goto end;
858         }
859
860         if (!ast_strlen_zero(origination->appdata)) {
861                 struct ast_app *app = pbx_findapp("Stasis");
862
863                 if (app) {
864                         ast_verb(4, "Launching Stasis(%s) on %s\n", origination->appdata,
865                                 ast_channel_name(ast_dial_answered(dial)));
866                         pbx_exec(ast_dial_answered(dial), app, origination->appdata);
867                 } else {
868                         ast_log(LOG_WARNING, "No such application 'Stasis'\n");
869                 }
870         } else {
871                 struct ast_channel *answered = ast_dial_answered(dial);
872
873                 if (!ast_strlen_zero(origination->context)) {
874                         ast_channel_context_set(answered, origination->context);
875                 }
876
877                 if (!ast_strlen_zero(origination->exten)) {
878                         ast_channel_exten_set(answered, origination->exten);
879                 }
880
881                 if (origination->priority > 0) {
882                         ast_channel_priority_set(answered, origination->priority);
883                 }
884
885                 if (ast_pbx_run(answered)) {
886                         ast_log(LOG_ERROR, "Failed to start PBX on %s\n", ast_channel_name(answered));
887                 } else {
888                         /* PBX will have taken care of hanging up, so we steal the answered channel so dial doesn't do it */
889                         ast_dial_answered_steal(dial);
890                 }
891         }
892
893 end:
894         ast_dial_destroy(dial);
895         ast_free(origination);
896         return NULL;
897 }
898
899 static void ari_channels_handle_originate_with_id(const char *args_endpoint,
900         const char *args_extension,
901         const char *args_context,
902         long args_priority,
903         const char *args_label,
904         const char *args_app,
905         const char *args_app_args,
906         const char *args_caller_id,
907         int args_timeout,
908         struct ast_variable *variables,
909         const char *args_channel_id,
910         const char *args_other_channel_id,
911         const char *args_originator,
912         struct ast_ari_response *response)
913 {
914         char *dialtech;
915         char dialdevice[AST_CHANNEL_NAME];
916         struct ast_dial *dial;
917         char *caller_id = NULL;
918         char *cid_num = NULL;
919         char *cid_name = NULL;
920         char *stuff;
921         struct ast_channel *other = NULL;
922         struct ast_channel *chan = NULL;
923         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
924         struct ast_assigned_ids assignedids = {
925                 .uniqueid = args_channel_id,
926                 .uniqueid2 = args_other_channel_id,
927         };
928         struct ari_origination *origination;
929         pthread_t thread;
930
931         if ((assignedids.uniqueid && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid))
932                 || (assignedids.uniqueid2 && AST_MAX_PUBLIC_UNIQUEID < strlen(assignedids.uniqueid2))) {
933                 ast_ari_response_error(response, 400, "Bad Request",
934                         "Uniqueid length exceeds maximum of %d", AST_MAX_PUBLIC_UNIQUEID);
935                 return;
936         }
937
938         if (ast_strlen_zero(args_endpoint)) {
939                 ast_ari_response_error(response, 400, "Bad Request",
940                         "Endpoint must be specified");
941                 return;
942         }
943
944         dialtech = ast_strdupa(args_endpoint);
945         if ((stuff = strchr(dialtech, '/'))) {
946                 *stuff++ = '\0';
947                 ast_copy_string(dialdevice, stuff, sizeof(dialdevice));
948         }
949
950         if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) {
951                 ast_ari_response_error(response, 400, "Bad Request",
952                         "Invalid endpoint specified");
953                 return;
954         }
955
956         if (!ast_strlen_zero(args_app)) {
957                 RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);
958
959                 if (!appdata) {
960                         ast_ari_response_alloc_failed(response);
961                         return;
962                 }
963
964                 ast_str_set(&appdata, 0, "%s", args_app);
965                 if (!ast_strlen_zero(args_app_args)) {
966                         ast_str_append(&appdata, 0, ",%s", args_app_args);
967                 }
968
969                 origination = ast_calloc(1, sizeof(*origination) + ast_str_size(appdata) + 1);
970                 if (!origination) {
971                         ast_ari_response_alloc_failed(response);
972                         return;
973                 }
974
975                 strcpy(origination->appdata, ast_str_buffer(appdata));
976         } else if (!ast_strlen_zero(args_extension)) {
977                 origination = ast_calloc(1, sizeof(*origination) + 1);
978                 if (!origination) {
979                         ast_ari_response_alloc_failed(response);
980                         return;
981                 }
982
983                 ast_copy_string(origination->context, S_OR(args_context, "default"), sizeof(origination->context));
984                 ast_copy_string(origination->exten, args_extension, sizeof(origination->exten));
985
986                 if (!ast_strlen_zero(args_label)) {
987                         /* A label was provided in the request, use that */
988                         int ipri = 1;
989                         if (sscanf(args_label, "%30d", &ipri) != 1) {
990                                 ipri = ast_findlabel_extension(chan, origination->context, origination->exten, args_label, args_caller_id);
991
992                                 if (ipri == -1) {
993                                         ast_log(AST_LOG_ERROR, "Requested label: %s can not be found in context: %s\n", args_label, args_context);
994                                         ast_ari_response_error(response, 404, "Not Found", "Requested label can not be found");
995                                         return;
996                                 }
997                         } else {
998                                 ast_debug(3, "Numeric value provided for label, jumping to that priority\n");
999                         }
1000
1001                         if (ipri == 0) {
1002                                 ast_log(AST_LOG_ERROR, "Invalid priority label '%s' specified for extension %s in context: %s\n",
1003                                                 args_label, args_extension, args_context);
1004                                 ast_ari_response_error(response, 400, "Bad Request", "Requested priority is illegal");
1005                                 return;
1006                         }
1007
1008                         /* Our priority was provided by a label */
1009                         origination->priority =  ipri;
1010                 } else {
1011                         /* No label provided, use provided priority */
1012                         origination->priority = args_priority ? args_priority : 1;
1013                 }
1014
1015                 origination->appdata[0] = '\0';
1016         } else {
1017                 ast_ari_response_error(response, 400, "Bad Request",
1018                         "Application or extension must be specified");
1019                 return;
1020         }
1021
1022         dial = ast_dial_create();
1023         if (!dial) {
1024                 ast_ari_response_alloc_failed(response);
1025                 ast_free(origination);
1026                 return;
1027         }
1028         ast_dial_set_user_data(dial, origination);
1029
1030         if (ast_dial_append(dial, dialtech, dialdevice, &assignedids)) {
1031                 ast_ari_response_alloc_failed(response);
1032                 ast_dial_destroy(dial);
1033                 ast_free(origination);
1034                 return;
1035         }
1036
1037         if (args_timeout > 0) {
1038                 ast_dial_set_global_timeout(dial, args_timeout * 1000);
1039         } else if (args_timeout == -1) {
1040                 ast_dial_set_global_timeout(dial, -1);
1041         } else {
1042                 ast_dial_set_global_timeout(dial, 30000);
1043         }
1044
1045         if (!ast_strlen_zero(args_caller_id)) {
1046                 caller_id = ast_strdupa(args_caller_id);
1047                 ast_callerid_parse(caller_id, &cid_name, &cid_num);
1048
1049                 if (ast_is_shrinkable_phonenumber(cid_num)) {
1050                         ast_shrink_phone_number(cid_num);
1051                 }
1052         }
1053
1054         if (!ast_strlen_zero(args_originator)) {
1055                 other = ast_channel_get_by_name(args_originator);
1056                 if (!other) {
1057                         ast_ari_response_error(
1058                                 response, 400, "Bad Request",
1059                                 "Provided originator channel was not found");
1060                         ast_dial_destroy(dial);
1061                         ast_free(origination);
1062                         return;
1063                 }
1064         }
1065
1066         if (ast_dial_prerun(dial, other, NULL)) {
1067                 ast_ari_response_alloc_failed(response);
1068                 ast_dial_destroy(dial);
1069                 ast_free(origination);
1070                 ast_channel_cleanup(other);
1071                 return;
1072         }
1073
1074         ast_channel_cleanup(other);
1075
1076         chan = ast_dial_get_channel(dial, 0);
1077         if (!chan) {
1078                 ast_ari_response_alloc_failed(response);
1079                 ast_dial_destroy(dial);
1080                 ast_free(origination);
1081                 return;
1082         }
1083
1084         if (!ast_strlen_zero(cid_num) || !ast_strlen_zero(cid_name)) {
1085                 struct ast_party_connected_line connected;
1086
1087                 /*
1088                  * It seems strange to set the CallerID on an outgoing call leg
1089                  * to whom we are calling, but this function's callers are doing
1090                  * various Originate methods.  This call leg goes to the local
1091                  * user.  Once the called party answers, the dialplan needs to
1092                  * be able to access the CallerID from the CALLERID function as
1093                  * if the called party had placed this call.
1094                  */
1095                 ast_set_callerid(chan, cid_num, cid_name, cid_num);
1096
1097                 ast_party_connected_line_set_init(&connected, ast_channel_connected(chan));
1098                 if (!ast_strlen_zero(cid_num)) {
1099                         connected.id.number.valid = 1;
1100                         connected.id.number.str = (char *) cid_num;
1101                         connected.id.number.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
1102                 }
1103                 if (!ast_strlen_zero(cid_name)) {
1104                         connected.id.name.valid = 1;
1105                         connected.id.name.str = (char *) cid_name;
1106                         connected.id.name.presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
1107                 }
1108                 ast_channel_set_connected_line(chan, &connected, NULL);
1109         }
1110
1111         ast_channel_lock(chan);
1112         if (variables) {
1113                 ast_set_variables(chan, variables);
1114         }
1115         ast_set_flag(ast_channel_flags(chan), AST_FLAG_ORIGINATED);
1116
1117         if (!ast_strlen_zero(args_app)) {
1118                 struct ast_channel *local_peer;
1119
1120                 stasis_app_subscribe_channel(args_app, chan);
1121
1122                 /* Subscribe to the Local channel peer also. */
1123                 local_peer = ast_local_get_peer(chan);
1124                 if (local_peer) {
1125                         stasis_app_subscribe_channel(args_app, local_peer);
1126                         ast_channel_unref(local_peer);
1127                 }
1128         }
1129
1130         snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
1131         ast_channel_unlock(chan);
1132
1133         /* Before starting the async dial bump the ref in case the dial quickly goes away and takes
1134          * the reference with it
1135          */
1136         ast_channel_ref(chan);
1137
1138         if (ast_pthread_create_detached(&thread, NULL, ari_originate_dial, dial)) {
1139                 ast_ari_response_alloc_failed(response);
1140                 ast_dial_destroy(dial);
1141                 ast_free(origination);
1142         } else {
1143                 ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL));
1144         }
1145
1146         ast_channel_unref(chan);
1147         return;
1148 }
1149
1150 /*!
1151  * \internal
1152  * \brief Convert a \c ast_json list of key/value pair tuples into a \c ast_variable list
1153  * \since 13.3.0
1154  *
1155  * \param[out] response HTTP response if error
1156  * \param json_variables The JSON blob containing the variable
1157  * \param[out] variables An out reference to the variables to populate.
1158  *
1159  * \retval 0 on success.
1160  * \retval -1 on error.
1161  */
1162 static int json_to_ast_variables(struct ast_ari_response *response, struct ast_json *json_variables, struct ast_variable **variables)
1163 {
1164         enum ast_json_to_ast_vars_code res;
1165
1166         res = ast_json_to_ast_variables(json_variables, variables);
1167         switch (res) {
1168         case AST_JSON_TO_AST_VARS_CODE_SUCCESS:
1169                 return 0;
1170         case AST_JSON_TO_AST_VARS_CODE_INVALID_TYPE:
1171                 ast_ari_response_error(response, 400, "Bad Request",
1172                         "Only string values in the 'variables' object allowed");
1173                 break;
1174         case AST_JSON_TO_AST_VARS_CODE_OOM:
1175                 ast_ari_response_alloc_failed(response);
1176                 break;
1177         }
1178         ast_log(AST_LOG_ERROR, "Unable to convert 'variables' in JSON body to channel variables\n");
1179
1180         return -1;
1181 }
1182
1183 void ast_ari_channels_originate_with_id(struct ast_variable *headers,
1184         struct ast_ari_channels_originate_with_id_args *args,
1185         struct ast_ari_response *response)
1186 {
1187         struct ast_variable *variables = NULL;
1188
1189         /* Parse any query parameters out of the body parameter */
1190         if (args->variables) {
1191                 struct ast_json *json_variables;
1192
1193                 ast_ari_channels_originate_with_id_parse_body(args->variables, args);
1194                 json_variables = ast_json_object_get(args->variables, "variables");
1195                 if (json_variables
1196                         && json_to_ast_variables(response, json_variables, &variables)) {
1197                         return;
1198                 }
1199         }
1200
1201         ari_channels_handle_originate_with_id(
1202                 args->endpoint,
1203                 args->extension,
1204                 args->context,
1205                 args->priority,
1206                 args->label,
1207                 args->app,
1208                 args->app_args,
1209                 args->caller_id,
1210                 args->timeout,
1211                 variables,
1212                 args->channel_id,
1213                 args->other_channel_id,
1214                 args->originator,
1215                 response);
1216         ast_variables_destroy(variables);
1217 }
1218
1219 void ast_ari_channels_originate(struct ast_variable *headers,
1220         struct ast_ari_channels_originate_args *args,
1221         struct ast_ari_response *response)
1222 {
1223         struct ast_variable *variables = NULL;
1224
1225         /* Parse any query parameters out of the body parameter */
1226         if (args->variables) {
1227                 struct ast_json *json_variables;
1228
1229                 ast_ari_channels_originate_parse_body(args->variables, args);
1230                 json_variables = ast_json_object_get(args->variables, "variables");
1231                 if (json_variables
1232                         && json_to_ast_variables(response, json_variables, &variables)) {
1233                         return;
1234                 }
1235         }
1236
1237         ari_channels_handle_originate_with_id(
1238                 args->endpoint,
1239                 args->extension,
1240                 args->context,
1241                 args->priority,
1242                 args->label,
1243                 args->app,
1244                 args->app_args,
1245                 args->caller_id,
1246                 args->timeout,
1247                 variables,
1248                 args->channel_id,
1249                 args->other_channel_id,
1250                 args->originator,
1251                 response);
1252         ast_variables_destroy(variables);
1253 }
1254
1255 void ast_ari_channels_get_channel_var(struct ast_variable *headers,
1256         struct ast_ari_channels_get_channel_var_args *args,
1257         struct ast_ari_response *response)
1258 {
1259         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
1260         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
1261         RAII_VAR(struct ast_str *, value, ast_str_create(32), ast_free);
1262         RAII_VAR(struct ast_channel *, channel, NULL, ast_channel_cleanup);
1263
1264         ast_assert(response != NULL);
1265
1266         if (!value) {
1267                 ast_ari_response_alloc_failed(response);
1268                 return;
1269         }
1270
1271         if (ast_strlen_zero(args->variable)) {
1272                 ast_ari_response_error(
1273                         response, 400, "Bad Request",
1274                         "Variable name is required");
1275                 return;
1276         }
1277
1278         if (ast_strlen_zero(args->channel_id)) {
1279                 ast_ari_response_error(
1280                         response, 400, "Bad Request",
1281                         "Channel ID is required");
1282                 return;
1283         }
1284
1285         channel = ast_channel_get_by_name(args->channel_id);
1286         if (!channel) {
1287                 ast_ari_response_error(
1288                         response, 404, "Channel Not Found",
1289                         "Provided channel was not found");
1290                 return;
1291         }
1292
1293         /* You may be tempted to lock the channel you're about to read from. You
1294          * would be wrong. Some dialplan functions put the channel into
1295          * autoservice, which deadlocks if the channel is already locked.
1296          * ast_str_retrieve_variable() does its own locking, and the dialplan
1297          * functions need to as well. We should be fine without the lock.
1298          */
1299
1300         if (args->variable[strlen(args->variable) - 1] == ')') {
1301                 if (ast_func_read2(channel, args->variable, &value, 0)) {
1302                         ast_ari_response_error(
1303                                 response, 500, "Error With Function",
1304                                 "Unable to read provided function");
1305                         return;
1306                 }
1307         } else {
1308                 if (!ast_str_retrieve_variable(&value, 0, channel, NULL, args->variable)) {
1309                         ast_ari_response_error(
1310                                 response, 404, "Variable Not Found",
1311                                 "Provided variable was not found");
1312                         return;
1313                 }
1314         }
1315
1316         if (!(json = ast_json_pack("{s: s}", "value", S_OR(ast_str_buffer(value), "")))) {
1317                 ast_ari_response_alloc_failed(response);
1318                 return;
1319         }
1320
1321         ast_ari_response_ok(response, ast_json_ref(json));
1322 }
1323
1324 void ast_ari_channels_set_channel_var(struct ast_variable *headers,
1325         struct ast_ari_channels_set_channel_var_args *args,
1326         struct ast_ari_response *response)
1327 {
1328         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
1329
1330         ast_assert(response != NULL);
1331
1332         if (ast_strlen_zero(args->variable)) {
1333                 ast_ari_response_error(
1334                         response, 400, "Bad Request",
1335                         "Variable name is required");
1336                 return;
1337         }
1338
1339         control = find_control(response, args->channel_id);
1340         if (control == NULL) {
1341                 /* response filled in by find_control */
1342                 return;
1343         }
1344
1345         if (stasis_app_control_set_channel_var(control, args->variable, args->value)) {
1346                 ast_ari_response_error(
1347                         response, 400, "Bad Request",
1348                         "Failed to execute function");
1349                 return;
1350         }
1351
1352         ast_ari_response_no_content(response);
1353 }
1354
1355 static void ari_channels_handle_snoop_channel(
1356         const char *args_channel_id,
1357         const char *args_spy,
1358         const char *args_whisper,
1359         const char *args_app,
1360         const char *args_app_args,
1361         const char *args_snoop_id,
1362         struct ast_ari_response *response)
1363 {
1364         enum stasis_app_snoop_direction spy, whisper;
1365         RAII_VAR(struct ast_channel *, chan, NULL, ast_channel_cleanup);
1366         RAII_VAR(struct ast_channel *, snoop, NULL, ast_channel_cleanup);
1367         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
1368
1369         ast_assert(response != NULL);
1370
1371         if (ast_strlen_zero(args_spy) || !strcmp(args_spy, "none")) {
1372                 spy = STASIS_SNOOP_DIRECTION_NONE;
1373         } else if (!strcmp(args_spy, "both")) {
1374                 spy = STASIS_SNOOP_DIRECTION_BOTH;
1375         } else if (!strcmp(args_spy, "out")) {
1376                 spy = STASIS_SNOOP_DIRECTION_OUT;
1377         } else if (!strcmp(args_spy, "in")) {
1378                 spy = STASIS_SNOOP_DIRECTION_IN;
1379         } else {
1380                 ast_ari_response_error(
1381                         response, 400, "Bad Request",
1382                         "Invalid direction specified for spy");
1383                 return;
1384         }
1385
1386         if (ast_strlen_zero(args_whisper) || !strcmp(args_whisper, "none")) {
1387                 whisper = STASIS_SNOOP_DIRECTION_NONE;
1388         } else if (!strcmp(args_whisper, "both")) {
1389                 whisper = STASIS_SNOOP_DIRECTION_BOTH;
1390         } else if (!strcmp(args_whisper, "out")) {
1391                 whisper = STASIS_SNOOP_DIRECTION_OUT;
1392         } else if (!strcmp(args_whisper, "in")) {
1393                 whisper = STASIS_SNOOP_DIRECTION_IN;
1394         } else {
1395                 ast_ari_response_error(
1396                         response, 400, "Bad Request",
1397                         "Invalid direction specified for whisper");
1398                 return;
1399         }
1400
1401         if (spy == STASIS_SNOOP_DIRECTION_NONE && whisper == STASIS_SNOOP_DIRECTION_NONE) {
1402                 ast_ari_response_error(
1403                         response, 400, "Bad Request",
1404                         "Direction must be specified for at least spy or whisper");
1405                 return;
1406         } else if (ast_strlen_zero(args_app)) {
1407                 ast_ari_response_error(
1408                         response, 400, "Bad Request",
1409                         "Application name is required");
1410                 return;
1411         }
1412
1413         chan = ast_channel_get_by_name(args_channel_id);
1414         if (chan == NULL) {
1415                 ast_ari_response_error(
1416                         response, 404, "Channel Not Found",
1417                         "Provided channel was not found");
1418                 return;
1419         }
1420
1421         snoop = stasis_app_control_snoop(chan, spy, whisper, args_app, args_app_args,
1422                 args_snoop_id);
1423         if (snoop == NULL) {
1424                 ast_ari_response_error(
1425                         response, 500, "Internal error",
1426                         "Snoop channel could not be created");
1427                 return;
1428         }
1429
1430         snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(snoop));
1431         ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot, NULL));
1432 }
1433
1434 void ast_ari_channels_snoop_channel(struct ast_variable *headers,
1435         struct ast_ari_channels_snoop_channel_args *args,
1436         struct ast_ari_response *response)
1437 {
1438         ari_channels_handle_snoop_channel(
1439                 args->channel_id,
1440                 args->spy,
1441                 args->whisper,
1442                 args->app,
1443                 args->app_args,
1444                 args->snoop_id,
1445                 response);
1446 }
1447
1448 void ast_ari_channels_snoop_channel_with_id(struct ast_variable *headers,
1449         struct ast_ari_channels_snoop_channel_with_id_args *args,
1450         struct ast_ari_response *response)
1451 {
1452         ari_channels_handle_snoop_channel(
1453                 args->channel_id,
1454                 args->spy,
1455                 args->whisper,
1456                 args->app,
1457                 args->app_args,
1458                 args->snoop_id,
1459                 response);
1460 }