bridge_holding/app_bridgewait: Add new entertainment options
[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/bridging.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 || hold_limits.duration == 0) {
150                 ast_log(LOG_ERROR, "Duration value provided for the timeout ('S') option must be greater than 0\n");
151                 ast_bridge_features_limits_destroy(&hold_limits);
152                 return -1;
153         }
154
155         /* Limits struct holds time as milliseconds, so muliply 1000x */
156         hold_limits.duration *= 1000;
157         ast_bridge_features_set_limits(features, &hold_limits, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
158         ast_bridge_features_limits_destroy(&hold_limits);
159
160         return 0;
161 }
162
163 static int apply_option_moh(struct ast_channel *chan, const char *class_arg)
164 {
165         return ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", class_arg);
166 }
167
168 static int apply_option_entertainment(struct ast_channel *chan, const char *entertainment_arg)
169 {
170         char entertainment = entertainment_arg[0];
171         switch (entertainment) {
172         case 'm':
173                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
174         case 'r':
175                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing");
176         case 's':
177                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "silence");
178         case 'h':
179                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "hold");
180         case 'n':
181                 return ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "none");
182         default:
183                 ast_log(LOG_ERROR, "Invalid argument for BridgeWait entertainment '%s'\n", entertainment_arg);
184                 return -1;
185         }
186 }
187
188 enum wait_bridge_roles {
189         ROLE_PARTICIPANT = 0,
190         ROLE_ANNOUNCER,
191         ROLE_INVALID,
192 };
193
194 static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features, enum wait_bridge_roles role)
195 {
196         if (ast_test_flag(flags, MUXFLAG_TIMEOUT)) {
197                 if (apply_option_timeout(features, opts[OPT_ARG_TIMEOUT])) {
198                         return -1;
199                 }
200         }
201
202         switch (role) {
203         case ROLE_PARTICIPANT:
204                 if (ast_channel_add_bridge_role(chan, "holding_participant")) {
205                         return -1;
206                 }
207
208                 if (ast_test_flag(flags, MUXFLAG_MOHCLASS)) {
209                         if (apply_option_moh(chan, opts[OPT_ARG_MOHCLASS])) {
210                                 return -1;
211                         }
212                 }
213
214                 if (ast_test_flag(flags, MUXFLAG_ENTERTAINMENT)) {
215                         if (apply_option_entertainment(chan, opts[OPT_ARG_ENTERTAINMENT])) {
216                                 return -1;
217                         }
218                 }
219
220                 break;
221         case ROLE_ANNOUNCER:
222                 if (ast_channel_add_bridge_role(chan, "announcer")) {
223                         return -1;
224                 }
225                 break;
226         case ROLE_INVALID:
227                 ast_assert(0);
228                 return -1;
229         }
230
231         return 0;
232 }
233
234 static enum wait_bridge_roles validate_role(const char *role)
235 {
236         if (!strcmp(role, "participant")) {
237                 return ROLE_PARTICIPANT;
238         } else if (!strcmp(role, "announcer")) {
239                 return ROLE_ANNOUNCER;
240         } else {
241                 return ROLE_INVALID;
242         }
243 }
244
245 static int bridgewait_exec(struct ast_channel *chan, const char *data)
246 {
247         struct ast_bridge_features chan_features;
248         struct ast_flags flags = { 0 };
249         char *parse;
250         enum wait_bridge_roles role = ROLE_PARTICIPANT;
251         char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
252
253         AST_DECLARE_APP_ARGS(args,
254                 AST_APP_ARG(role);
255                 AST_APP_ARG(options);
256                 AST_APP_ARG(other);             /* Any remaining unused arguments */
257         );
258
259         ast_mutex_lock(&bridgewait_lock);
260         if (!holding_bridge) {
261                 holding_bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING,
262                         AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
263                                 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
264         }
265         ast_mutex_unlock(&bridgewait_lock);
266         if (!holding_bridge) {
267                 ast_log(LOG_ERROR, "Could not create holding bridge for '%s'.\n", ast_channel_name(chan));
268                 return -1;
269         }
270
271         parse = ast_strdupa(data);
272         AST_STANDARD_APP_ARGS(args, parse);
273
274         if (!ast_strlen_zero(args.role)) {
275                 role = validate_role(args.role);
276                 if (role == ROLE_INVALID) {
277                         ast_log(LOG_ERROR, "Requested waiting bridge role '%s' is invalid.\n", args.role);
278                         return -1;
279                 }
280         }
281
282         if (ast_bridge_features_init(&chan_features)) {
283                 ast_bridge_features_cleanup(&chan_features);
284                 return -1;
285         }
286
287         if (args.options) {
288                 ast_app_parse_options(bridgewait_opts, &flags, opts, args.options);
289         }
290
291         if (process_options(chan, &flags, opts, &chan_features, role)) {
292                 ast_bridge_features_cleanup(&chan_features);
293                 return -1;
294         }
295
296         ast_bridge_join(holding_bridge, chan, NULL, &chan_features, NULL, 0);
297
298         ast_bridge_features_cleanup(&chan_features);
299         return ast_check_hangup_locked(chan) ? -1 : 0;
300 }
301
302 static int unload_module(void)
303 {
304         ao2_cleanup(holding_bridge);
305         holding_bridge = NULL;
306
307         return ast_unregister_application(app);
308 }
309
310 static int load_module(void)
311 {
312         return ast_register_application_xml(app, bridgewait_exec);
313 }
314
315 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Place the channel into a holding bridge application");