ARI: Add support for suppressing media streams.
[asterisk/asterisk.git] / res / stasis / control.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 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 Stasis application control support.
22  *
23  * \author David M. Lee, II <dlee@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include "asterisk/stasis_channels.h"
31
32 #include "command.h"
33 #include "control.h"
34 #include "asterisk/dial.h"
35 #include "asterisk/bridging.h"
36 #include "asterisk/bridging_basic.h"
37 #include "asterisk/bridging_features.h"
38 #include "asterisk/frame.h"
39 #include "asterisk/pbx.h"
40
41 struct stasis_app_control {
42         /*! Queue of commands to dispatch on the channel */
43         struct ao2_container *command_queue;
44         /*!
45          * When set, /c app_stasis should exit and continue in the dialplan.
46          */
47         int is_done:1;
48         /*!
49          * The associated channel.
50          * Be very careful with the threading associated w/ manipulating
51          * the channel.
52          */
53         struct ast_channel *channel;
54 };
55
56 struct stasis_app_control *control_create(struct ast_channel *channel)
57 {
58         struct stasis_app_control *control;
59
60         control = ao2_alloc(sizeof(*control), NULL);
61         if (!control) {
62                 return NULL;
63         }
64
65         control->command_queue = ao2_container_alloc_list(
66                 AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
67
68         control->channel = channel;
69
70         return control;
71 }
72
73 static struct stasis_app_command *exec_command(
74         struct stasis_app_control *control, stasis_app_command_cb command_fn,
75         void *data)
76 {
77         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
78
79         command = command_create(command_fn, data);
80
81         if (!command) {
82                 return NULL;
83         }
84
85         /* command_queue is a thread safe list; no lock needed */
86         ao2_link(control->command_queue, command);
87
88         ao2_ref(command, +1);
89         return command;
90 }
91
92 struct stasis_app_control_dial_data {
93         char endpoint[AST_CHANNEL_NAME];
94         int timeout;
95 };
96
97 static void *app_control_dial(struct stasis_app_control *control,
98         struct ast_channel *chan, void *data)
99 {
100         RAII_VAR(struct ast_dial *, dial, ast_dial_create(), ast_dial_destroy);
101         RAII_VAR(struct stasis_app_control_dial_data *, dial_data, data, ast_free);
102         enum ast_dial_result res;
103         char *tech, *resource;
104
105         struct ast_channel *new_chan;
106         struct ast_bridge *bridge;
107
108         tech = dial_data->endpoint;
109         if (!(resource = strchr(tech, '/'))) {
110                 return NULL;
111         }
112         *resource++ = '\0';
113
114         if (!dial) {
115                 ast_log(LOG_ERROR, "Failed to create dialing structure.\n");
116                 return NULL;
117         }
118
119         if (ast_dial_append(dial, tech, resource) < 0) {
120                 ast_log(LOG_ERROR, "Failed to add %s/%s to dialing structure.\n", tech, resource);
121                 return NULL;
122         }
123
124         ast_dial_set_global_timeout(dial, dial_data->timeout);
125
126         res = ast_dial_run(dial, NULL, 0);
127
128         if (res != AST_DIAL_RESULT_ANSWERED || !(new_chan = ast_dial_answered_steal(dial))) {
129                 return NULL;
130         }
131
132         if (!(bridge = ast_bridge_basic_new())) {
133                 ast_log(LOG_ERROR, "Failed to create basic bridge.\n");
134                 return NULL;
135         }
136
137         ast_bridge_impart(bridge, new_chan, NULL, NULL, 1);
138         stasis_app_control_add_channel_to_bridge(control, bridge);
139
140         return NULL;
141 }
142
143 int stasis_app_control_dial(struct stasis_app_control *control, const char *endpoint, int timeout)
144 {
145         struct stasis_app_control_dial_data *dial_data;
146
147         if (!(dial_data = ast_calloc(1, sizeof(*dial_data)))) {
148                 return -1;
149         }
150
151         ast_copy_string(dial_data->endpoint, endpoint, sizeof(dial_data->endpoint));
152
153         if (timeout > 0) {
154                 dial_data->timeout = timeout * 1000;
155         } else if (timeout == -1) {
156                 dial_data->timeout = -1;
157         } else {
158                 dial_data->timeout = 30000;
159         }
160
161         stasis_app_send_command_async(control, app_control_dial, dial_data);
162
163         return 0;
164 }
165
166 int control_is_done(struct stasis_app_control *control)
167 {
168         /* Called from stasis_app_exec thread; no lock needed */
169         return control->is_done;
170 }
171
172 struct stasis_app_control_continue_data {
173         char context[AST_MAX_CONTEXT];
174         char extension[AST_MAX_EXTENSION];
175         int priority;
176 };
177
178 static void *app_control_continue(struct stasis_app_control *control,
179         struct ast_channel *chan, void *data)
180 {
181         RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free);
182
183         /* Called from stasis_app_exec thread; no lock needed */
184         ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority);
185
186         control->is_done = 1;
187
188         return NULL;
189 }
190
191 int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority)
192 {
193         struct stasis_app_control_continue_data *continue_data;
194
195         if (!(continue_data = ast_calloc(1, sizeof(*continue_data)))) {
196                 return -1;
197         }
198         ast_copy_string(continue_data->context, S_OR(context, ""), sizeof(continue_data->context));
199         ast_copy_string(continue_data->extension, S_OR(extension, ""), sizeof(continue_data->extension));
200         if (priority > 0) {
201                 continue_data->priority = priority;
202         } else {
203                 continue_data->priority = -1;
204         }
205
206         stasis_app_send_command_async(control, app_control_continue, continue_data);
207
208         return 0;
209 }
210
211 struct stasis_app_control_mute_data {
212         enum ast_frame_type frametype;
213         unsigned int direction;
214 };
215
216 static void *app_control_mute(struct stasis_app_control *control,
217         struct ast_channel *chan, void *data)
218 {
219         RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
220         SCOPED_CHANNELLOCK(lockvar, chan);
221
222         ast_channel_suppress(control->channel, mute_data->direction, mute_data->frametype);
223
224         return NULL;
225 }
226
227 int stasis_app_control_mute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
228 {
229         struct stasis_app_control_mute_data *mute_data;
230
231         if (!(mute_data = ast_calloc(1, sizeof(*mute_data)))) {
232                 return -1;
233         }
234
235         mute_data->direction = direction;
236         mute_data->frametype = frametype;
237
238         stasis_app_send_command_async(control, app_control_mute, mute_data);
239
240         return 0;
241 }
242
243 static void *app_control_unmute(struct stasis_app_control *control,
244         struct ast_channel *chan, void *data)
245 {
246         RAII_VAR(struct stasis_app_control_mute_data *, mute_data, data, ast_free);
247         SCOPED_CHANNELLOCK(lockvar, chan);
248
249         ast_channel_unsuppress(control->channel, mute_data->direction, mute_data->frametype);
250
251         return NULL;
252 }
253
254 int stasis_app_control_unmute(struct stasis_app_control *control, unsigned int direction, enum ast_frame_type frametype)
255 {
256         struct stasis_app_control_mute_data *mute_data;
257
258         if (!(mute_data = ast_calloc(1, sizeof(*mute_data)))) {
259                 return -1;
260         }
261
262         mute_data->direction = direction;
263         mute_data->frametype = frametype;
264
265         stasis_app_send_command_async(control, app_control_unmute, mute_data);
266
267         return 0;
268 }
269
270 char *stasis_app_control_get_channel_var(struct stasis_app_control *control, const char *variable)
271 {
272         RAII_VAR(struct ast_str *, tmp, ast_str_create(32), ast_free);
273         SCOPED_CHANNELLOCK(lockvar, control->channel);
274
275         if (!tmp) {
276                 return NULL;
277         }
278
279         if (variable[strlen(variable) - 1] == ')') {
280                 if (ast_func_read2(control->channel, variable, &tmp, 0)) {
281                         return NULL;
282                 }
283         } else {
284                 if (!ast_str_retrieve_variable(&tmp, 0, control->channel, NULL, variable)) {
285                         return NULL;
286                 }
287         }
288
289         return ast_strdup(ast_str_buffer(tmp));
290 }
291
292 int stasis_app_control_set_channel_var(struct stasis_app_control *control, const char *variable, const char *value)
293 {
294         return pbx_builtin_setvar_helper(control->channel, variable, value);
295 }
296
297 static void *app_control_hold(struct stasis_app_control *control,
298         struct ast_channel *chan, void *data)
299 {
300         ast_indicate(control->channel, AST_CONTROL_HOLD);
301
302         return NULL;
303 }
304
305 void stasis_app_control_hold(struct stasis_app_control *control)
306 {
307         stasis_app_send_command_async(control, app_control_hold, NULL);
308 }
309
310 static void *app_control_unhold(struct stasis_app_control *control,
311         struct ast_channel *chan, void *data)
312 {
313         ast_indicate(control->channel, AST_CONTROL_UNHOLD);
314
315         return NULL;
316 }
317
318 void stasis_app_control_unhold(struct stasis_app_control *control)
319 {
320         stasis_app_send_command_async(control, app_control_unhold, NULL);
321 }
322
323 struct ast_channel_snapshot *stasis_app_control_get_snapshot(
324         const struct stasis_app_control *control)
325 {
326         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
327         struct stasis_caching_topic *caching_topic;
328         struct ast_channel_snapshot *snapshot;
329
330         caching_topic = ast_channel_topic_all_cached();
331         ast_assert(caching_topic != NULL);
332
333         msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
334                 stasis_app_control_get_channel_id(control));
335         if (!msg) {
336                 return NULL;
337         }
338
339         snapshot = stasis_message_data(msg);
340         ast_assert(snapshot != NULL);
341
342         ao2_ref(snapshot, +1);
343         return snapshot;
344 }
345
346 void *stasis_app_send_command(struct stasis_app_control *control,
347         stasis_app_command_cb command_fn, void *data)
348 {
349         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
350
351         if (control == NULL) {
352                 return NULL;
353         }
354
355         command = exec_command(control, command_fn, data);
356         if (!command) {
357                 return NULL;
358         }
359
360         return command_join(command);
361 }
362
363 int stasis_app_send_command_async(struct stasis_app_control *control,
364         stasis_app_command_cb command_fn, void *data)
365 {
366         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
367
368         if (control == NULL) {
369                 return -1;
370         }
371
372         command = exec_command(control, command_fn, data);
373         if (!command) {
374                 return -1;
375         }
376
377         return 0;
378 }
379
380 const char *stasis_app_control_get_channel_id(
381         const struct stasis_app_control *control)
382 {
383         return ast_channel_uniqueid(control->channel);
384 }
385
386 void stasis_app_control_publish(
387         struct stasis_app_control *control, struct stasis_message *message)
388 {
389         if (!control || !control->channel || !message) {
390                 return;
391         }
392         stasis_publish(ast_channel_topic(control->channel), message);
393 }
394
395 int stasis_app_control_queue_control(struct stasis_app_control *control,
396         enum ast_control_frame_type frame_type)
397 {
398         return ast_queue_control(control->channel, frame_type);
399 }
400
401 int control_dispatch_all(struct stasis_app_control *control,
402         struct ast_channel *chan)
403 {
404         int count = 0;
405         struct ao2_iterator i;
406         void *obj;
407
408         ast_assert(control->channel == chan);
409
410         i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
411
412         while ((obj = ao2_iterator_next(&i))) {
413                 RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
414                 command_invoke(command, control, chan);
415                 ++count;
416         }
417
418         ao2_iterator_destroy(&i);
419         return count;
420 }
421
422 /* Must be defined here since it must operate on the channel outside of the queue */
423 int stasis_app_control_remove_channel_from_bridge(
424         struct stasis_app_control *control, struct ast_bridge *bridge)
425 {
426         return ast_bridge_remove(bridge, control->channel);
427 }