ARI: Add support for continuing to a different location in dialplan.
[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/bridging.h"
35 #include "asterisk/bridging_features.h"
36 #include "asterisk/pbx.h"
37
38 struct stasis_app_control {
39         /*! Queue of commands to dispatch on the channel */
40         struct ao2_container *command_queue;
41         /*!
42          * When set, /c app_stasis should exit and continue in the dialplan.
43          */
44         int is_done:1;
45         /*!
46          * The associated channel.
47          * Be very careful with the threading associated w/ manipulating
48          * the channel.
49          */
50         struct ast_channel *channel;
51 };
52
53 struct stasis_app_control *control_create(struct ast_channel *channel)
54 {
55         struct stasis_app_control *control;
56
57         control = ao2_alloc(sizeof(*control), NULL);
58         if (!control) {
59                 return NULL;
60         }
61
62         control->command_queue = ao2_container_alloc_list(
63                 AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
64
65         control->channel = channel;
66
67         return control;
68 }
69
70 static struct stasis_app_command *exec_command(
71         struct stasis_app_control *control, stasis_app_command_cb command_fn,
72         void *data)
73 {
74         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
75
76         command = command_create(command_fn, data);
77
78         if (!command) {
79                 return NULL;
80         }
81
82         /* command_queue is a thread safe list; no lock needed */
83         ao2_link(control->command_queue, command);
84
85         ao2_ref(command, +1);
86         return command;
87 }
88
89 int control_is_done(struct stasis_app_control *control)
90 {
91         /* Called from stasis_app_exec thread; no lock needed */
92         return control->is_done;
93 }
94
95 struct stasis_app_control_continue_data {
96         char context[AST_MAX_CONTEXT];
97         char extension[AST_MAX_EXTENSION];
98         int priority;
99 };
100
101 static void *app_control_continue(struct stasis_app_control *control,
102         struct ast_channel *chan, void *data)
103 {
104         RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free);
105
106         /* Called from stasis_app_exec thread; no lock needed */
107         ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority);
108
109         control->is_done = 1;
110
111         return NULL;
112 }
113
114 int stasis_app_control_continue(struct stasis_app_control *control, const char *context, const char *extension, int priority)
115 {
116         struct stasis_app_control_continue_data *continue_data;
117
118         if (!(continue_data = ast_calloc(1, sizeof(*continue_data)))) {
119                 return -1;
120         }
121         ast_copy_string(continue_data->context, S_OR(context, ""), sizeof(continue_data->context));
122         ast_copy_string(continue_data->extension, S_OR(extension, ""), sizeof(continue_data->extension));
123         if (priority > 0) {
124                 continue_data->priority = priority;
125         } else {
126                 continue_data->priority = -1;
127         }
128
129         stasis_app_send_command_async(control, app_control_continue, continue_data);
130
131         return 0;
132 }
133
134 struct ast_channel_snapshot *stasis_app_control_get_snapshot(
135         const struct stasis_app_control *control)
136 {
137         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
138         struct stasis_caching_topic *caching_topic;
139         struct ast_channel_snapshot *snapshot;
140
141         caching_topic = ast_channel_topic_all_cached();
142         ast_assert(caching_topic != NULL);
143
144         msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
145                 stasis_app_control_get_channel_id(control));
146         if (!msg) {
147                 return NULL;
148         }
149
150         snapshot = stasis_message_data(msg);
151         ast_assert(snapshot != NULL);
152
153         ao2_ref(snapshot, +1);
154         return snapshot;
155 }
156
157 void *stasis_app_send_command(struct stasis_app_control *control,
158         stasis_app_command_cb command_fn, void *data)
159 {
160         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
161
162         if (control == NULL) {
163                 return NULL;
164         }
165
166         command = exec_command(control, command_fn, data);
167         if (!command) {
168                 return NULL;
169         }
170
171         return command_join(command);
172 }
173
174 int stasis_app_send_command_async(struct stasis_app_control *control,
175         stasis_app_command_cb command_fn, void *data)
176 {
177         RAII_VAR(struct stasis_app_command *, command, NULL, ao2_cleanup);
178
179         if (control == NULL) {
180                 return -1;
181         }
182
183         command = exec_command(control, command_fn, data);
184         if (!command) {
185                 return -1;
186         }
187
188         return 0;
189 }
190
191 const char *stasis_app_control_get_channel_id(
192         const struct stasis_app_control *control)
193 {
194         return ast_channel_uniqueid(control->channel);
195 }
196
197 void stasis_app_control_publish(
198         struct stasis_app_control *control, struct stasis_message *message)
199 {
200         if (!control || !control->channel || !message) {
201                 return;
202         }
203         stasis_publish(ast_channel_topic(control->channel), message);
204 }
205
206 int stasis_app_control_queue_control(struct stasis_app_control *control,
207         enum ast_control_frame_type frame_type)
208 {
209         return ast_queue_control(control->channel, frame_type);
210 }
211
212 int control_dispatch_all(struct stasis_app_control *control,
213         struct ast_channel *chan)
214 {
215         int count = 0;
216         struct ao2_iterator i;
217         void *obj;
218
219         ast_assert(control->channel == chan);
220
221         i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
222
223         while ((obj = ao2_iterator_next(&i))) {
224                 RAII_VAR(struct stasis_app_command *, command, obj, ao2_cleanup);
225                 command_invoke(command, control, chan);
226                 ++count;
227         }
228
229         ao2_iterator_destroy(&i);
230         return count;
231 }
232
233 /* Must be defined here since it must operate on the channel outside of the queue */
234 int stasis_app_control_remove_channel_from_bridge(
235         struct stasis_app_control *control, struct ast_bridge *bridge)
236 {
237         return ast_bridge_remove(bridge, control->channel);
238 }