53165f545c5b9ef8e7943b3c2569f7b28fd79c71
[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="options">
58                                 <optionlist>
59                                         <option name="A">
60                                                 <para>The channel will join the holding bridge as an
61                                                 announcer</para>
62                                         </option>
63                                         <option name="m">
64                                                 <argument name="class" required="false" />
65                                                 <para>Play music on hold to the entering channel while it is
66                                                 on hold. If the <emphasis>class</emphasis> is included, then
67                                                 that class of music on hold will take priority over the
68                                                 channel default.</para>
69                                         </option>
70                                         <option name="r">
71                                                 <para>Play a ringing tone to the entering channel while it is
72                                                 on hold.</para>
73                                         </option>
74                                         <option name="S">
75                                                 <argument name="duration" required="true" />
76                                                 <para>Automatically end the hold and return to the PBX after
77                                                 <emphasis>duration</emphasis> seconds.</para>
78                                         </option>
79                                 </optionlist>
80                         </parameter>
81                 </syntax>
82                 <description>
83                         <para>This application places the incoming channel into a holding bridge.
84                         The channel will then wait in the holding bridge until some
85                         event occurs which removes it from the holding bridge.</para>
86                 </description>
87         </application>
88  ***/
89 /* BUGBUG Add bridge name/id parameter to specify which holding bridge to join (required) */
90 /* BUGBUG Add h(moh-class) option to put channel on hold using AST_CONTROL_HOLD/AST_CONTROL_UNHOLD while in bridge */
91 /* BUGBUG Add s option to send silence media frames to channel while in bridge (uses a silence generator) */
92 /* BUGBUG Add n option to send no media to channel while in bridge (Channel should not be answered yet) */
93 /* BUGBUG The channel may or may not be answered with the r option. */
94 /* BUGBUG You should not place an announcer into a holding bridge with unanswered channels. */
95 /* BUGBUG Not supplying any option flags will assume the m option with the default music class. */
96
97 static char *app = "BridgeWait";
98 static struct ast_bridge *holding_bridge;
99
100 AST_MUTEX_DEFINE_STATIC(bridgewait_lock);
101
102 enum bridgewait_flags {
103         MUXFLAG_PLAYMOH = (1 << 0),
104         MUXFLAG_RINGING = (1 << 1),
105         MUXFLAG_TIMEOUT = (1 << 2),
106         MUXFLAG_ANNOUNCER = (1 << 3),
107 };
108
109 enum bridgewait_args {
110         OPT_ARG_MOHCLASS,
111         OPT_ARG_TIMEOUT,
112         OPT_ARG_ARRAY_SIZE, /* Always the last element of the enum */
113 };
114
115 AST_APP_OPTIONS(bridgewait_opts, {
116         AST_APP_OPTION('A', MUXFLAG_ANNOUNCER),
117         AST_APP_OPTION('r', MUXFLAG_RINGING),
118         AST_APP_OPTION_ARG('m', MUXFLAG_PLAYMOH, OPT_ARG_MOHCLASS),
119         AST_APP_OPTION_ARG('S', MUXFLAG_TIMEOUT, OPT_ARG_TIMEOUT),
120 });
121
122 static int apply_option_timeout(struct ast_bridge_features *features, char *duration_arg)
123 {
124         struct ast_bridge_features_limits hold_limits;
125
126         if (ast_strlen_zero(duration_arg)) {
127                 ast_log(LOG_ERROR, "No duration value provided for the timeout ('S') option.\n");
128                 return -1;
129         }
130
131         if (ast_bridge_features_limits_construct(&hold_limits)) {
132                 ast_log(LOG_ERROR, "Could not construct duration limits. Bridge canceled.\n");
133                 return -1;
134         }
135
136         if (sscanf(duration_arg, "%u", &(hold_limits.duration)) != 1 || hold_limits.duration == 0) {
137                 ast_log(LOG_ERROR, "Duration value provided for the timeout ('S') option must be greater than 0\n");
138                 ast_bridge_features_limits_destroy(&hold_limits);
139                 return -1;
140         }
141
142         /* Limits struct holds time as milliseconds, so muliply 1000x */
143         hold_limits.duration *= 1000;
144         ast_bridge_features_set_limits(features, &hold_limits, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
145         ast_bridge_features_limits_destroy(&hold_limits);
146
147         return 0;
148 }
149
150 static void apply_option_moh(struct ast_channel *chan, char *class_arg)
151 {
152         ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "musiconhold");
153         ast_channel_set_bridge_role_option(chan, "holding_participant", "moh_class", class_arg);
154 }
155
156 static void apply_option_ringing(struct ast_channel *chan)
157 {
158         ast_channel_set_bridge_role_option(chan, "holding_participant", "idle_mode", "ringing");
159 }
160
161 static int process_options(struct ast_channel *chan, struct ast_flags *flags, char **opts, struct ast_bridge_features *features)
162 {
163         if (ast_test_flag(flags, MUXFLAG_TIMEOUT)) {
164                 if (apply_option_timeout(features, opts[OPT_ARG_TIMEOUT])) {
165                         return -1;
166                 }
167         }
168
169         if (ast_test_flag(flags, MUXFLAG_ANNOUNCER)) {
170                 /* Announcer specific stuff */
171                 ast_channel_add_bridge_role(chan, "announcer");
172         } else {
173                 /* Non Announcer specific stuff */
174                 ast_channel_add_bridge_role(chan, "holding_participant");
175
176                 if (ast_test_flag(flags, MUXFLAG_PLAYMOH)) {
177                         apply_option_moh(chan, opts[OPT_ARG_MOHCLASS]);
178                 } else if (ast_test_flag(flags, MUXFLAG_RINGING)) {
179                         apply_option_ringing(chan);
180                 }
181         }
182
183         return 0;
184 }
185
186 static int bridgewait_exec(struct ast_channel *chan, const char *data)
187 {
188         struct ast_bridge_features chan_features;
189         struct ast_flags flags = { 0 };
190         char *parse;
191
192         AST_DECLARE_APP_ARGS(args,
193                 AST_APP_ARG(options);
194                 AST_APP_ARG(other);             /* Any remaining unused arguments */
195         );
196
197         ast_mutex_lock(&bridgewait_lock);
198         if (!holding_bridge) {
199                 holding_bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING,
200                         AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
201                                 | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
202         }
203         ast_mutex_unlock(&bridgewait_lock);
204         if (!holding_bridge) {
205                 ast_log(LOG_ERROR, "Could not create holding bridge for '%s'.\n", ast_channel_name(chan));
206                 return -1;
207         }
208
209         parse = ast_strdupa(data);
210         AST_STANDARD_APP_ARGS(args, parse);
211
212         if (ast_bridge_features_init(&chan_features)) {
213                 ast_bridge_features_cleanup(&chan_features);
214                 return -1;
215         }
216
217         if (args.options) {
218                 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
219                 ast_app_parse_options(bridgewait_opts, &flags, opts, args.options);
220                 if (process_options(chan, &flags, opts, &chan_features)) {
221                         ast_bridge_features_cleanup(&chan_features);
222                         return -1;
223                 }
224         }
225
226         ast_bridge_join(holding_bridge, chan, NULL, &chan_features, NULL, 0);
227
228         ast_bridge_features_cleanup(&chan_features);
229         return ast_check_hangup_locked(chan) ? -1 : 0;
230 }
231
232 static int unload_module(void)
233 {
234         ao2_cleanup(holding_bridge);
235         holding_bridge = NULL;
236
237         return ast_unregister_application(app);
238 }
239
240 static int load_module(void)
241 {
242         return ast_register_application_xml(app, bridgewait_exec);
243 }
244
245 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Place the channel into a holding bridge application");