Asterisk - The Open Source Telephony Project  21.4.1
app_page.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (c) 2004 - 2006 Digium, Inc. All rights reserved.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * This code is released under the GNU General Public License
9  * version 2.0. See LICENSE for more information.
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  */
18 
19 /*! \file
20  *
21  * \brief page() - Paging application
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \ingroup applications
26  */
27 
28 /*** MODULEINFO
29  <depend>app_confbridge</depend>
30  <support_level>core</support_level>
31  ***/
32 
33 #include "asterisk.h"
34 
35 #include "asterisk/channel.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/module.h"
38 #include "asterisk/file.h"
39 #include "asterisk/app.h"
40 #include "asterisk/chanvars.h"
41 #include "asterisk/utils.h"
42 #include "asterisk/devicestate.h"
43 #include "asterisk/dial.h"
44 
45 /*** DOCUMENTATION
46  <application name="Page" language="en_US">
47  <synopsis>
48  Page series of phones
49  </synopsis>
50  <syntax>
51  <parameter name="Technology/Resource" required="false" argsep="&amp;">
52  <argument name="Technology/Resource" required="true">
53  <para>Specification of the device(s) to dial. These must be in the format of
54  <literal>Technology/Resource</literal>, where <replaceable>Technology</replaceable>
55  represents a particular channel driver, and <replaceable>Resource</replaceable> represents a resource
56  available to that particular channel driver.</para>
57  </argument>
58  <argument name="Technology2/Resource2" multiple="true">
59  <para>Optional extra devices to dial in parallel</para>
60  <para>If you need more than one, enter them as Technology2/Resource2&amp;
61  Technology3/Resource3&amp;.....</para>
62  </argument>
63  </parameter>
64  <parameter name="options">
65  <optionlist>
66  <option name="b" argsep="^">
67  <para>Before initiating an outgoing call, Gosub to the specified
68  location using the newly created channel. The Gosub will be
69  executed for each destination channel.</para>
70  <argument name="context" required="false" />
71  <argument name="exten" required="false" />
72  <argument name="priority" required="true" hasparams="optional" argsep="^">
73  <argument name="arg1" multiple="true" required="true" />
74  <argument name="argN" />
75  </argument>
76  </option>
77  <option name="B" argsep="^">
78  <para>Before initiating the outgoing call(s), Gosub to the specified
79  location using the current channel.</para>
80  <argument name="context" required="false" />
81  <argument name="exten" required="false" />
82  <argument name="priority" required="true" hasparams="optional" argsep="^">
83  <argument name="arg1" multiple="true" required="true" />
84  <argument name="argN" />
85  </argument>
86  </option>
87  <option name="d">
88  <para>Full duplex audio</para>
89  </option>
90  <option name="i">
91  <para>Ignore attempts to forward the call</para>
92  </option>
93  <option name="q">
94  <para>Quiet, do not play beep to caller</para>
95  </option>
96  <option name="r">
97  <para>Record the page into a file (<literal>CONFBRIDGE(bridge,record_conference)</literal>)</para>
98  </option>
99  <option name="s">
100  <para>Only dial a channel if its device state says that it is <literal>NOT_INUSE</literal></para>
101  </option>
102  <option name="A">
103  <argument name="x" required="true">
104  <para>The announcement to playback to all devices</para>
105  </argument>
106  <para>Play an announcement to all paged participants</para>
107  </option>
108  <option name="n">
109  <para>Do not play announcement to caller (alters <literal>A(x)</literal> behavior)</para>
110  </option>
111  </optionlist>
112  </parameter>
113  <parameter name="timeout">
114  <para>Specify the length of time that the system will attempt to connect a call.
115  After this duration, any page calls that have not been answered will be hung up by the
116  system.</para>
117  </parameter>
118  </syntax>
119  <description>
120  <para>Places outbound calls to the given <replaceable>technology</replaceable>/<replaceable>resource</replaceable>
121  and dumps them into a conference bridge as muted participants. The original
122  caller is dumped into the conference as a speaker and the room is
123  destroyed when the original caller leaves.</para>
124  </description>
125  <see-also>
126  <ref type="application">ConfBridge</ref>
127  </see-also>
128  </application>
129  ***/
130 static const char * const app_page= "Page";
131 
132 enum page_opt_flags {
133  PAGE_DUPLEX = (1 << 0),
134  PAGE_QUIET = (1 << 1),
135  PAGE_RECORD = (1 << 2),
136  PAGE_SKIP = (1 << 3),
137  PAGE_IGNORE_FORWARDS = (1 << 4),
138  PAGE_ANNOUNCE = (1 << 5),
139  PAGE_NOCALLERANNOUNCE = (1 << 6),
140  PAGE_PREDIAL_CALLEE = (1 << 7),
141  PAGE_PREDIAL_CALLER = (1 << 8),
142 };
143 
144 enum {
145  OPT_ARG_ANNOUNCE = 0,
146  OPT_ARG_PREDIAL_CALLEE = 1,
147  OPT_ARG_PREDIAL_CALLER = 2,
148  OPT_ARG_ARRAY_SIZE = 3,
149 };
150 
151 AST_APP_OPTIONS(page_opts, {
152  AST_APP_OPTION_ARG('b', PAGE_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLEE),
153  AST_APP_OPTION_ARG('B', PAGE_PREDIAL_CALLER, OPT_ARG_PREDIAL_CALLER),
154  AST_APP_OPTION('d', PAGE_DUPLEX),
155  AST_APP_OPTION('q', PAGE_QUIET),
156  AST_APP_OPTION('r', PAGE_RECORD),
157  AST_APP_OPTION('s', PAGE_SKIP),
158  AST_APP_OPTION('i', PAGE_IGNORE_FORWARDS),
159  AST_APP_OPTION_ARG('A', PAGE_ANNOUNCE, OPT_ARG_ANNOUNCE),
160  AST_APP_OPTION('n', PAGE_NOCALLERANNOUNCE),
161 });
162 
163 #define PAGE_BEEP "beep"
164 
165 /* We use this structure as a way to pass this to all dialed channels */
166 struct page_options {
167  char *opts[OPT_ARG_ARRAY_SIZE];
168  struct ast_flags flags;
169 };
170 
171 /*!
172  * \internal
173  * \brief Setup the page bridge profile.
174  *
175  * \param chan Setup bridge profile on this channel.
176  * \param options Options to setup bridge profile.
177  */
178 static void setup_profile_bridge(struct ast_channel *chan, struct page_options *options)
179 {
180  /* Use default_bridge as a starting point */
181  ast_func_write(chan, "CONFBRIDGE(bridge,template)", "");
182  if (ast_test_flag(&options->flags, PAGE_RECORD)) {
183  ast_func_write(chan, "CONFBRIDGE(bridge,record_conference)", "yes");
184  }
185 }
186 
187 /*!
188  * \internal
189  * \brief Setup the paged user profile.
190  *
191  * \param chan Setup user profile on this channel.
192  * \param options Options to setup paged user profile.
193  */
194 static void setup_profile_paged(struct ast_channel *chan, struct page_options *options)
195 {
196  /* Use default_user as a starting point */
197  ast_func_write(chan, "CONFBRIDGE(user,template)", "");
198  ast_func_write(chan, "CONFBRIDGE(user,quiet)", "yes");
199  ast_func_write(chan, "CONFBRIDGE(user,end_marked)", "yes");
200  if (!ast_test_flag(&options->flags, PAGE_DUPLEX)) {
201  ast_func_write(chan, "CONFBRIDGE(user,startmuted)", "yes");
202  }
203  if (ast_test_flag(&options->flags, PAGE_ANNOUNCE)
204  && !ast_strlen_zero(options->opts[OPT_ARG_ANNOUNCE])) {
205  ast_func_write(chan, "CONFBRIDGE(user,announcement)", options->opts[OPT_ARG_ANNOUNCE]);
206  }
207 }
208 
209 /*!
210  * \internal
211  * \brief Setup the caller user profile.
212  *
213  * \param chan Setup user profile on this channel.
214  * \param options Options to setup caller user profile.
215  */
216 static void setup_profile_caller(struct ast_channel *chan, struct page_options *options)
217 {
218  /* Use default_user as a starting point if not already setup. */
219  ast_func_write(chan, "CONFBRIDGE(user,template)", "");
220  ast_func_write(chan, "CONFBRIDGE(user,quiet)", "yes");
221  ast_func_write(chan, "CONFBRIDGE(user,marked)", "yes");
222  if (!ast_test_flag(&options->flags, PAGE_NOCALLERANNOUNCE)
223  && ast_test_flag(&options->flags, PAGE_ANNOUNCE)
224  && !ast_strlen_zero(options->opts[OPT_ARG_ANNOUNCE])) {
225  ast_func_write(chan, "CONFBRIDGE(user,announcement)", options->opts[OPT_ARG_ANNOUNCE]);
226  }
227 }
228 
229 static void page_state_callback(struct ast_dial *dial)
230 {
231  struct ast_channel *chan;
232  struct page_options *options;
233 
235  !(chan = ast_dial_answered(dial)) ||
236  !(options = ast_dial_get_user_data(dial))) {
237  return;
238  }
239 
240  setup_profile_bridge(chan, options);
241  setup_profile_paged(chan, options);
242 }
243 
244 static int page_exec(struct ast_channel *chan, const char *data)
245 {
246  char *tech;
247  char *resource;
248  char *tmp;
249  char *predial_callee = NULL;
250  char confbridgeopts[128];
251  char originator[AST_CHANNEL_NAME];
252  struct page_options options = { { 0, }, { 0, } };
253  unsigned int confid = ast_random();
254  struct ast_app *app;
255  int res = 0;
256  int pos = 0;
257  int i = 0;
258  struct ast_dial **dial_list;
259  unsigned int num_dials;
260  int timeout = 0;
261  char *parse;
262 
265  AST_APP_ARG(options);
266  AST_APP_ARG(timeout);
267  );
268 
269  if (!(app = pbx_findapp("ConfBridge"))) {
270  ast_log(LOG_WARNING, "There is no ConfBridge application available!\n");
271  return -1;
272  };
273 
274  parse = ast_strdupa(data ?: "");
275 
276  AST_STANDARD_APP_ARGS(args, parse);
277 
278  ast_copy_string(originator, ast_channel_name(chan), sizeof(originator));
279  if ((tmp = strchr(originator, '-'))) {
280  *tmp = '\0';
281  }
282 
283  if (!ast_strlen_zero(args.options)) {
284  ast_app_parse_options(page_opts, &options.flags, options.opts, args.options);
285  }
286 
287  if (!ast_strlen_zero(args.timeout)) {
288  timeout = atoi(args.timeout);
289  }
290 
291  snprintf(confbridgeopts, sizeof(confbridgeopts), "ConfBridge,%u", confid);
292 
293  /* Count number of extensions in list by number of ampersands + 1 */
294  num_dials = 1;
295  tmp = args.devices ?: "";
296  while (*tmp) {
297  if (*tmp == '&') {
298  num_dials++;
299  }
300  tmp++;
301  }
302 
303  if (!(dial_list = ast_calloc(num_dials, sizeof(struct ast_dial *)))) {
304  ast_log(LOG_ERROR, "Can't allocate %ld bytes for dial list\n", (long)(sizeof(struct ast_dial *) * num_dials));
305  return -1;
306  }
307 
308  /* PREDIAL: Preprocess any callee gosub arguments. */
309  if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLEE)
310  && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLEE])) {
311  ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLEE]);
312  predial_callee =
313  (char *) ast_app_expand_sub_args(chan, options.opts[OPT_ARG_PREDIAL_CALLEE]);
314  }
315 
316  /* PREDIAL: Run gosub on the caller's channel */
317  if (ast_test_flag(&options.flags, PAGE_PREDIAL_CALLER)
318  && !ast_strlen_zero(options.opts[OPT_ARG_PREDIAL_CALLER])) {
319  ast_replace_subargument_delimiter(options.opts[OPT_ARG_PREDIAL_CALLER]);
320  ast_app_exec_sub(NULL, chan, options.opts[OPT_ARG_PREDIAL_CALLER], 0);
321  }
322 
323  /* Go through parsing/calling each device */
324  while ((tech = strsep(&args.devices, "&"))) {
325  int state = 0;
326  struct ast_dial *dial = NULL;
327 
328  tech = ast_strip(tech);
329  if (ast_strlen_zero(tech)) {
330  /* No tech/resource in this position. */
331  continue;
332  }
333 
334  /* don't call the originating device */
335  if (!strcasecmp(tech, originator)) {
336  continue;
337  }
338 
339  /* If no resource is available, continue on */
340  if (!(resource = strchr(tech, '/'))) {
341  ast_log(LOG_WARNING, "Incomplete destination: '%s' supplied.\n", tech);
342  continue;
343  }
344 
345  /* Ensure device is not in use if skip option is enabled */
346  if (ast_test_flag(&options.flags, PAGE_SKIP)) {
347  state = ast_device_state(tech);
348  if (state == AST_DEVICE_UNKNOWN) {
349  ast_verb(3, "Destination '%s' has device state '%s'. Paging anyway.\n",
350  tech, ast_devstate2str(state));
351  } else if (state != AST_DEVICE_NOT_INUSE) {
352  ast_verb(3, "Destination '%s' has device state '%s'.\n",
353  tech, ast_devstate2str(state));
354  continue;
355  }
356  }
357 
358  *resource++ = '\0';
359 
360  /* Create a dialing structure */
361  if (!(dial = ast_dial_create())) {
362  ast_log(LOG_WARNING, "Failed to create dialing structure.\n");
363  continue;
364  }
365 
366  /* Append technology and resource */
367  if (ast_dial_append(dial, tech, resource, NULL) == -1) {
368  ast_log(LOG_ERROR, "Failed to add %s/%s to outbound dial\n", tech, resource);
369  ast_dial_destroy(dial);
370  continue;
371  }
372 
373  /* Set ANSWER_EXEC as global option */
375 
376  if (predial_callee) {
378  }
379 
380  if (timeout) {
381  ast_dial_set_global_timeout(dial, timeout * 1000);
382  }
383 
384  if (ast_test_flag(&options.flags, PAGE_IGNORE_FORWARDS)) {
386  }
387 
388  ast_dial_set_state_callback(dial, &page_state_callback);
389  ast_dial_set_user_data(dial, &options);
390 
391  /* Run this dial in async mode */
392  ast_dial_run(dial, chan, 1);
393 
394  /* Put in our dialing array */
395  dial_list[pos++] = dial;
396  }
397 
398  ast_free(predial_callee);
399 
400  if (!ast_test_flag(&options.flags, PAGE_QUIET)) {
401  if (!ast_fileexists(PAGE_BEEP, NULL, NULL)) {
402  ast_log(LOG_WARNING, "Missing required sound file: '" PAGE_BEEP "'\n");
403  } else {
404  res = ast_streamfile(chan, PAGE_BEEP, ast_channel_language(chan));
405  if (!res) {
406  res = ast_waitstream(chan, "");
407  }
408  }
409  }
410 
411  if (!res) {
412  setup_profile_bridge(chan, &options);
413  setup_profile_caller(chan, &options);
414 
415  snprintf(confbridgeopts, sizeof(confbridgeopts), "%u", confid);
416  pbx_exec(chan, app, confbridgeopts);
417  }
418 
419  /* Go through each dial attempt cancelling, joining, and destroying */
420  for (i = 0; i < pos; i++) {
421  struct ast_dial *dial = dial_list[i];
422 
423  /* We have to wait for the async thread to exit as it's possible ConfBridge won't throw them out immediately */
424  ast_dial_join(dial);
425 
426  /* Hangup all channels */
427  ast_dial_hangup(dial);
428 
429  /* Destroy dialing structure */
430  ast_dial_destroy(dial);
431  }
432 
433  ast_free(dial_list);
434 
435  return -1;
436 }
437 
438 static int unload_module(void)
439 {
440  return ast_unregister_application(app_page);
441 }
442 
443 static int load_module(void)
444 {
445  return ast_register_application_xml(app_page, page_exec);
446 }
447 
448 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Page Multiple Phones",
449  .support_level = AST_MODULE_SUPPORT_CORE,
450  .load = load_module,
451  .unload = unload_module,
452  .requires = "app_confbridge",
453 );
const char * ast_devstate2str(enum ast_device_state devstate) attribute_pure
Convert device state to text string for output.
Definition: devicestate.c:237
Main Channel structure associated with a channel.
ast_device_state
Device States.
Definition: devicestate.h:52
int ast_dial_destroy(struct ast_dial *dial)
Destroys a dialing structure.
Definition: dial.c:1091
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
int ast_dial_option_global_enable(struct ast_dial *dial, enum ast_dial_option option, void *data)
Enables an option globally.
Definition: dial.c:1145
Asterisk main include file. File version handling, generic pbx functions.
void * ast_dial_get_user_data(struct ast_dial *dial)
Return the user data on a dial structure.
Definition: dial.c:1279
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
Main dialing structure. Contains global options, channels being dialed, and more! ...
Definition: dial.c:48
void ast_dial_set_global_timeout(struct ast_dial *dial, int timeout)
Set the maximum time (globally) allowed for trying to ring phones.
Definition: dial.c:1284
const char * ast_app_expand_sub_args(struct ast_channel *chan, const char *args)
Add missing context/exten to subroutine argument string.
Definition: main/app.c:278
Device state management.
void ast_dial_hangup(struct ast_dial *dial)
Hangup channels.
Definition: dial.c:1074
Channel Variables.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Dialing API.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
Utility functions.
void ast_dial_set_user_data(struct ast_dial *dial, void *user_data)
Set user data on a dial structure.
Definition: dial.c:1274
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
void ast_dial_set_state_callback(struct ast_dial *dial, ast_dial_state_callback callback)
Set a callback for state changes.
Definition: dial.c:1269
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition: utils.c:2343
General Asterisk PBX channel definitions.
enum ast_dial_result ast_dial_join(struct ast_dial *dial)
Cancel async thread.
Definition: dial.c:1017
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
enum ast_dial_result ast_dial_state(struct ast_dial *dial)
Return state of dial.
Definition: dial.c:1008
Core PBX routines and definitions.
enum ast_dial_result ast_dial_run(struct ast_dial *dial, struct ast_channel *chan, int async)
Execute dialing synchronously or asynchronously.
Definition: dial.c:935
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
int ast_dial_append(struct ast_dial *dial, const char *tech, const char *device, const struct ast_assigned_ids *assignedids)
Append a channel.
Definition: dial.c:280
struct ast_channel * ast_dial_answered(struct ast_dial *dial)
Return channel that answered.
Definition: dial.c:977
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
struct ast_dial * ast_dial_create(void)
New dialing structure.
Definition: dial.c:223
#define AST_CHANNEL_NAME
Definition: channel.h:171
Structure used to handle boolean flags.
Definition: utils.h:199
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
ast_app: A registered application
Definition: pbx_app.c:45
int timeout
Definition: dial.c:50
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
Run a subroutine on a channel, placing an optional second channel into autoservice.
Definition: main/app.c:297
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_func_write(struct ast_channel *chan, const char *function, const char *value)
executes a write operation on a function
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
#define AST_APP_ARG(name)
Define an application argument.