Asterisk - The Open Source Telephony Project GIT-master-85241bd
resource_bridges.c
Go to the documentation of this file.
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 <support_level>core</support_level>
28 ***/
29
30#include "asterisk.h"
31
32#include "resource_bridges.h"
33#include "asterisk/stasis.h"
35#include "asterisk/stasis_app.h"
41#include "asterisk/channel.h"
42#include "asterisk/bridge.h"
43#include "asterisk/format_cap.h"
44#include "asterisk/file.h"
47
48/*!
49 * \brief Finds a bridge, filling the response with an error, if appropriate.
50 *
51 * \param[out] response Response to fill with an error if control is not found.
52 * \param bridge_id ID of the bridge to lookup.
53 *
54 * \return Bridget.
55 * \retval NULL if bridge does not exist.
56 */
57static struct ast_bridge *find_bridge(
58 struct ast_ari_response *response,
59 const char *bridge_id)
60{
61 RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
62
63 ast_assert(response != NULL);
64
65 bridge = stasis_app_bridge_find_by_id(bridge_id);
66 if (bridge == NULL) {
67 RAII_VAR(struct ast_bridge_snapshot *, snapshot,
69 if (!snapshot) {
70 ast_ari_response_error(response, 404, "Not found",
71 "Bridge not found");
72 return NULL;
73 }
74
75 ast_ari_response_error(response, 409, "Conflict",
76 "Bridge not in Stasis application");
77 return NULL;
78 }
79
80 ao2_ref(bridge, +1);
81 return bridge;
82}
83
84/*!
85 * \brief Finds the control object for a channel, filling the response with an
86 * error, if appropriate.
87 * \param[out] response Response to fill with an error if control is not found.
88 * \param channel_id ID of the channel to lookup.
89 * \return Channel control object.
90 * \retval NULL if control object does not exist.
91 */
93 struct ast_ari_response *response,
94 const char *channel_id)
95{
96 RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
97
98 ast_assert(response != NULL);
99
100 control = stasis_app_control_find_by_channel_id(channel_id);
101 if (control == NULL) {
102 /* Distinguish between 400 and 422 errors */
103 RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL,
105 snapshot = ast_channel_snapshot_get_latest(channel_id);
106 if (snapshot == NULL) {
107 ast_log(LOG_DEBUG, "Couldn't find '%s'\n", channel_id);
108 ast_ari_response_error(response, 400, "Bad Request",
109 "Channel not found");
110 return NULL;
111 }
112
113 ast_log(LOG_DEBUG, "Found non-stasis '%s'\n", channel_id);
114 ast_ari_response_error(response, 422, "Unprocessable Entity",
115 "Channel not in Stasis application");
116 return NULL;
117 }
118
119 ao2_ref(control, +1);
120 return control;
121}
122
124 size_t count;
126};
127
128static void control_list_dtor(void *obj) {
129 struct control_list *list = obj;
130 size_t i;
131
132 for (i = 0; i < list->count; ++i) {
133 ao2_cleanup(list->controls[i]);
134 list->controls[i] = NULL;
135 }
136}
137
138static struct control_list *control_list_create(struct ast_ari_response *response, size_t count, const char **channels) {
139 RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
140 size_t i;
141
142 if (count == 0 || !channels) {
143 ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel");
144 return NULL;
145 }
146
147 list = ao2_alloc(sizeof(*list) + count * sizeof(list->controls[0]), control_list_dtor);
148 if (!list) {
150 return NULL;
151 }
152
153 for (i = 0; i < count; ++i) {
154 if (ast_strlen_zero(channels[i])) {
155 continue;
156 }
157 list->controls[list->count] =
158 find_channel_control(response, channels[i]);
159 if (!list->controls[list->count]) {
160 /* response filled in by find_channel_control() */
161 return NULL;
162 }
163 ++list->count;
164 }
165
166 if (list->count == 0) {
167 ast_ari_response_error(response, 400, "Bad Request", "Missing parameter channel");
168 return NULL;
169 }
170
171 ao2_ref(list, +1);
172 return list;
173}
174
175static int check_add_remove_channel(struct ast_ari_response *response,
176 struct stasis_app_control *control,
178{
179 switch (result) {
182 response, 409, "Conflict", "Channel %s currently recording",
184 return -1;
186 return 0;
187 }
188 return 0;
189}
190
193 struct ast_ari_response *response)
194{
195 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
196 RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
197 size_t i;
198 int has_error = 0;
199
200 if (!bridge) {
201 /* Response filled in by find_bridge() */
202 return;
203 }
204
205 list = control_list_create(response, args->channel_count, args->channel);
206 if (!list) {
207 /* Response filled in by control_list_create() */
208 return;
209 }
210
211 for (i = 0; i < list->count; ++i) {
212 stasis_app_control_clear_roles(list->controls[i]);
213 if (!ast_strlen_zero(args->role)) {
214 if (stasis_app_control_add_role(list->controls[i], args->role)) {
216 return;
217 }
218 }
219
220 /* Apply bridge features to each of the channel controls */
221 if (!stasis_app_control_bridge_features_init(list->controls[i])) {
222 stasis_app_control_absorb_dtmf_in_bridge(list->controls[i], args->absorb_dtmf);
223 stasis_app_control_mute_in_bridge(list->controls[i], args->mute);
224 stasis_app_control_inhibit_colp_in_bridge(list->controls[i], args->inhibit_connected_line_updates);
225 }
226 }
227
228 for (i = 0; i < list->count; ++i) {
229 if ((has_error = check_add_remove_channel(response, list->controls[i],
231 list->controls[i], bridge)))) {
232 break;
233 }
234 }
235
236 if (!has_error) {
238 }
239}
240
243 struct ast_ari_response *response)
244{
245 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
246 RAII_VAR(struct control_list *, list, NULL, ao2_cleanup);
247 size_t i;
248
249 if (!bridge) {
250 /* Response filled in by find_bridge() */
251 return;
252 }
253
254 list = control_list_create(response, args->channel_count, args->channel);
255 if (!list) {
256 /* Response filled in by control_list_create() */
257 return;
258 }
259
260 /* Make sure all of the channels are in this bridge */
261 for (i = 0; i < list->count; ++i) {
262 if (stasis_app_get_bridge(list->controls[i]) != bridge) {
263 ast_log(LOG_WARNING, "Channel %s not in bridge %s\n",
264 args->channel[i], args->bridge_id);
265 ast_ari_response_error(response, 422,
266 "Unprocessable Entity",
267 "Channel not in this bridge");
268 return;
269 }
270 }
271
272 /* Now actually remove it */
273 for (i = 0; i < list->count; ++i) {
275 bridge);
276 }
277
279}
280
285 char bridge_id[0];
286};
287
288static void *bridge_channel_control_thread(void *data)
289{
290 struct bridge_channel_control_thread_data *thread_data = data;
291 struct ast_channel *bridge_channel = thread_data->bridge_channel;
292 struct stasis_app_control *control = thread_data->control;
293 struct stasis_forward *forward = thread_data->forward;
294 ast_callid callid = ast_channel_callid(bridge_channel);
295 char *bridge_id = ast_strdupa(thread_data->bridge_id);
296
297 if (callid) {
299 }
300
301 ast_free(thread_data);
302 thread_data = NULL;
303
304 stasis_app_control_execute_until_exhausted(bridge_channel, control);
306
308 stasis_forward_cancel(forward);
309 ao2_cleanup(control);
310 ast_hangup(bridge_channel);
311 return NULL;
312}
313
315{
316 RAII_VAR(struct ast_format_cap *, cap, NULL, ao2_cleanup);
317 struct ast_channel *chan;
318
320 if (!cap) {
321 return NULL;
322 }
323
325
326 chan = ast_request(type, cap, NULL, NULL, "ARI", NULL);
327 if (!chan) {
328 return NULL;
329 }
330
333 return NULL;
334 }
335 return chan;
336}
337
338/*!
339 * \brief Performs common setup for a bridge playback operation
340 * with both new controls and when existing controls are found.
341 *
342 * \param args_media medias to play
343 * \param args_media_count number of media items in \c media
344 * \param args_lang language string split from arguments
345 * \param args_offset_ms milliseconds offset split from arguments
346 * \param args_skipms
347 * \param args_playback_id string to use for playback split from
348 * arguments (null valid)
349 * \param response ARI response being built
350 * \param bridge Bridge the playback is being performed on
351 * \param control Control being used for the playback channel
352 * \param json contents of the response to ARI
353 * \param playback_url stores playback URL for use with response
354 *
355 * \retval -1 operation failed
356 * \return operation was successful
357 */
358static int ari_bridges_play_helper(const char **args_media,
359 size_t args_media_count,
360 const char *args_lang,
361 int args_offset_ms,
362 int args_skipms,
363 const char *args_playback_id,
364 struct ast_ari_response *response,
365 struct ast_bridge *bridge,
366 struct stasis_app_control *control,
367 struct ast_json **json,
368 char **playback_url)
369{
371 RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
372
373 const char *language;
374
376 if (!snapshot) {
378 response, 500, "Internal Error", "Failed to get control snapshot");
379 return -1;
380 }
381
382 language = S_OR(args_lang, snapshot->base->language);
383
384 playback = stasis_app_control_play_uri(control, args_media, args_media_count,
386 args_offset_ms, args_playback_id);
387
388 if (!playback) {
390 return -1;
391 }
392
393 if (ast_asprintf(playback_url, "/playbacks/%s",
394 stasis_app_playback_get_id(playback)) == -1) {
396 return -1;
397 }
398
399 *json = stasis_app_playback_to_json(playback);
400 if (!*json) {
402 return -1;
403 }
404
405 return 0;
406}
407
408static void ari_bridges_play_new(const char **args_media,
409 size_t args_media_count,
410 const char *args_lang,
411 int args_offset_ms,
412 int args_skipms,
413 const char *args_playback_id,
414 struct ast_ari_response *response,
415 struct ast_bridge *bridge)
416{
417 RAII_VAR(struct ast_channel *, play_channel, NULL, ast_hangup);
418 RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
419 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
421 RAII_VAR(char *, playback_url, NULL, ast_free);
422
423 struct stasis_topic *channel_topic;
424 struct stasis_topic *bridge_topic;
425 struct bridge_channel_control_thread_data *thread_data;
426 pthread_t threadid;
427
428 struct ast_frame prog = {
430 .subclass.integer = AST_CONTROL_PROGRESS,
431 };
432
433
434 if (!(play_channel = prepare_bridge_media_channel("Announcer"))) {
436 response, 500, "Internal Error", "Could not create playback channel");
437 return;
438 }
439 ast_debug(1, "Created announcer channel '%s'\n", ast_channel_name(play_channel));
440
441 bridge_topic = ast_bridge_topic(bridge);
442 channel_topic = ast_channel_topic(play_channel);
443
444 /* Forward messages from the playback channel topic to the bridge topic so that anything listening for
445 * messages on the bridge topic will receive the playback start/stop messages. Other messages that would
446 * go to this channel will be suppressed since the channel is marked as internal.
447 */
448 if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) {
450 response, 500, "Internal Error", "Could not forward playback channel stasis messages to bridge topic");
451 return;
452 }
453
454 if (ast_unreal_channel_push_to_bridge(play_channel, bridge,
457 response, 500, "Internal Error", "Failed to put playback channel into the bridge");
458 return;
459 }
460
461 control = stasis_app_control_create(play_channel);
462 if (control == NULL) {
464 return;
465 }
466
467 ao2_lock(control);
468 if (ari_bridges_play_helper(args_media, args_media_count, args_lang,
469 args_offset_ms, args_skipms, args_playback_id, response, bridge,
470 control, &json, &playback_url)) {
471 ao2_unlock(control);
472 return;
473 }
474 ao2_unlock(control);
475
476 if (stasis_app_bridge_playback_channel_add(bridge, play_channel, control)) {
478 return;
479 }
480
481 ast_bridge_queue_everyone_else(bridge, NULL, &prog);
482
483 /* Give play_channel and control reference to the thread data */
484 thread_data = ast_malloc(sizeof(*thread_data) + strlen(bridge->uniqueid) + 1);
485 if (!thread_data) {
486 stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
488 return;
489 }
490
491 thread_data->bridge_channel = play_channel;
492 thread_data->control = control;
493 thread_data->forward = channel_forward;
494 /* Safe */
495 strcpy(thread_data->bridge_id, bridge->uniqueid);
496
497 if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) {
498 stasis_app_bridge_playback_channel_remove((char *)bridge->uniqueid, control);
500 ast_free(thread_data);
501 return;
502 }
503
504 /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */
505 play_channel = NULL;
506 control = NULL;
507 channel_forward = NULL;
508
509 ast_ari_response_created(response, playback_url, ast_json_ref(json));
510}
511
516};
517
518/*!
519 * \brief Performs common setup for a bridge playback operation
520 * with both new controls and when existing controls are found.
521 *
522 * \param args_media medias to play
523 * \param args_media_count number of media items in \c media
524 * \param args_lang language string split from arguments
525 * \param args_offset_ms milliseconds offset split from arguments
526 * \param args_skipms
527 * \param args_playback_id string to use for playback split from
528 * arguments (null valid)
529 * \param response ARI response being built
530 * \param bridge Bridge the playback is being performed on
531 * \param found_channel The channel that was found controlling playback
532 *
533 * \retval PLAY_FOUND_SUCCESS The operation was successful
534 * \retval PLAY_FOUND_FAILURE The operation failed (terminal failure)
535 * \retval PLAY_FOUND_CHANNEL_UNAVAILABLE The operation failed because
536 * the channel requested to playback with is breaking down.
537 */
538static enum play_found_result ari_bridges_play_found(const char **args_media,
539 size_t args_media_count,
540 const char *args_lang,
541 int args_offset_ms,
542 int args_skipms,
543 const char *args_playback_id,
544 struct ast_ari_response *response,
545 struct ast_bridge *bridge,
546 struct ast_channel *found_channel)
547{
548 RAII_VAR(struct ast_channel *, play_channel, found_channel, ao2_cleanup);
549 RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
550 RAII_VAR(char *, playback_url, NULL, ast_free);
551 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
552
553 control = stasis_app_control_find_by_channel(play_channel);
554 if (!control) {
556 }
557
558 ao2_lock(control);
559 if (stasis_app_control_is_done(control)) {
560 /* We failed to queue the action. Bailout and return that we aren't terminal. */
561 ao2_unlock(control);
563 }
564
565 if (ari_bridges_play_helper(args_media, args_media_count,
566 args_lang, args_offset_ms, args_skipms, args_playback_id,
567 response, bridge, control, &json, &playback_url)) {
568 ao2_unlock(control);
569 return PLAY_FOUND_FAILURE;
570 }
571 ao2_unlock(control);
572
573 ast_ari_response_created(response, playback_url, ast_json_ref(json));
574 return PLAY_FOUND_SUCCESS;
575}
576
578 const char *args_bridge_id,
579 const char **args_media,
580 size_t args_media_count,
581 const char *args_lang,
582 int args_offset_ms,
583 int args_skipms,
584 const char *args_playback_id,
585 struct ast_ari_response *response)
586{
587 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args_bridge_id), ao2_cleanup);
588 struct ast_channel *play_channel;
589
590 ast_assert(response != NULL);
591
592 if (!bridge) {
593 return;
594 }
595
596 while ((play_channel = stasis_app_bridge_playback_channel_find(bridge))) {
597 /* If ari_bridges_play_found fails because the channel is unavailable for
598 * playback, The channel will be removed from the playback list soon. We
599 * can keep trying to get channels from the list until we either get one
600 * that will work or else there isn't a channel for this bridge anymore,
601 * in which case we'll revert to ari_bridges_play_new.
602 */
603 if (ari_bridges_play_found(args_media, args_media_count, args_lang,
604 args_offset_ms, args_skipms, args_playback_id, response,bridge,
605 play_channel) == PLAY_FOUND_CHANNEL_UNAVAILABLE) {
606 continue;
607 }
608 return;
609 }
610
611 ari_bridges_play_new(args_media, args_media_count, args_lang, args_offset_ms,
612 args_skipms, args_playback_id, response, bridge);
613}
614
615
618 struct ast_ari_response *response)
619{
620 ari_bridges_handle_play(args->bridge_id,
621 args->media,
622 args->media_count,
623 args->lang,
624 args->offsetms,
625 args->skipms,
626 args->playback_id,
627 response);
628}
629
632 struct ast_ari_response *response)
633{
634 ari_bridges_handle_play(args->bridge_id,
635 args->media,
636 args->media_count,
637 args->lang,
638 args->offsetms,
639 args->skipms,
640 args->playback_id,
641 response);
642}
643
646 struct ast_ari_response *response)
647{
648 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
649 RAII_VAR(struct ast_channel *, record_channel, NULL, ast_hangup);
650 RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
651 RAII_VAR(struct stasis_app_recording *, recording, NULL, ao2_cleanup);
652 RAII_VAR(char *, recording_url, NULL, ast_free);
653 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
655 RAII_VAR(char *, uri_encoded_name, NULL, ast_free);
657
658 struct stasis_topic *channel_topic;
659 struct stasis_topic *bridge_topic;
660 size_t uri_name_maxlen;
661 struct bridge_channel_control_thread_data *thread_data;
662 pthread_t threadid;
663
664 ast_assert(response != NULL);
665
666 if (bridge == NULL) {
667 return;
668 }
669
670 if (!(record_channel = prepare_bridge_media_channel("Recorder"))) {
672 response, 500, "Internal Server Error", "Failed to create recording channel");
673 return;
674 }
675
676 bridge_topic = ast_bridge_topic(bridge);
677 channel_topic = ast_channel_topic(record_channel);
678
679 /* Forward messages from the recording channel topic to the bridge topic so that anything listening for
680 * messages on the bridge topic will receive the recording start/stop messages. Other messages that would
681 * go to this channel will be suppressed since the channel is marked as internal.
682 */
683 if (!bridge_topic || !channel_topic || !(channel_forward = stasis_forward_all(channel_topic, bridge_topic))) {
685 response, 500, "Internal Error", "Could not forward record channel stasis messages to bridge topic");
686 return;
687 }
688
689 if (ast_unreal_channel_push_to_bridge(record_channel, bridge,
692 response, 500, "Internal Error", "Failed to put recording channel into the bridge");
693 return;
694 }
695
696 control = stasis_app_control_create(record_channel);
697 if (control == NULL) {
699 return;
700 }
701
703 if (options == NULL) {
705 return;
706 }
707
708 ast_string_field_build(options, target, "bridge:%s", args->bridge_id);
709 options->max_silence_seconds = args->max_silence_seconds;
710 options->max_duration_seconds = args->max_duration_seconds;
711 options->terminate_on =
713 options->if_exists =
715 options->beep = args->beep;
716
717 if (options->terminate_on == STASIS_APP_RECORDING_TERMINATE_INVALID) {
719 response, 400, "Bad Request",
720 "terminateOn invalid");
721 return;
722 }
723
724 if (options->if_exists == AST_RECORD_IF_EXISTS_ERROR) {
726 response, 400, "Bad Request",
727 "ifExists invalid");
728 return;
729 }
730
731 if (!ast_get_format_for_file_ext(options->format)) {
733 response, 422, "Unprocessable Entity",
734 "specified format is unknown on this system");
735 return;
736 }
737
739 if (recording == NULL) {
740 switch(errno) {
741 case EINVAL:
742 /* While the arguments are invalid, we should have
743 * caught them prior to calling record.
744 */
746 response, 500, "Internal Server Error",
747 "Error parsing request");
748 break;
749 case EEXIST:
750 ast_ari_response_error(response, 409, "Conflict",
751 "Recording '%s' already exists and can not be overwritten",
752 args->name);
753 break;
754 case ENOMEM:
756 break;
757 case EPERM:
759 response, 400, "Bad Request",
760 "Recording name invalid");
761 break;
762 default:
764 "Unrecognized recording error: %s\n",
765 strerror(errno));
767 response, 500, "Internal Server Error",
768 "Internal Server Error");
769 break;
770 }
771 return;
772 }
773
774 uri_name_maxlen = strlen(args->name) * 3;
775 uri_encoded_name = ast_malloc(uri_name_maxlen);
776 if (!uri_encoded_name) {
778 return;
779 }
780 ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, ast_uri_http);
781
782 if (ast_asprintf(&recording_url, "/recordings/live/%s",
783 uri_encoded_name) == -1) {
784 recording_url = NULL;
786 return;
787 }
788
789 json = stasis_app_recording_to_json(recording);
790 if (!json) {
792 return;
793 }
794
795 thread_data = ast_calloc(1, sizeof(*thread_data));
796 if (!thread_data) {
798 return;
799 }
800
801 thread_data->bridge_channel = record_channel;
802 thread_data->control = control;
803 thread_data->forward = channel_forward;
804
805 if (ast_pthread_create_detached(&threadid, NULL, bridge_channel_control_thread, thread_data)) {
807 ast_free(thread_data);
808 return;
809 }
810
811 /* These are owned by the other thread now, so we don't want RAII_VAR disposing of them. */
812 record_channel = NULL;
813 control = NULL;
814 channel_forward = NULL;
815
816 ast_ari_response_created(response, recording_url, ast_json_ref(json));
817}
818
821 struct ast_ari_response *response)
822{
823 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
824 struct ast_channel *moh_channel;
825 const char *moh_class = args->moh_class;
826
827 if (!bridge) {
828 /* The response is provided by find_bridge() */
829 return;
830 }
831
833 if (!moh_channel) {
835 return;
836 }
837
838 ast_moh_start(moh_channel, moh_class, NULL);
839 ast_channel_cleanup(moh_channel);
840
842
843}
844
847 struct ast_ari_response *response)
848{
849 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
850
851 if (!bridge) {
852 /* the response is provided by find_bridge() */
853 return;
854 }
855
858 response, 409, "Conflict",
859 "Bridge isn't playing music");
860 return;
861 }
862
864}
865
868 struct ast_ari_response *response)
869{
871 if (!snapshot) {
873 response, 404, "Not Found",
874 "Bridge not found");
875 return;
876 }
877
878 ast_ari_response_ok(response,
880}
881
884 struct ast_ari_response *response)
885{
886 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
887 if (!bridge) {
888 return;
889 }
890
893}
894
897 struct ast_ari_response *response)
898{
900 RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
901 struct ao2_iterator i;
902 struct ast_bridge *bridge;
903
905 if (!bridges) {
907 return;
908 }
909
910 json = ast_json_array_create();
911 if (!json) {
913 return;
914 }
915
917 while ((bridge = ao2_iterator_next(&i))) {
918 struct ast_bridge_snapshot *snapshot;
919 struct ast_json *json_bridge = NULL;
920
921 /* Invisible bridges don't get shown externally and have no snapshot */
923 ao2_ref(bridge, -1);
924 continue;
925 }
926
927 snapshot = ast_bridge_get_snapshot(bridge);
928 if (snapshot) {
929 json_bridge = ast_bridge_snapshot_to_json(snapshot, stasis_app_get_sanitizer());
930 ao2_ref(snapshot, -1);
931 }
932
933 ao2_ref(bridge, -1);
934
935 if (!json_bridge || ast_json_array_append(json, json_bridge)) {
938 return;
939 }
940 }
942
943 ast_ari_response_ok(response, ast_json_ref(json));
944}
945
948 struct ast_ari_response *response)
949{
950 RAII_VAR(struct ast_bridge *, bridge, stasis_app_bridge_create(args->type, args->name, args->bridge_id), ao2_cleanup);
951 RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
952
953 if (!bridge) {
955 response, 500, "Internal Error",
956 "Unable to create bridge");
957 return;
958 }
959
960 ast_bridge_lock(bridge);
961 snapshot = ast_bridge_snapshot_create(bridge);
962 ast_bridge_unlock(bridge);
963
964 if (!snapshot) {
966 response, 500, "Internal Error",
967 "Unable to create snapshot for new bridge");
968 return;
969 }
970
971 ast_ari_response_ok(response,
973}
974
977 struct ast_ari_response *response)
978{
979 RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
980 RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
981
982 if (bridge) {
983 /* update */
984 if (!ast_strlen_zero(args->name)
985 && strcmp(args->name, bridge->name)) {
987 response, 500, "Internal Error",
988 "Changing bridge name is not implemented");
989 return;
990 }
991 if (!ast_strlen_zero(args->type)) {
993 response, 500, "Internal Error",
994 "Supplying a bridge type when updating a bridge is not allowed.");
995 return;
996 }
997 ast_ari_response_ok(response,
999 return;
1000 }
1001
1002 bridge = stasis_app_bridge_create(args->type, args->name, args->bridge_id);
1003 if (!bridge) {
1005 response, 500, "Internal Error",
1006 "Unable to create bridge");
1007 return;
1008 }
1009
1010 ast_bridge_lock(bridge);
1011 snapshot = ast_bridge_snapshot_create(bridge);
1012 ast_bridge_unlock(bridge);
1013
1014 if (!snapshot) {
1016 response, 500, "Internal Error",
1017 "Unable to create snapshot for new bridge");
1018 return;
1019 }
1020
1021 ast_ari_response_ok(response,
1023}
1024
1026 struct ast_channel *chan, void *data)
1027{
1028 struct ast_bridge *bridge = data;
1029
1030 ast_bridge_lock(bridge);
1032 ast_bridge_unlock(bridge);
1033
1034 return 0;
1035}
1036
1039{
1040 struct ast_bridge *bridge;
1041 struct stasis_app_control *control;
1042
1043 bridge = find_bridge(response, args->bridge_id);
1044 if (!bridge) {
1045 return;
1046 }
1047
1048 control = find_channel_control(response, args->channel_id);
1049 if (!control) {
1050 ao2_ref(bridge, -1);
1051 return;
1052 }
1053
1054 if (stasis_app_get_bridge(control) != bridge) {
1055 ast_ari_response_error(response, 422,
1056 "Unprocessable Entity",
1057 "Channel not in this bridge");
1058 ao2_ref(bridge, -1);
1059 ao2_ref(control, -1);
1060 return;
1061 }
1062
1065
1066 ao2_ref(bridge, -1);
1067 ao2_ref(control, -1);
1068
1070}
1071
1074{
1075 struct ast_bridge *bridge;
1076
1077 bridge = find_bridge(response, args->bridge_id);
1078 if (!bridge) {
1079 return;
1080 }
1081
1082 ast_bridge_lock(bridge);
1084 ast_bridge_unlock(bridge);
1085
1086 ao2_ref(bridge, -1);
1088}
void ast_ari_response_created(struct ast_ari_response *response, const char *url, struct ast_json *message)
Fill in a Created (201) ast_ari_response.
Definition: res_ari.c:305
void ast_ari_response_error(struct ast_ari_response *response, int response_code, const char *response_text, const char *message_fmt,...)
Fill in an error ast_ari_response.
Definition: res_ari.c:259
void ast_ari_response_ok(struct ast_ari_response *response, struct ast_json *message)
Fill in an OK (200) ast_ari_response.
Definition: res_ari.c:276
void ast_ari_response_alloc_failed(struct ast_ari_response *response)
Fill in response with a 500 message for allocation failures.
Definition: res_ari.c:298
void ast_ari_response_no_content(struct ast_ari_response *response)
Fill in a No Content (204) ast_ari_response.
Definition: res_ari.c:284
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void __ao2_cleanup(void *obj)
Definition: astobj2.c:677
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
static struct ao2_container * bridges
Definition: bridge.c:123
Bridging API.
struct ao2_container * ast_bridges(void)
Returns the global bridges container.
Definition: bridge.c:174
void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
Set the bridge to pick the strongest talker supporting video as the single source video feed.
Definition: bridge.c:3766
#define ast_bridge_unlock(bridge)
Unlock the bridge.
Definition: bridge.h:481
void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan)
Set a bridge to feed a single video source to all participants.
Definition: bridge.c:3749
#define ast_bridge_lock(bridge)
Lock the bridge.
Definition: bridge.h:470
int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
Queue the given frame to everyone else.
@ AST_BRIDGE_FLAG_INVISIBLE
@ AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE
@ AST_BRIDGE_CHANNEL_FLAG_LONELY
static PGresult * result
Definition: cel_pgsql.c:84
static char language[MAX_LANGUAGE]
Definition: chan_iax2.c:324
static const char type[]
Definition: chan_ooh323.c:109
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
ast_callid ast_channel_callid(const struct ast_channel *chan)
#define ast_channel_cleanup(c)
Cleanup a channel reference.
Definition: channel.h:2969
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition: channel.c:6354
static struct channel_usage channels
Unreal channel derivative framework.
int ast_unreal_channel_push_to_bridge(struct ast_channel *ast, struct ast_bridge *bridge, unsigned int flags)
Push the semi2 unreal channel into a bridge from either member of the unreal pair.
Definition: core_unreal.c:928
Generic File Format Support. Should be included by clients of the file handling routines....
struct ast_format * ast_get_format_for_file_ext(const char *file_ext)
Get the ast_format associated with the given file extension.
Definition: file.c:2006
Media Format Cache API.
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
Format Capabilities API.
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object,...
@ AST_RECORD_IF_EXISTS_ERROR
@ AST_FRAME_CONTROL
@ AST_CONTROL_PROGRESS
#define ast_debug(level,...)
Log a DEBUG message.
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition: logger.c:2320
unsigned int ast_callid
#define LOG_DEBUG
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
int ast_json_array_append(struct ast_json *array, struct ast_json *value)
Append to an array.
Definition: json.c:378
struct ast_json * ast_json_array_create(void)
Create a empty JSON array.
Definition: json.c:362
struct ast_json * ast_json_ref(struct ast_json *value)
Increase refcount on value.
Definition: json.c:67
int errno
Music on hold handling.
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7766
#define NULL
Definition: resample.c:96
void ast_ari_bridges_add_channel(struct ast_variable *headers, struct ast_ari_bridges_add_channel_args *args, struct ast_ari_response *response)
Add a channel to a bridge.
void ast_ari_bridges_clear_video_source(struct ast_variable *headers, struct ast_ari_bridges_clear_video_source_args *args, struct ast_ari_response *response)
Removes any explicit video source in a multi-party mixing bridge. This operation has no effect on bri...
void ast_ari_bridges_get(struct ast_variable *headers, struct ast_ari_bridges_get_args *args, struct ast_ari_response *response)
Get bridge details.
static struct stasis_app_control * find_channel_control(struct ast_ari_response *response, const char *channel_id)
Finds the control object for a channel, filling the response with an error, if appropriate.
void ast_ari_bridges_play_with_id(struct ast_variable *headers, struct ast_ari_bridges_play_with_id_args *args, struct ast_ari_response *response)
Start playback of media on a bridge.
static struct control_list * control_list_create(struct ast_ari_response *response, size_t count, const char **channels)
void ast_ari_bridges_play(struct ast_variable *headers, struct ast_ari_bridges_play_args *args, struct ast_ari_response *response)
Start playback of media on a bridge.
static void control_list_dtor(void *obj)
static struct ast_bridge * find_bridge(struct ast_ari_response *response, const char *bridge_id)
Finds a bridge, filling the response with an error, if appropriate.
static int bridge_set_video_source_cb(struct stasis_app_control *control, struct ast_channel *chan, void *data)
void ast_ari_bridges_destroy(struct ast_variable *headers, struct ast_ari_bridges_destroy_args *args, struct ast_ari_response *response)
Shut down a bridge.
play_found_result
@ PLAY_FOUND_CHANNEL_UNAVAILABLE
@ PLAY_FOUND_SUCCESS
@ PLAY_FOUND_FAILURE
void ast_ari_bridges_remove_channel(struct ast_variable *headers, struct ast_ari_bridges_remove_channel_args *args, struct ast_ari_response *response)
Remove a channel from a bridge.
void ast_ari_bridges_create(struct ast_variable *headers, struct ast_ari_bridges_create_args *args, struct ast_ari_response *response)
Create a new bridge.
void ast_ari_bridges_create_with_id(struct ast_variable *headers, struct ast_ari_bridges_create_with_id_args *args, struct ast_ari_response *response)
Create a new bridge or updates an existing one.
static int ari_bridges_play_helper(const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge, struct stasis_app_control *control, struct ast_json **json, char **playback_url)
Performs common setup for a bridge playback operation with both new controls and when existing contro...
static void * bridge_channel_control_thread(void *data)
static int check_add_remove_channel(struct ast_ari_response *response, struct stasis_app_control *control, enum stasis_app_control_channel_result result)
static void ari_bridges_play_new(const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge)
void ast_ari_bridges_set_video_source(struct ast_variable *headers, struct ast_ari_bridges_set_video_source_args *args, struct ast_ari_response *response)
Set a channel as the video source in a multi-party mixing bridge. This operation has no effect on bri...
static enum play_found_result ari_bridges_play_found(const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response, struct ast_bridge *bridge, struct ast_channel *found_channel)
Performs common setup for a bridge playback operation with both new controls and when existing contro...
void ast_ari_bridges_record(struct ast_variable *headers, struct ast_ari_bridges_record_args *args, struct ast_ari_response *response)
Start a recording.
void ast_ari_bridges_start_moh(struct ast_variable *headers, struct ast_ari_bridges_start_moh_args *args, struct ast_ari_response *response)
Play music on hold to a bridge or change the MOH class that is playing.
static void ari_bridges_handle_play(const char *args_bridge_id, const char **args_media, size_t args_media_count, const char *args_lang, int args_offset_ms, int args_skipms, const char *args_playback_id, struct ast_ari_response *response)
void ast_ari_bridges_stop_moh(struct ast_variable *headers, struct ast_ari_bridges_stop_moh_args *args, struct ast_ari_response *response)
Stop playing music on hold to a bridge.
void ast_ari_bridges_list(struct ast_variable *headers, struct ast_ari_bridges_list_args *args, struct ast_ari_response *response)
List all active bridges in Asterisk.
static struct ast_channel * prepare_bridge_media_channel(const char *type)
Generated file - declares stubs to be implemented in res/ari/resource_bridges.c.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
struct stasis_forward * stasis_forward_cancel(struct stasis_forward *forward)
Definition: stasis.c:1548
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
Definition: stasis.c:1578
Stasis Application API. See Stasis Application API for detailed documentation.
void stasis_app_control_mute_in_bridge(struct stasis_app_control *control, int mute)
Set whether audio from the channel is muted instead of passing through to the bridge.
Definition: control.c:1492
void stasis_app_bridge_playback_channel_remove(char *bridge_id, struct stasis_app_control *control)
remove channel from list of ARI playback channels for bridges.
Definition: res_stasis.c:743
struct stasis_message_sanitizer * stasis_app_get_sanitizer(void)
Get the Stasis message sanitizer for app_stasis applications.
Definition: res_stasis.c:2271
const char * stasis_app_control_get_channel_id(const struct stasis_app_control *control)
Returns the uniqueid of the channel associated with this control.
Definition: control.c:1452
int stasis_app_control_add_channel_to_bridge(struct stasis_app_control *control, struct ast_bridge *bridge)
Add a channel to the bridge.
Definition: control.c:1405
void stasis_app_control_clear_roles(struct stasis_app_control *control)
Clear bridge roles currently applied to a channel controlled by a stasis app control.
Definition: control.c:360
void stasis_app_bridge_destroy(const char *bridge_id)
Destroy the bridge.
Definition: res_stasis.c:864
struct ast_bridge * stasis_app_bridge_find_by_id(const char *bridge_id)
Returns the bridge with the given id.
Definition: res_stasis.c:774
struct ast_bridge * stasis_app_bridge_create(const char *type, const char *name, const char *id)
Create a bridge of the specified type.
Definition: res_stasis.c:854
void stasis_app_control_absorb_dtmf_in_bridge(struct stasis_app_control *control, int absorb)
Set whether DTMF from the channel is absorbed instead of passing through to the bridge.
Definition: control.c:1486
void stasis_app_control_execute_until_exhausted(struct ast_channel *chan, struct stasis_app_control *control)
Act on a stasis app control queue until it is empty.
Definition: res_stasis.c:1253
struct ast_channel * stasis_app_bridge_moh_channel(struct ast_bridge *bridge)
Finds or creates an announcer channel in a bridge that can play music on hold.
Definition: res_stasis.c:629
void stasis_app_control_flush_queue(struct stasis_app_control *control)
Flush the control command queue.
Definition: res_stasis.c:1281
int stasis_app_control_add_role(struct stasis_app_control *control, const char *role)
Apply a bridge role to a channel controlled by a stasis app control.
Definition: control.c:338
int stasis_app_channel_unreal_set_internal(struct ast_channel *chan)
Mark this unreal channel and it's other half as being internal to Stasis.
Definition: res_stasis.c:2295
void stasis_app_control_inhibit_colp_in_bridge(struct stasis_app_control *control, int inhibit_colp)
Set whether COLP frames should be generated when joining the bridge.
Definition: control.c:1498
int stasis_app_control_remove_channel_from_bridge(struct stasis_app_control *control, struct ast_bridge *bridge)
Remove a channel from the bridge.
Definition: control.c:1442
struct ast_bridge * stasis_app_get_bridge(struct stasis_app_control *control)
Gets the bridge currently associated with a control object.
Definition: control.c:953
int stasis_app_control_bridge_features_init(struct stasis_app_control *control)
Initialize bridge features into a channel control.
Definition: control.c:1473
int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge, struct ast_channel *chan, struct stasis_app_control *control)
Adds a channel to the list of ARI playback channels for bridges.
Definition: res_stasis.c:705
struct stasis_app_control * stasis_app_control_find_by_channel_id(const char *channel_id)
Returns the handler for the channel with the given id.
Definition: res_stasis.c:349
struct ast_channel * stasis_app_bridge_playback_channel_find(struct ast_bridge *bridge)
Finds an existing ARI playback channel in a bridge.
Definition: res_stasis.c:759
int stasis_app_control_is_done(struct stasis_app_control *control)
Check if a control is marked as done.
Definition: res_stasis.c:1276
struct ast_channel_snapshot * stasis_app_control_get_snapshot(const struct stasis_app_control *control)
Returns the most recent snapshot for the associated channel.
Definition: control.c:882
stasis_app_control_channel_result
Result codes used when adding/removing channels to/from bridges.
Definition: stasis_app.h:776
@ STASIS_APP_CHANNEL_RECORDING
Definition: stasis_app.h:780
@ STASIS_APP_CHANNEL_OKAY
Definition: stasis_app.h:778
struct stasis_app_control * stasis_app_control_find_by_channel(const struct ast_channel *chan)
Returns the handler for the given channel.
Definition: res_stasis.c:338
struct stasis_app_control * stasis_app_control_create(struct ast_channel *chan)
Creates a control handler for a channel that isn't in a stasis app.
Definition: res_stasis.c:333
int stasis_app_bridge_moh_stop(struct ast_bridge *bridge)
Breaks down MOH channels playing on the bridge created by stasis_app_bridge_moh_channel.
Definition: res_stasis.c:649
Backend API for implementing components of res_stasis.
int stasis_app_send_command(struct stasis_app_control *control, stasis_app_command_cb command, void *data, command_data_destructor_fn data_destructor)
Invokes a command on a control's channel.
Definition: control.c:920
Stasis Application Playback API. See StasisApplication API" for detailed documentation.
@ STASIS_PLAYBACK_TARGET_BRIDGE
struct stasis_app_playback * stasis_app_control_play_uri(struct stasis_app_control *control, const char **media, size_t media_count, const char *language, const char *target_id, enum stasis_app_playback_target_type target_type, int skipms, long offsetms, const char *id)
Play a file to the control's channel.
struct ast_json * stasis_app_playback_to_json(const struct stasis_app_playback *playback)
Convert a playback to its JSON representation.
const char * stasis_app_playback_get_id(struct stasis_app_playback *playback)
Gets the unique id of a playback object.
Stasis Application Recording API. See StasisApplication API" for detailed documentation.
struct stasis_app_recording_options * stasis_app_recording_options_create(const char *name, const char *format)
Allocate a recording options object.
struct ast_json * stasis_app_recording_to_json(const struct stasis_app_recording *recording)
Construct a JSON model of a recording.
struct stasis_app_recording * stasis_app_control_record(struct stasis_app_control *control, struct stasis_app_recording_options *options)
Record media from a channel.
enum ast_record_if_exists stasis_app_recording_if_exists_parse(const char *str)
Parse a string into the if_exists enum.
#define STASIS_APP_RECORDING_TERMINATE_INVALID
char stasis_app_recording_termination_parse(const char *str)
Parse a string into the recording termination enum.
struct ast_json * ast_bridge_snapshot_to_json(const struct ast_bridge_snapshot *snapshot, const struct stasis_message_sanitizer *sanitize)
Build a JSON object from a ast_bridge_snapshot.
struct ast_bridge_snapshot * ast_bridge_get_snapshot(struct ast_bridge *bridge)
Returns the current snapshot for the bridge.
struct ast_bridge_snapshot * ast_bridge_get_snapshot_by_uniqueid(const char *bridge_id)
Returns the current snapshot for the bridge.
struct ast_bridge_snapshot * ast_bridge_snapshot_create(struct ast_bridge *bridge)
Generate a snapshot of the bridge state. This is an ao2 object, so ao2_cleanup() to deallocate.
struct stasis_topic * ast_bridge_topic(struct ast_bridge *bridge)
A topic which publishes the events for a particular bridge.
#define ast_string_field_build(x, field, fmt, args...)
Set a field to a complex (built) value.
Definition: stringfields.h:555
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Structure that contains a snapshot of information about a bridge.
Definition: bridge.h:314
Structure that contains information about a bridge.
Definition: bridge.h:349
const ast_string_field uniqueid
Definition: bridge.h:401
struct ast_flags feature_flags
Definition: bridge.h:369
const ast_string_field language
Structure representing a snapshot of channel state.
struct ast_channel_snapshot_base * base
Main Channel structure associated with a channel.
struct ast_bridge_channel * bridge_channel
struct ast_channel_snapshot * snapshot
struct ast_bridge * bridge
struct stasis_forward * channel_forward
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Data structure associated with a single frame of data.
enum ast_frame_type frametype
Abstract JSON element (object, array, string, int, ...).
Structure for variables, used for configurations and for channel variables.
struct stasis_app_control * control
struct stasis_app_control * controls[]
struct ast_bridge * bridge
Definition: control.c:66
Forwarding information.
Definition: stasis.c:1531
const char * args
static struct test_options options
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
#define ast_assert(a)
Definition: utils.h:739
char * ast_uri_encode(const char *string, char *outbuf, int buflen, struct ast_flags spec)
Turn text string to URI-encoded XX version.
Definition: utils.c:723
const struct ast_flags ast_uri_http
Definition: utils.c:719
#define ast_pthread_create_detached(a, b, c, d)
Definition: utils.h:588