Files
asterisk/apps/app_page.c

347 lines
9.9 KiB
C
Raw Normal View History

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (c) 2004 - 2006 Digium, Inc. All rights reserved.
*
* Mark Spencer <markster@digium.com>
*
* This code is released under the GNU General Public License
* version 2.0. See LICENSE for more information.
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
*/
/*! \file
*
* \brief page() - Paging application
*
* \author Mark Spencer <markster@digium.com>
*
* \ingroup applications
*/
/*** MODULEINFO
<depend>app_confbridge</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/file.h"
#include "asterisk/app.h"
Merged revisions 7265-7266,7268-7275 via svnmerge from https://origsvn.digium.com/svn/asterisk/branches/1.2 ........ r7265 | oej | 2005-12-01 17:18:14 -0600 (Thu, 01 Dec 2005) | 2 lines Changing bug report address to the Asterisk issue tracker ........ r7266 | kpfleming | 2005-12-01 17:18:29 -0600 (Thu, 01 Dec 2005) | 3 lines Makefile 'update' target now supports updating from Subversion repositories (issue #5875) remove support for 'patches' subdirectory, it's no longer useful ........ r7268 | kpfleming | 2005-12-01 17:34:58 -0600 (Thu, 01 Dec 2005) | 2 lines ensure channel's scheduling context is freed (issue #5788) ........ r7269 | kpfleming | 2005-12-01 17:49:44 -0600 (Thu, 01 Dec 2005) | 2 lines don't block waiting for the Festival server forever when it goes away (issue #5882) ........ r7270 | kpfleming | 2005-12-01 18:26:12 -0600 (Thu, 01 Dec 2005) | 2 lines allow variables to exist on both 'halves' of the Local channel (issue #5810) ........ r7271 | kpfleming | 2005-12-01 18:28:48 -0600 (Thu, 01 Dec 2005) | 2 lines protect agent_bridgedchannel() from segfaulting when there is no bridged channel (issue #5879) ........ r7272 | kpfleming | 2005-12-01 18:39:00 -0600 (Thu, 01 Dec 2005) | 3 lines properly handle password changes when mailbox is last line of config file and not followed by a newline (issue #5870) reformat password changing code to conform to coding guidelines (issue #5870) ........ r7273 | kpfleming | 2005-12-01 18:42:40 -0600 (Thu, 01 Dec 2005) | 2 lines allow previous context-searching behavior to be used if desired (issue #5899) ........ r7274 | kpfleming | 2005-12-01 18:51:15 -0600 (Thu, 01 Dec 2005) | 2 lines inherit channel variables into channels created by Page() application (issue #5888) ........ r7275 | oej | 2005-12-01 18:52:13 -0600 (Thu, 01 Dec 2005) | 2 lines Bug #5907. Improve SIP INFO DTMF debugging output. (1.2 & Trunk) ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@7276 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2005-12-02 01:01:11 +00:00
#include "asterisk/chanvars.h"
#include "asterisk/utils.h"
#include "asterisk/devicestate.h"
#include "asterisk/dial.h"
/*** DOCUMENTATION
<application name="Page" language="en_US">
<synopsis>
Page series of phones
</synopsis>
<syntax>
<parameter name="Technology/Resource" required="true" argsep="&amp;">
<argument name="Technology/Resource" required="true">
<para>Specification of the device(s) to dial. These must be in the format of
<literal>Technology/Resource</literal>, where <replaceable>Technology</replaceable>
represents a particular channel driver, and <replaceable>Resource</replaceable> represents a resource
available to that particular channel driver.</para>
</argument>
<argument name="Technology2/Resource2" multiple="true">
<para>Optional extra devices to dial inparallel</para>
<para>If you need more then one enter them as Technology2/Resource2&amp;
Technology3/Resourse3&amp;.....</para>
</argument>
</parameter>
<parameter name="options">
<optionlist>
<option name="d">
<para>Full duplex audio</para>
</option>
<option name="i">
<para>Ignore attempts to forward the call</para>
</option>
<option name="q">
<para>Quiet, do not play beep to caller</para>
</option>
<option name="r">
<para>Record the page into a file (ConfBridge option <literal>r</literal>)</para>
</option>
<option name="s">
<para>Only dial a channel if its device state says that it is <literal>NOT_INUSE</literal></para>
</option>
<option name="A">
<argument name="x" required="true">
<para>The announcement to playback in all devices</para>
</argument>
<para>Play an announcement simultaneously to all paged participants</para>
</option>
<option name="n">
<para>Do not play simultaneous announcement to caller (implies <literal>A(x)</literal>)</para>
</option>
</optionlist>
</parameter>
<parameter name="timeout">
<para>Specify the length of time that the system will attempt to connect a call.
After this duration, any intercom calls that have not been answered will be hung up by the
system.</para>
</parameter>
</syntax>
<description>
<para>Places outbound calls to the given <replaceable>technology</replaceable>/<replaceable>resource</replaceable>
and dumps them into a conference bridge as muted participants. The original
caller is dumped into the conference as a speaker and the room is
destroyed when the original callers leaves.</para>
</description>
<see-also>
<ref type="application">ConfBridge</ref>
</see-also>
</application>
***/
static const char * const app_page= "Page";
enum page_opt_flags {
PAGE_DUPLEX = (1 << 0),
PAGE_QUIET = (1 << 1),
PAGE_RECORD = (1 << 2),
PAGE_SKIP = (1 << 3),
PAGE_IGNORE_FORWARDS = (1 << 4),
PAGE_ANNOUNCE = (1 << 5),
PAGE_NOCALLERANNOUNCE = (1 << 6),
};
enum {
OPT_ARG_ANNOUNCE = 0,
OPT_ARG_ARRAY_SIZE = 1,
};
AST_APP_OPTIONS(page_opts, {
AST_APP_OPTION('d', PAGE_DUPLEX),
AST_APP_OPTION('q', PAGE_QUIET),
AST_APP_OPTION('r', PAGE_RECORD),
AST_APP_OPTION('s', PAGE_SKIP),
AST_APP_OPTION('i', PAGE_IGNORE_FORWARDS),
AST_APP_OPTION_ARG('A', PAGE_ANNOUNCE, OPT_ARG_ANNOUNCE),
AST_APP_OPTION('n', PAGE_NOCALLERANNOUNCE),
});
/* We use this structure as a way to pass this to all dialed channels */
struct page_options {
char *opts[OPT_ARG_ARRAY_SIZE];
struct ast_flags flags;
};
static void page_state_callback(struct ast_dial *dial)
{
struct ast_channel *chan;
struct page_options *options;
if (ast_dial_state(dial) != AST_DIAL_RESULT_ANSWERED ||
!(chan = ast_dial_answered(dial)) ||
!(options = ast_dial_get_user_data(dial))) {
return;
}
ast_func_write(chan, "CONFBRIDGE(bridge,template)", "default_bridge");
if (ast_test_flag(&options->flags, PAGE_RECORD)) {
ast_func_write(chan, "CONFBRIDGE(bridge,record_conference)", "yes");
}
ast_func_write(chan, "CONFBRIDGE(user,quiet)", "yes");
ast_func_write(chan, "CONFBRIDGE(user,end_marked)", "yes");
if (!ast_test_flag(&options->flags, PAGE_DUPLEX)) {
ast_func_write(chan, "CONFBRIDGE(user,startmuted)", "yes");
}
if (ast_test_flag(&options->flags, PAGE_ANNOUNCE) && !ast_strlen_zero(options->opts[OPT_ARG_ANNOUNCE])) {
ast_func_write(chan, "CONFBRIDGE(user,announcement)", options->opts[OPT_ARG_ANNOUNCE]);
}
}
static int page_exec(struct ast_channel *chan, const char *data)
{
char *tech, *resource, *tmp;
char confbridgeopts[128], originator[AST_CHANNEL_NAME];
struct page_options options = { { 0, }, { 0, } };
unsigned int confid = ast_random();
struct ast_app *app;
int res = 0, pos = 0, i = 0;
struct ast_dial **dial_list;
unsigned int num_dials;
int timeout = 0;
char *parse;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(devices);
AST_APP_ARG(options);
AST_APP_ARG(timeout);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "This application requires at least one argument (destination(s) to page)\n");
return -1;
}
if (!(app = pbx_findapp("ConfBridge"))) {
ast_log(LOG_WARNING, "There is no ConfBridge application available!\n");
return -1;
};
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
Replace direct access to channel name with accessor functions There are many benefits to making the ast_channel an opaque handle, from increasing maintainability to presenting ways to kill masquerades. This patch kicks things off by taking things a field at a time, renaming the field to '__do_not_use_${fieldname}' and then writing setters/getters and converting the existing code to using them. When all fields are done, we can move ast_channel to a C file from channel.h and lop off the '__do_not_use_'. This patch sets up main/channel_interal_api.c to be the only file that actually accesses the ast_channel's fields directly. The intent would be for any API functions in channel.c to use the accessor functions. No more monkeying around with channel internals. We should use our own APIs. The interesting changes in this patch are the addition of channel_internal_api.c, the moving of the AST_DATA stuff from channel.c to channel_internal_api.c (note: the AST_DATA stuff will have to be reworked to use accessor functions when ast_channel is really opaque), and some re-working of the way channel iterators/callbacks are handled so as to avoid creating fake ast_channels on the stack to pass in matching data by directly accessing fields (since "name" is a stringfield and the fake channel doesn't init the stringfields, you can't use the ast_channel_name_set() function). I went with ast_channel_name(chan) for a getter, and ast_channel_name_set(chan, name) for a setter. The majority of the grunt-work for this change was done by writing a semantic patch using Coccinelle ( http://coccinelle.lip6.fr/ ). Review: https://reviewboard.asterisk.org/r/1655/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@350223 65c4cc65-6c06-0410-ace0-fbb531ad65f3
2012-01-09 22:15:50 +00:00
ast_copy_string(originator, ast_channel_name(chan), sizeof(originator));
if ((tmp = strchr(originator, '-'))) {
*tmp = '\0';
}
if (!ast_strlen_zero(args.options)) {
ast_app_parse_options(page_opts, &options.flags, options.opts, args.options);
}
if (!ast_strlen_zero(args.timeout)) {
timeout = atoi(args.timeout);
}
snprintf(confbridgeopts, sizeof(confbridgeopts), "ConfBridge,%u", confid);
/* Count number of extensions in list by number of ampersands + 1 */
num_dials = 1;
tmp = args.devices;
while (*tmp) {
if (*tmp == '&') {
num_dials++;
}
tmp++;
}
if (!(dial_list = ast_calloc(num_dials, sizeof(struct ast_dial *)))) {
ast_log(LOG_ERROR, "Can't allocate %ld bytes for dial list\n", (long)(sizeof(struct ast_dial *) * num_dials));
return -1;
}
/* Go through parsing/calling each device */
while ((tech = strsep(&args.devices, "&"))) {
int state = 0;
struct ast_dial *dial = NULL;
/* don't call the originating device */
if (!strcasecmp(tech, originator))
continue;
/* If no resource is available, continue on */
if (!(resource = strchr(tech, '/'))) {
ast_log(LOG_WARNING, "Incomplete destination '%s' supplied.\n", tech);
continue;
}
/* Ensure device is not in use if skip option is enabled */
if (ast_test_flag(&options.flags, PAGE_SKIP)) {
state = ast_device_state(tech);
if (state == AST_DEVICE_UNKNOWN) {
ast_log(LOG_WARNING, "Destination '%s' has device state '%s'. Paging anyway.\n", tech, ast_devstate2str(state));
} else if (state != AST_DEVICE_NOT_INUSE) {
ast_log(LOG_WARNING, "Destination '%s' has device state '%s'.\n", tech, ast_devstate2str(state));
continue;
}
}
*resource++ = '\0';
/* Create a dialing structure */
if (!(dial = ast_dial_create())) {
ast_log(LOG_WARNING, "Failed to create dialing structure.\n");
continue;
}
/* Append technology and resource */
if (ast_dial_append(dial, tech, resource) == -1) {
ast_log(LOG_ERROR, "Failed to add %s to outbound dial\n", tech);
continue;
}
/* Set ANSWER_EXEC as global option */
ast_dial_option_global_enable(dial, AST_DIAL_OPTION_ANSWER_EXEC, confbridgeopts);
if (timeout) {
ast_dial_set_global_timeout(dial, timeout * 1000);
}
if (ast_test_flag(&options.flags, PAGE_IGNORE_FORWARDS)) {
ast_dial_option_global_enable(dial, AST_DIAL_OPTION_DISABLE_CALL_FORWARDING, NULL);
}
ast_dial_set_state_callback(dial, &page_state_callback);
ast_dial_set_user_data(dial, &options);
/* Run this dial in async mode */
ast_dial_run(dial, chan, 1);
/* Put in our dialing array */
dial_list[pos++] = dial;
}
if (!ast_test_flag(&options.flags, PAGE_QUIET)) {
res = ast_streamfile(chan, "beep", ast_channel_language(chan));
if (!res)
res = ast_waitstream(chan, "");
}
if (!res) {
ast_func_write(chan, "CONFBRIDGE(bridge,template)", "default_bridge");
if (ast_test_flag(&options.flags, PAGE_RECORD)) {
ast_func_write(chan, "CONFBRIDGE(bridge,record_conference)", "yes");
}
ast_func_write(chan, "CONFBRIDGE(user,quiet)", "yes");
ast_func_write(chan, "CONFBRIDGE(user,marked)", "yes");
snprintf(confbridgeopts, sizeof(confbridgeopts), "%u", confid);
pbx_exec(chan, app, confbridgeopts);
}
/* Go through each dial attempt cancelling, joining, and destroying */
for (i = 0; i < pos; i++) {
struct ast_dial *dial = dial_list[i];
/* We have to wait for the async thread to exit as it's possible ConfBridge won't throw them out immediately */
ast_dial_join(dial);
/* Hangup all channels */
ast_dial_hangup(dial);
/* Destroy dialing structure */
ast_dial_destroy(dial);
}
return -1;
}
static int unload_module(void)
{
return ast_unregister_application(app_page);
}
static int load_module(void)
{
return ast_register_application_xml(app_page, page_exec);
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Page Multiple Phones");