res_ari_channels: Add ring operation, dtmf operation, hangup reasons, and tweak early...
[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_channels.h"
43 #include "asterisk/causes.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 ast_ari_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                         ast_ari_response_error(response, 404, "Not Found",
71                                    "Channel not found");
72                         return NULL;
73                 }
74
75                 ast_ari_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 ast_ari_continue_in_dialplan(
85         struct ast_variable *headers,
86         struct ast_continue_in_dialplan_args *args,
87         struct ast_ari_response *response)
88 {
89         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
90
91         ast_assert(response != NULL);
92
93         control = find_control(response, args->channel_id);
94         if (control == NULL) {
95                 return;
96         }
97
98         if (stasis_app_control_continue(control, args->context, args->extension, args->priority)) {
99                 ast_ari_response_alloc_failed(response);
100                 return;
101         }
102
103         ast_ari_response_no_content(response);
104 }
105
106 void ast_ari_answer_channel(struct ast_variable *headers,
107                                 struct ast_answer_channel_args *args,
108                                 struct ast_ari_response *response)
109 {
110         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
111
112         control = find_control(response, args->channel_id);
113         if (control == NULL) {
114                 return;
115         }
116
117         if (stasis_app_control_answer(control) != 0) {
118                 ast_ari_response_error(
119                         response, 500, "Internal Server Error",
120                         "Failed to answer channel");
121                 return;
122         }
123
124         ast_ari_response_no_content(response);
125 }
126
127 void ast_ari_ring_channel(struct ast_variable *headers,
128                                 struct ast_ring_channel_args *args,
129                                 struct ast_ari_response *response)
130 {
131         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
132
133         control = find_control(response, args->channel_id);
134         if (control == NULL) {
135                 return;
136         }
137
138         stasis_app_control_ring(control);
139
140         ast_ari_response_no_content(response);
141 }
142
143 void ast_ari_mute_channel(struct ast_variable *headers, struct ast_mute_channel_args *args, struct ast_ari_response *response)
144 {
145         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
146         unsigned int direction = 0;
147         enum ast_frame_type frametype = AST_FRAME_VOICE;
148
149         control = find_control(response, args->channel_id);
150         if (control == NULL) {
151                 return;
152         }
153
154         if (ast_strlen_zero(args->direction)) {
155                 ast_ari_response_error(
156                         response, 400, "Bad Request",
157                         "Direction is required");
158                 return;
159         }
160
161         if (!strcmp(args->direction, "in")) {
162                 direction = AST_MUTE_DIRECTION_READ;
163         } else if (!strcmp(args->direction, "out")) {
164                 direction = AST_MUTE_DIRECTION_WRITE;
165         } else if (!strcmp(args->direction, "both")) {
166                 direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE;
167         } else {
168                 ast_ari_response_error(
169                         response, 400, "Bad Request",
170                         "Invalid direction specified");
171                 return;
172         }
173
174         stasis_app_control_mute(control, direction, frametype);
175
176         ast_ari_response_no_content(response);
177 }
178
179 void ast_ari_unmute_channel(struct ast_variable *headers, struct ast_unmute_channel_args *args, struct ast_ari_response *response)
180 {
181         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
182         unsigned int direction = 0;
183         enum ast_frame_type frametype = AST_FRAME_VOICE;
184
185         control = find_control(response, args->channel_id);
186         if (control == NULL) {
187                 return;
188         }
189
190         if (ast_strlen_zero(args->direction)) {
191                 ast_ari_response_error(
192                         response, 400, "Bad Request",
193                         "Direction is required");
194                 return;
195         }
196
197         if (!strcmp(args->direction, "in")) {
198                 direction = AST_MUTE_DIRECTION_READ;
199         } else if (!strcmp(args->direction, "out")) {
200                 direction = AST_MUTE_DIRECTION_WRITE;
201         } else if (!strcmp(args->direction, "both")) {
202                 direction = AST_MUTE_DIRECTION_READ | AST_MUTE_DIRECTION_WRITE;
203         } else {
204                 ast_ari_response_error(
205                         response, 400, "Bad Request",
206                         "Invalid direction specified");
207                 return;
208         }
209
210         stasis_app_control_unmute(control, direction, frametype);
211
212         ast_ari_response_no_content(response);
213 }
214
215 void ast_ari_send_dtmfchannel(struct ast_variable *headers, struct ast_send_dtmfchannel_args *args, struct ast_ari_response *response)
216 {
217         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
218
219         control = find_control(response, args->channel_id);
220         if (control == NULL) {
221                 return;
222         }
223
224         if (ast_strlen_zero(args->dtmf)) {
225                 ast_ari_response_error(
226                         response, 400, "Bad Request",
227                         "DTMF is required");
228                 return;
229         }
230
231         stasis_app_control_dtmf(control, args->dtmf, args->before, args->between, args->duration, args->after);
232
233         ast_ari_response_no_content(response);
234 }
235
236 void ast_ari_hold_channel(struct ast_variable *headers, struct ast_hold_channel_args *args, struct ast_ari_response *response)
237 {
238         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
239
240         control = find_control(response, args->channel_id);
241         if (control == NULL) {
242                 /* Response filled in by find_control */
243                 return;
244         }
245
246         stasis_app_control_hold(control);
247
248         ast_ari_response_no_content(response);
249 }
250
251 void ast_ari_unhold_channel(struct ast_variable *headers, struct ast_unhold_channel_args *args, struct ast_ari_response *response)
252 {
253         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
254
255         control = find_control(response, args->channel_id);
256         if (control == NULL) {
257                 /* Response filled in by find_control */
258                 return;
259         }
260
261         stasis_app_control_unhold(control);
262
263         ast_ari_response_no_content(response);
264 }
265
266 void ast_ari_moh_start_channel(struct ast_variable *headers, struct ast_moh_start_channel_args *args, struct ast_ari_response *response)
267 {
268         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
269
270         control = find_control(response, args->channel_id);
271         if (control == NULL) {
272                 /* Response filled in by find_control */
273                 return;
274         }
275
276         stasis_app_control_moh_start(control, args->moh_class);
277         ast_ari_response_no_content(response);
278 }
279
280 void ast_ari_moh_stop_channel(struct ast_variable *headers, struct ast_moh_stop_channel_args *args, struct ast_ari_response *response)
281 {
282         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
283
284         control = find_control(response, args->channel_id);
285         if (control == NULL) {
286                 /* Response filled in by find_control */
287                 return;
288         }
289
290         stasis_app_control_moh_stop(control);
291         ast_ari_response_no_content(response);
292 }
293
294 void ast_ari_play_on_channel(struct ast_variable *headers,
295         struct ast_play_on_channel_args *args,
296         struct ast_ari_response *response)
297 {
298         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
299         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
300         RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
301         RAII_VAR(char *, playback_url, NULL, ast_free);
302         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
303         const char *language;
304
305         ast_assert(response != NULL);
306
307         control = find_control(response, args->channel_id);
308         if (control == NULL) {
309                 /* Response filled in by find_control */
310                 return;
311         }
312
313         snapshot = stasis_app_control_get_snapshot(control);
314         if (!snapshot) {
315                 ast_ari_response_error(
316                         response, 404, "Not Found",
317                         "Channel not found");
318                 return;
319         }
320
321         if (args->skipms < 0) {
322                 ast_ari_response_error(
323                         response, 400, "Bad Request",
324                         "skipms cannot be negative");
325                 return;
326         }
327
328         if (args->offsetms < 0) {
329                 ast_ari_response_error(
330                         response, 400, "Bad Request",
331                         "offsetms cannot be negative");
332                 return;
333         }
334
335         language = S_OR(args->lang, snapshot->language);
336
337         playback = stasis_app_control_play_uri(control, args->media, language,
338                 args->channel_id, STASIS_PLAYBACK_TARGET_CHANNEL, args->skipms, args->offsetms);
339         if (!playback) {
340                 ast_ari_response_error(
341                         response, 500, "Internal Server Error",
342                         "Failed to queue media for playback");
343                 return;
344         }
345
346         ast_asprintf(&playback_url, "/playback/%s",
347                 stasis_app_playback_get_id(playback));
348         if (!playback_url) {
349                 ast_ari_response_error(
350                         response, 500, "Internal Server Error",
351                         "Out of memory");
352                 return;
353         }
354
355         json = stasis_app_playback_to_json(playback);
356         if (!json) {
357                 ast_ari_response_error(
358                         response, 500, "Internal Server Error",
359                         "Out of memory");
360                 return;
361         }
362
363         ast_ari_response_created(response, playback_url, json);
364 }
365
366 void ast_ari_record_channel(struct ast_variable *headers,
367         struct ast_record_channel_args *args,
368         struct ast_ari_response *response)
369 {
370         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
371         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
372         RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
373         RAII_VAR(char *, recording_url, NULL, ast_free);
374         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
375         RAII_VAR(struct stasis_app_recording_options *, options, NULL,
376                 ao2_cleanup);
377         RAII_VAR(char *, uri_encoded_name, NULL, ast_free);
378         size_t uri_name_maxlen;
379
380         ast_assert(response != NULL);
381
382         if (args->max_duration_seconds < 0) {
383                 ast_ari_response_error(
384                         response, 400, "Bad Request",
385                         "max_duration_seconds cannot be negative");
386                 return;
387         }
388
389         if (args->max_silence_seconds < 0) {
390                 ast_ari_response_error(
391                         response, 400, "Bad Request",
392                         "max_silence_seconds cannot be negative");
393                 return;
394         }
395
396         control = find_control(response, args->channel_id);
397         if (control == NULL) {
398                 /* Response filled in by find_control */
399                 return;
400         }
401
402         options = stasis_app_recording_options_create(args->name, args->format);
403         if (options == NULL) {
404                 ast_ari_response_error(
405                         response, 500, "Internal Server Error",
406                         "Out of memory");
407         }
408         options->max_silence_seconds = args->max_silence_seconds;
409         options->max_duration_seconds = args->max_duration_seconds;
410         options->terminate_on =
411                 stasis_app_recording_termination_parse(args->terminate_on);
412         options->if_exists =
413                 stasis_app_recording_if_exists_parse(args->if_exists);
414         options->beep = args->beep;
415
416         if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) {
417                 ast_ari_response_error(
418                         response, 400, "Bad Request",
419                         "terminateOn invalid");
420                 return;
421         }
422
423         if (options->if_exists == -1) {
424                 ast_ari_response_error(
425                         response, 400, "Bad Request",
426                         "ifExists invalid");
427                 return;
428         }
429
430         if (!ast_get_format_for_file_ext(options->format)) {
431                 ast_ari_response_error(
432                         response, 422, "Unprocessable Entity",
433                         "specified format is unknown on this system");
434                 return;
435         }
436
437         recording = stasis_app_control_record(control, options);
438         if (recording == NULL) {
439                 switch(errno) {
440                 case EINVAL:
441                         /* While the arguments are invalid, we should have
442                          * caught them prior to calling record.
443                          */
444                         ast_ari_response_error(
445                                 response, 500, "Internal Server Error",
446                                 "Error parsing request");
447                         break;
448                 case EEXIST:
449                         ast_ari_response_error(response, 409, "Conflict",
450                                 "Recording '%s' already exists and can not be overwritten",
451                                 args->name);
452                         break;
453                 case ENOMEM:
454                         ast_ari_response_error(
455                                 response, 500, "Internal Server Error",
456                                 "Out of memory");
457                         break;
458                 case EPERM:
459                         ast_ari_response_error(
460                                 response, 400, "Bad Request",
461                                 "Recording name invalid");
462                         break;
463                 default:
464                         ast_log(LOG_WARNING,
465                                 "Unrecognized recording error: %s\n",
466                                 strerror(errno));
467                         ast_ari_response_error(
468                                 response, 500, "Internal Server Error",
469                                 "Internal Server Error");
470                         break;
471                 }
472                 return;
473         }
474
475         uri_name_maxlen = strlen(args->name) * 3;
476         uri_encoded_name = ast_malloc(uri_name_maxlen);
477         if (!uri_encoded_name) {
478                 ast_ari_response_error(
479                         response, 500, "Internal Server Error",
480                         "Out of memory");
481                 return;
482         }
483         ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen,
484                 ast_uri_http);
485
486         ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name);
487         if (!recording_url) {
488                 ast_ari_response_error(
489                         response, 500, "Internal Server Error",
490                         "Out of memory");
491                 return;
492         }
493
494         json = stasis_app_recording_to_json(recording);
495         if (!json) {
496                 ast_ari_response_error(
497                         response, 500, "Internal Server Error",
498                         "Out of memory");
499                 return;
500         }
501
502         ast_ari_response_created(response, recording_url, json);
503 }
504
505 void ast_ari_get_channel(struct ast_variable *headers,
506                              struct ast_get_channel_args *args,
507                              struct ast_ari_response *response)
508 {
509         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
510         struct stasis_cache *cache;
511         struct ast_channel_snapshot *snapshot;
512
513         cache = ast_channel_cache();
514         if (!cache) {
515                 ast_ari_response_error(
516                         response, 500, "Internal Server Error",
517                         "Message bus not initialized");
518                 return;
519         }
520
521         msg = stasis_cache_get(cache, ast_channel_snapshot_type(),
522                                args->channel_id);
523         if (!msg) {
524                 ast_ari_response_error(
525                         response, 404, "Not Found",
526                         "Channel not found");
527                 return;
528         }
529
530         snapshot = stasis_message_data(msg);
531         ast_assert(snapshot != NULL);
532
533         ast_ari_response_ok(response,
534                                 ast_channel_snapshot_to_json(snapshot));
535 }
536
537 void ast_ari_delete_channel(struct ast_variable *headers,
538                                 struct ast_delete_channel_args *args,
539                                 struct ast_ari_response *response)
540 {
541         RAII_VAR(struct ast_channel *, chan, NULL, ao2_cleanup);
542         int cause;
543
544         chan = ast_channel_get_by_name(args->channel_id);
545         if (chan == NULL) {
546                 ast_ari_response_error(
547                         response, 404, "Not Found",
548                         "Channel not found");
549                 return;
550         }
551
552         if (ast_strlen_zero(args->reason) || !strcmp(args->reason, "normal")) {
553                 cause = AST_CAUSE_NORMAL;
554         } else if (!strcmp(args->reason, "busy")) {
555                 cause = AST_CAUSE_BUSY;
556         } else if (!strcmp(args->reason, "congestion")) {
557                 cause = AST_CAUSE_CONGESTION;
558         } else {
559                 ast_ari_response_error(
560                         response, 400, "Invalid Reason",
561                         "Invalid reason for hangup provided");
562                 return;
563         }
564
565         ast_channel_hangupcause_set(chan, cause);
566         ast_softhangup(chan, AST_SOFTHANGUP_EXPLICIT);
567
568         ast_ari_response_no_content(response);
569 }
570
571 void ast_ari_get_channels(struct ast_variable *headers,
572                               struct ast_get_channels_args *args,
573                               struct ast_ari_response *response)
574 {
575         RAII_VAR(struct stasis_cache *, cache, NULL, ao2_cleanup);
576         RAII_VAR(struct ao2_container *, snapshots, NULL, ao2_cleanup);
577         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
578         struct ao2_iterator i;
579         void *obj;
580
581         cache = ast_channel_cache();
582         if (!cache) {
583                 ast_ari_response_error(
584                         response, 500, "Internal Server Error",
585                         "Message bus not initialized");
586                 return;
587         }
588         ao2_ref(cache, +1);
589
590         snapshots = stasis_cache_dump(cache, ast_channel_snapshot_type());
591         if (!snapshots) {
592                 ast_ari_response_alloc_failed(response);
593                 return;
594         }
595
596         json = ast_json_array_create();
597         if (!json) {
598                 ast_ari_response_alloc_failed(response);
599                 return;
600         }
601
602         i = ao2_iterator_init(snapshots, 0);
603         while ((obj = ao2_iterator_next(&i))) {
604                 RAII_VAR(struct stasis_message *, msg, obj, ao2_cleanup);
605                 struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
606                 int r = ast_json_array_append(
607                         json, ast_channel_snapshot_to_json(snapshot));
608                 if (r != 0) {
609                         ast_ari_response_alloc_failed(response);
610                         return;
611                 }
612         }
613         ao2_iterator_destroy(&i);
614
615         ast_ari_response_ok(response, ast_json_ref(json));
616 }
617
618 void ast_ari_originate(struct ast_variable *headers,
619                            struct ast_originate_args *args,
620                            struct ast_ari_response *response)
621 {
622         char *dialtech;
623         char dialdevice[AST_CHANNEL_NAME];
624         char *caller_id = NULL;
625         char *cid_num = NULL;
626         char *cid_name = NULL;
627         int timeout = 30000;
628
629         char *stuff;
630         struct ast_channel *chan;
631         RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
632
633         if (ast_strlen_zero(args->endpoint)) {
634                 ast_ari_response_error(response, 400, "Bad Request",
635                         "Endpoint must be specified");
636                 return;
637         }
638
639         dialtech = ast_strdupa(args->endpoint);
640         if ((stuff = strchr(dialtech, '/'))) {
641                 *stuff++ = '\0';
642                 ast_copy_string(dialdevice, stuff, sizeof(dialdevice));
643         }
644
645         if (ast_strlen_zero(dialtech) || ast_strlen_zero(dialdevice)) {
646                 ast_ari_response_error(response, 400, "Bad Request",
647                         "Invalid endpoint specified");
648                 return;
649         }
650
651         if (args->timeout > 0) {
652                 timeout = args->timeout * 1000;
653         } else if (args->timeout == -1) {
654                 timeout = -1;
655         }
656
657         if (!ast_strlen_zero(args->caller_id)) {
658                 caller_id = ast_strdupa(args->caller_id);
659                 ast_callerid_parse(caller_id, &cid_name, &cid_num);
660
661                 if (ast_is_shrinkable_phonenumber(cid_num)) {
662                         ast_shrink_phone_number(cid_num);
663                 }
664         }
665
666         if (!ast_strlen_zero(args->app)) {
667                 const char *app = "Stasis";
668
669                 RAII_VAR(struct ast_str *, appdata, ast_str_create(64), ast_free);
670
671                 if (!appdata) {
672                         ast_ari_response_alloc_failed(response);
673                         return;
674                 }
675
676                 ast_str_set(&appdata, 0, "%s", args->app);
677                 if (!ast_strlen_zero(args->app_args)) {
678                         ast_str_append(&appdata, 0, ",%s", args->app_args);
679                 }
680
681                 /* originate a channel, putting it into an application */
682                 if (ast_pbx_outgoing_app(dialtech, NULL, dialdevice, timeout, app, ast_str_buffer(appdata), NULL, 0, cid_num, cid_name, NULL, NULL, &chan)) {
683                         ast_ari_response_alloc_failed(response);
684                         return;
685                 }
686         } else if (!ast_strlen_zero(args->extension)) {
687                 /* originate a channel, sending it to an extension */
688                 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, &chan, 0)) {
689                         ast_ari_response_alloc_failed(response);
690                         return;
691                 }
692         } else {
693                 ast_ari_response_error(response, 400, "Bad Request",
694                         "Application or extension must be specified");
695                 return;
696         }
697
698         snapshot = ast_channel_snapshot_create(chan);
699         ast_channel_unlock(chan);
700
701         if (!ast_strlen_zero(args->app)) {
702                 /* channel: + channel ID + null terminator */
703                 char uri[9 + strlen(ast_channel_uniqueid(chan))];
704                 const char *uris[1] = { uri, };
705
706                 sprintf(uri, "channel:%s", ast_channel_uniqueid(chan));
707                 stasis_app_subscribe(args->app, uris, 1, NULL);
708         }
709
710         ast_ari_response_ok(response, ast_channel_snapshot_to_json(snapshot));
711         ast_channel_unref(chan);
712 }
713
714 void ast_ari_get_channel_var(struct ast_variable *headers, struct ast_get_channel_var_args *args, struct ast_ari_response *response)
715 {
716         RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
717         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
718         RAII_VAR(char *, value, NULL, ast_free);
719
720         ast_assert(response != NULL);
721
722         if (ast_strlen_zero(args->variable)) {
723                 ast_ari_response_error(
724                         response, 400, "Bad Request",
725                         "Variable name is required");
726                 return;
727         }
728
729         control = find_control(response, args->channel_id);
730         if (control == NULL) {
731                 /* response filled in by find_control */
732                 return;
733         }
734
735         value = stasis_app_control_get_channel_var(control, args->variable);
736
737         if (!(json = ast_json_pack("{s: s}", "value", S_OR(value, "")))) {
738                 ast_ari_response_alloc_failed(response);
739                 return;
740         }
741
742         ast_ari_response_ok(response, ast_json_ref(json));
743 }
744
745 void ast_ari_set_channel_var(struct ast_variable *headers, struct ast_set_channel_var_args *args, struct ast_ari_response *response)
746 {
747         RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
748
749         ast_assert(response != NULL);
750
751         if (ast_strlen_zero(args->variable)) {
752                 ast_ari_response_error(
753                         response, 400, "Bad Request",
754                         "Variable name is required");
755                 return;
756         }
757
758         control = find_control(response, args->channel_id);
759         if (control == NULL) {
760                 /* response filled in by find_control */
761                 return;
762         }
763
764         if (stasis_app_control_set_channel_var(control, args->variable, args->value)) {
765                 ast_ari_response_error(
766                         response, 400, "Bad Request",
767                         "Failed to execute function");
768                 return;
769         }
770
771         ast_ari_response_no_content(response);
772 }
773