Remove some unnecessary parentheses.
[asterisk/asterisk.git] / apps / app_bridgewait.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2013, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Author: Jonathan Rose <jrose@digium.com>
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*! \file
22  *
23  * \brief Application to place the channel into a holding Bridge
24  *
25  * \author Jonathan Rose <jrose@digium.com>
26  *
27  * \ingroup applications
28  */
29
30 /*** MODULEINFO
31         <depend>bridge_holding</depend>
32         <support_level>core</support_level>
33  ***/
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/file.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/features.h"
44 #include "asterisk/say.h"
45 #include "asterisk/lock.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/app.h"
48 #include "asterisk/bridge.h"
49 #include "asterisk/musiconhold.h"
50
51 /*** DOCUMENTATION
52         <application name="BridgeWait" language="en_US">
53                 <synopsis>
54                         Put a call into the holding bridge.
55                 </synopsis>
56                 <syntax>
57                         <parameter name="role" required="false">
58                                 <para>Defines the channel's purpose for entering the holding bridge. Values are case sensitive.
59                                 </para>
60                                 <enumlist>
61                                         <enum name="participant">
62                                                 <para>The channel will enter the holding bridge to be placed on hold
63                                                 until it is removed from the bridge for some reason. (default)</para>
64                                         </enum>
65                                         <enum name="announcer">
66                                                 <para>The channel will enter the holding bridge to make announcements
67                                                 to channels that are currently in the holding bridge. While an
68                                                 announcer is present, holding for the participants will be
69                                                 suspended.</para>
70                                         </enum>
71                                 </enumlist>
72                         </parameter>
73                         <parameter name="options">
74                                 <optionlist>
75                                         <option name="m">
76                                                 <argument name="class" required="true" />
77                                                 <para>The specified MOH class will be used/suggested for
78                                                 music on hold operations. This option will only be useful for
79                                                 entertainment modes that use it (m and h).</para>
80                                         </option>
81                                         <option name="e">
82                                                 <para>Which entertainment mechanism should be used while on hold
83                                                 in the holding bridge. Only the first letter is read.</para>
84                                                 <enumlist>
85                                                         <enum name="m"><para>Play music on hold (default)</para></enum>
86                                                         <enum name="r"><para>Ring without pause</para></enum>
87                                                         <enum name="s"><para>Generate silent audio</para></enum>
88                                                         <enum name="h"><para>Put the channel on hold</para></enum>
89                                                         <enum name="n"><para>No entertainment</para></enum>
90                                                 </enumlist>
91                                         </option>
92                                         <option name="S">
93                                                 <argument name="duration" required="true" />
94                                                 <para>Automatically exit the bridge and return to the PBX after
95                                                 <emphasis>duration</emphasis> seconds.</para>
96                                         </option>
97                                 </optionlist>
98                         </parameter>
99                 </syntax>
100                 <description>
101                         <para>This application places the incoming channel into a holding bridge.
102                         The channel will then wait in the holding bridge until some
103                         event occurs which removes it from the holding bridge.</para>
104                 </description>
105         </application>
106  ***/
107 /* BUGBUG Add bridge name/id parameter to specify which holding bridge to join (required) */
108 /* BUGBUG The channel may or may not be answered with the r option. */
109 /* BUGBUG You should not place an announcer into a holding bridge with unanswered channels. */
110
111 static char *app = "BridgeWait";
112 static struct ast_bridge *holding_bridge;
113
114 AST_MUTEX_DEFINE_STATIC(bridgewait_lock);
115
116 enum bridgewait_flags {
117         MUXFLAG_MOHCLASS = (1 << 0),
118         MUXFLAG_ENTERTAINMENT = (1 << 1),
119         MUXFLAG_TIMEOUT = (1 << 2),
120 };
121
122 enum bridgewait_args {
123         OPT_ARG_ENTERTAINMENT,
124         OPT_ARG_MOHCLASS,
125         OPT_ARG_TIMEOUT,
126         OPT_ARG_ARRAY_SIZE, /* Always the last element of the enum */
127 };
128
129 AST_APP_OPTIONS(bridgewait_opts, {
130         AST_APP_OPTION_ARG('e', MUXFLAG_ENTERTAINMENT, OPT_ARG_ENTERTAINMENT),
131         AST_APP_OPTION_ARG('m', MUXFLAG_MOHCLASS, OPT_ARG_MOHCLASS),
132         AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
133 });
134
135 static int apply_option_timeout(struct ast_bridge_features *features, char *duration_arg)
136 {
137         struct ast_bridge_features_limits hold_limits;
138
139         if (ast_strlen_zero(duration_arg)) {
140                 ast_log(LOG_ERROR, "No duration value provided for the timeout ('S') option.\n");
141                 return -1;
142         }
143
144         if (ast_bridge_features_limits_construct(&hold_limits)) {
145                 ast_log(LOG_ERROR, "Could not construct duration limits. Bridge canceled.\n");
146                 return -1;
147         }
148
149         if (sscanf(duration_arg, "%u", &hold_limits.duration) != 1
150                 || hold_limits.duration == 0) {
151                 ast_log(LOG_ERROR, "Duration value provided for the timeout ('S') option must be greater than 0\n");
152                 ast_bridge_features_limits_destroy(&hold_limits);
153                 return -1;
154         }
155
156         /* Limits struct holds time as milliseconds, so muliply 1000x */
157         hold_limits.duration *= 1000;
158         ast_bridge_features_set_limits(features, &hold_limits, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
159         ast_bridge_features_limits_destroy(&hold_limits);
160
161         return 0;
162 }
163
164 static int apply_option_moh(struct ast_channel *chan, const char *class_arg)
165 {
166         return ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", class_arg);
167 }
168
169 static int apply_option_entertainment(struct ast_channel *chan, const char *entertainment_arg)
170 {
171         char entertainment = entertainment_arg[0];
172         switch (entertainment) {
173         case 'm':
174                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
175         case 'r':
176                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing");
177         case 's':
178                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "silence");
179         case 'h':
180                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "hold");
181         case 'n':
182                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "none");
183         default:
184                 ast_log(LOG_ERROR, "Invalid argument for BridgeWait entertainment '%s'\n", entertainment_arg);
185                 return -1;
186         }
187 }
188
189 enum wait_bridge_roles {
190         ROLE_PARTICIPANT = 0,
191         ROLE_ANNOUNCER,
192         ROLE_INVALID,
193 };
194
195 static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features, enum wait_bridge_roles role)
196 {
197         if (ast_test_flag(flags, MUXFLAG_TIMEOUT)) {
198                 if (apply_option_timeout(features, opts[OPT_ARG_TIMEOUT])) {
199                         return -1;
200                 }
201         }
202
203         switch (role) {
204         case ROLE_PARTICIPANT:
205                 if (ast_channel_add_bridge_role(chan, "holding_participant")) {
206                         return -1;
207                 }
208
209                 if (ast_test_flag(flags, MUXFLAG_MOHCLASS)) {
210                         if (apply_option_moh(chan, opts[OPT_ARG_MOHCLASS])) {
211                                 return -1;
212                         }
213                 }
214
215                 if (ast_test_flag(flags, MUXFLAG_ENTERTAINMENT)) {
216                         if (apply_option_entertainment(chan, opts[OPT_ARG_ENTERTAINMENT])) {
217                                 return -1;
218                         }
219                 }
220
221                 break;
222         case ROLE_ANNOUNCER:
223                 if (ast_channel_add_bridge_role(chan, "announcer")) {
224                         return -1;
225                 }
226                 break;
227         case ROLE_INVALID:
228                 ast_assert(0);
229                 return -1;
230         }
231
232         return 0;
233 }
234
235 static enum wait_bridge_roles validate_role(const char *role)
236 {
237         if (!strcmp(role, "participant")) {
238                 return ROLE_PARTICIPANT;
239         } else if (!strcmp(role, "announcer")) {
240                 return ROLE_ANNOUNCER;
241         } else {
242                 return ROLE_INVALID;
243         }
244 }
245
246 static int bridgewait_exec(struct ast_channel *chan, const char *data)
247 {
248         struct ast_bridge_features chan_features;
249         struct ast_flags flags = { 0 };
250         char *parse;
251         enum wait_bridge_roles role = ROLE_PARTICIPANT;
252         char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
253
254         AST_DECLARE_APP_ARGS(args,
255                 AST_APP_ARG(role);
256                 AST_APP_ARG(options);
257                 AST_APP_ARG(other);             /* Any remaining unused arguments */
258         );
259
260         ast_mutex_lock(&bridgewait_lock);
261         if (!holding_bridge) {
262                 holding_bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING,
263                         AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
264                                 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
265         }
266         ast_mutex_unlock(&bridgewait_lock);
267         if (!holding_bridge) {
268                 ast_log(LOG_ERROR, "Could not create holding bridge for '%s'.\n", ast_channel_name(chan));
269                 return -1;
270         }
271
272         parse = ast_strdupa(data);
273         AST_STANDARD_APP_ARGS(args, parse);
274
275         if (!ast_strlen_zero(args.role)) {
276                 role = validate_role(args.role);
277                 if (role == ROLE_INVALID) {
278                         ast_log(LOG_ERROR, "Requested waiting bridge role '%s' is invalid.\n", args.role);
279                         return -1;
280                 }
281         }
282
283         if (ast_bridge_features_init(&chan_features)) {
284                 ast_bridge_features_cleanup(&chan_features);
285                 return -1;
286         }
287
288         if (args.options) {
289                 ast_app_parse_options(bridgewait_opts, &flags, opts, args.options);
290         }
291
292         if (process_options(chan, &flags, opts, &chan_features, role)) {
293                 ast_bridge_features_cleanup(&chan_features);
294                 return -1;
295         }
296
297         ast_bridge_join(holding_bridge, chan, NULL, &chan_features, NULL, 0);
298
299         ast_bridge_features_cleanup(&chan_features);
300         return ast_check_hangup_locked(chan) ? -1 : 0;
301 }
302
303 static int unload_module(void)
304 {
305         ao2_cleanup(holding_bridge);
306         holding_bridge = NULL;
307
308         return ast_unregister_application(app);
309 }
310
311 static int load_module(void)
312 {
313         return ast_register_application_xml(app, bridgewait_exec);
314 }
315
316 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Place the channel into a holding bridge application");