123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2005, Joshua Colp
- *
- * Joshua Colp <jcolp@digium.com>
- *
- * Portions merged from app_pickupchan, which was
- * Copyright (C) 2008, Gary Cook
- *
- * 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.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*! \file
- *
- * \brief Directed Call Pickup Support
- *
- * \author Joshua Colp <jcolp@digium.com>
- * \author Gary Cook
- *
- * \ingroup applications
- */
- /*** MODULEINFO
- <support_level>core</support_level>
- ***/
- #include "asterisk.h"
- ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
- #include "asterisk/file.h"
- #include "asterisk/channel.h"
- #include "asterisk/pbx.h"
- #include "asterisk/module.h"
- #include "asterisk/lock.h"
- #include "asterisk/app.h"
- #include "asterisk/pickup.h"
- #include "asterisk/manager.h"
- #include "asterisk/callerid.h"
- #define PICKUPMARK "PICKUPMARK"
- /*** DOCUMENTATION
- <application name="Pickup" language="en_US">
- <synopsis>
- Directed extension call pickup.
- </synopsis>
- <syntax>
- <parameter name="targets" argsep="&">
- <argument name="extension" argsep="@" required="true">
- <para>Specification of the pickup target.</para>
- <argument name="extension" required="true"/>
- <argument name="context" />
- </argument>
- <argument name="extension2" argsep="@" multiple="true">
- <para>Additional specifications of pickup targets.</para>
- <argument name="extension2" required="true"/>
- <argument name="context2"/>
- </argument>
- </parameter>
- </syntax>
- <description>
- <para>This application can pickup a specified ringing channel. The channel
- to pickup can be specified in the following ways.</para>
- <para>1) If no <replaceable>extension</replaceable> targets are specified,
- the application will pickup a channel matching the pickup group of the
- requesting channel.</para>
- <para>2) If the <replaceable>extension</replaceable> is specified with a
- <replaceable>context</replaceable> of the special string
- <literal>PICKUPMARK</literal> (for example 10@PICKUPMARK), the application
- will pickup a channel which has defined the channel variable
- <variable>PICKUPMARK</variable> with the same value as
- <replaceable>extension</replaceable> (in this example,
- <literal>10</literal>).</para>
- <para>3) If the <replaceable>extension</replaceable> is specified
- with or without a <replaceable>context</replaceable>, the channel with a
- matching <replaceable>extension</replaceable> and <replaceable>context</replaceable>
- will be picked up. If no <replaceable>context</replaceable> is specified,
- the current context will be used.</para>
- <note><para>The <replaceable>extension</replaceable> is typically set on
- matching channels by the dial application that created the channel. The
- <replaceable>context</replaceable> is set on matching channels by the
- channel driver for the device.</para></note>
- </description>
- </application>
- <application name="PickupChan" language="en_US">
- <synopsis>
- Pickup a ringing channel.
- </synopsis>
- <syntax >
- <parameter name="channel" argsep="&" required="true">
- <argument name="channel" required="true" />
- <argument name="channel2" required="false" multiple="true" />
- <para>List of channel names or channel uniqueids to pickup if ringing.
- For example, a channel name could be <literal>SIP/bob</literal> or
- <literal>SIP/bob-00000000</literal> to find
- <literal>SIP/bob-00000000</literal>.
- </para>
- </parameter>
- <parameter name="options" required="false">
- <optionlist>
- <option name="p">
- <para>Supplied channel names are prefixes. For example,
- <literal>SIP/bob</literal> will match
- <literal>SIP/bob-00000000</literal> and
- <literal>SIP/bobby-00000000</literal>.
- </para>
- </option>
- </optionlist>
- </parameter>
- </syntax>
- <description>
- <para>Pickup a specified <replaceable>channel</replaceable> if ringing.</para>
- </description>
- </application>
- ***/
- static const char app[] = "Pickup";
- static const char app2[] = "PickupChan";
- struct pickup_by_name_args {
- /*! Channel attempting to pickup a call. */
- struct ast_channel *chan;
- /*! Channel uniqueid or partial channel name to match. */
- const char *name;
- /*! Length of partial channel name to match. */
- size_t len;
- };
- static int find_by_name(void *obj, void *arg, void *data, int flags)
- {
- struct ast_channel *target = obj;/*!< Potential pickup target */
- struct pickup_by_name_args *args = data;
- if (args->chan == target) {
- /* The channel attempting to pickup a call cannot pickup itself. */
- return 0;
- }
- ast_channel_lock(target);
- if (!strncasecmp(ast_channel_name(target), args->name, args->len)
- && ast_can_pickup(target)) {
- /* Return with the channel still locked on purpose */
- return CMP_MATCH | CMP_STOP;
- }
- ast_channel_unlock(target);
- return 0;
- }
- static int find_by_uniqueid(void *obj, void *arg, void *data, int flags)
- {
- struct ast_channel *target = obj;/*!< Potential pickup target */
- struct pickup_by_name_args *args = data;
- if (args->chan == target) {
- /* The channel attempting to pickup a call cannot pickup itself. */
- return 0;
- }
- ast_channel_lock(target);
- if (!strcasecmp(ast_channel_uniqueid(target), args->name)
- && ast_can_pickup(target)) {
- /* Return with the channel still locked on purpose */
- return CMP_MATCH | CMP_STOP;
- }
- ast_channel_unlock(target);
- return 0;
- }
- /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
- static struct ast_channel *find_by_channel(struct ast_channel *chan, const char *channame)
- {
- struct ast_channel *target;
- char *chkchan;
- struct pickup_by_name_args pickup_args;
- pickup_args.chan = chan;
- if (strchr(channame, '-')) {
- /*
- * Use the given channel name string as-is. This allows a full channel
- * name with a typical sequence number to be used as well as still
- * allowing the odd partial channel name that has a '-' in it to still
- * work, i.e. Local/bob@en-phone.
- */
- pickup_args.len = strlen(channame);
- pickup_args.name = channame;
- } else {
- /*
- * Append a '-' for the comparison so we check the channel name less
- * a sequence number, i.e Find SIP/bob- and not SIP/bobby.
- */
- pickup_args.len = strlen(channame) + 1;
- chkchan = ast_alloca(pickup_args.len + 1);
- strcpy(chkchan, channame);/* Safe */
- strcat(chkchan, "-");
- pickup_args.name = chkchan;
- }
- target = ast_channel_callback(find_by_name, NULL, &pickup_args, 0);
- if (target) {
- return target;
- }
- /* Now try a search for uniqueid. */
- pickup_args.name = channame;
- pickup_args.len = 0;
- return ast_channel_callback(find_by_uniqueid, NULL, &pickup_args, 0);
- }
- /*! \brief Attempt to pick up named channel. */
- static int pickup_by_channel(struct ast_channel *chan, const char *name)
- {
- int res = -1;
- struct ast_channel *target;/*!< Potential pickup target */
- /* The found channel is already locked. */
- target = find_by_channel(chan, name);
- if (target) {
- res = ast_do_pickup(chan, target);
- ast_channel_unlock(target);
- target = ast_channel_unref(target);
- }
- return res;
- }
- /* Attempt to pick up specified extension with context */
- static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
- {
- struct ast_channel *target = NULL;/*!< Potential pickup target */
- struct ast_channel_iterator *iter;
- int res = -1;
- if (!(iter = ast_channel_iterator_by_exten_new(exten, context))) {
- return -1;
- }
- while ((target = ast_channel_iterator_next(iter))) {
- ast_channel_lock(target);
- if ((chan != target) && ast_can_pickup(target)) {
- ast_log(LOG_NOTICE, "%s pickup by %s\n", ast_channel_name(target), ast_channel_name(chan));
- break;
- }
- ast_channel_unlock(target);
- target = ast_channel_unref(target);
- }
- ast_channel_iterator_destroy(iter);
- if (target) {
- res = ast_do_pickup(chan, target);
- ast_channel_unlock(target);
- target = ast_channel_unref(target);
- }
- return res;
- }
- static int find_by_mark(void *obj, void *arg, void *data, int flags)
- {
- struct ast_channel *target = obj;/*!< Potential pickup target */
- struct ast_channel *chan = arg;
- const char *mark = data;
- const char *tmp;
- if (chan == target) {
- /* The channel attempting to pickup a call cannot pickup itself. */
- return 0;
- }
- ast_channel_lock(target);
- tmp = pbx_builtin_getvar_helper(target, PICKUPMARK);
- if (tmp && !strcasecmp(tmp, mark) && ast_can_pickup(target)) {
- /* Return with the channel still locked on purpose */
- return CMP_MATCH | CMP_STOP;
- }
- ast_channel_unlock(target);
- return 0;
- }
- /* Attempt to pick up specified mark */
- static int pickup_by_mark(struct ast_channel *chan, const char *mark)
- {
- struct ast_channel *target;/*!< Potential pickup target */
- int res = -1;
- /* The found channel is already locked. */
- target = ast_channel_callback(find_by_mark, chan, (char *) mark, 0);
- if (target) {
- res = ast_do_pickup(chan, target);
- ast_channel_unlock(target);
- target = ast_channel_unref(target);
- }
- return res;
- }
- static int pickup_by_group(struct ast_channel *chan)
- {
- struct ast_channel *target;/*!< Potential pickup target */
- int res = -1;
- /* The found channel is already locked. */
- target = ast_pickup_find_by_group(chan);
- if (target) {
- ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
- res = ast_do_pickup(chan, target);
- ast_channel_unlock(target);
- target = ast_channel_unref(target);
- }
- return res;
- }
- /* application entry point for Pickup() */
- static int pickup_exec(struct ast_channel *chan, const char *data)
- {
- char *parse;
- char *exten;
- char *context;
- if (ast_strlen_zero(data)) {
- return pickup_by_group(chan) ? 0 : -1;
- }
- /* Parse extension (and context if there) */
- parse = ast_strdupa(data);
- for (;;) {
- if (ast_strlen_zero(parse)) {
- break;
- }
- exten = strsep(&parse, "&");
- if (ast_strlen_zero(exten)) {
- continue;
- }
- context = strchr(exten, '@');
- if (context) {
- *context++ = '\0';
- }
- if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
- if (!pickup_by_mark(chan, exten)) {
- /* Pickup successful. Stop the dialplan this channel is a zombie. */
- return -1;
- }
- } else {
- if (ast_strlen_zero(context)) {
- context = (char *) ast_channel_context(chan);
- }
- if (!pickup_by_exten(chan, exten, context)) {
- /* Pickup successful. Stop the dialplan this channel is a zombie. */
- return -1;
- }
- }
- ast_log(LOG_NOTICE, "No target channel found for %s@%s.\n", exten, context);
- }
- /* Pickup failed. Keep going in the dialplan. */
- return 0;
- }
- /* Find channel for pick up specified by partial channel name */
- static struct ast_channel *find_by_part(struct ast_channel *chan, const char *part)
- {
- struct ast_channel *target;
- struct pickup_by_name_args pickup_args;
- pickup_args.chan = chan;
- /* Try a partial channel name search. */
- pickup_args.name = part;
- pickup_args.len = strlen(part);
- target = ast_channel_callback(find_by_name, NULL, &pickup_args, 0);
- if (target) {
- return target;
- }
- /* Now try a search for uniqueid. */
- pickup_args.name = part;
- pickup_args.len = 0;
- return ast_channel_callback(find_by_uniqueid, NULL, &pickup_args, 0);
- }
- /* Attempt to pick up specified by partial channel name */
- static int pickup_by_part(struct ast_channel *chan, const char *part)
- {
- struct ast_channel *target;/*!< Potential pickup target */
- int res = -1;
- /* The found channel is already locked. */
- target = find_by_part(chan, part);
- if (target) {
- res = ast_do_pickup(chan, target);
- ast_channel_unlock(target);
- target = ast_channel_unref(target);
- }
- return res;
- }
- enum OPT_PICKUPCHAN_FLAGS {
- OPT_PICKUPCHAN_PARTIAL = (1 << 0), /* Channel name is a partial name. */
- };
- AST_APP_OPTIONS(pickupchan_opts, BEGIN_OPTIONS
- AST_APP_OPTION('p', OPT_PICKUPCHAN_PARTIAL),
- END_OPTIONS);
- /* application entry point for PickupChan() */
- static int pickupchan_exec(struct ast_channel *chan, const char *data)
- {
- char *pickup = NULL;
- char *parse = ast_strdupa(data);
- AST_DECLARE_APP_ARGS(args,
- AST_APP_ARG(channel);
- AST_APP_ARG(options);
- AST_APP_ARG(other); /* Any remining unused arguments */
- );
- struct ast_flags opts;
- AST_STANDARD_APP_ARGS(args, parse);
- if (ast_strlen_zero(args.channel)) {
- ast_log(LOG_WARNING, "PickupChan requires an argument (channel)!\n");
- /* Pickup failed. Keep going in the dialplan. */
- return 0;
- }
- if (ast_app_parse_options(pickupchan_opts, &opts, NULL, args.options)) {
- /*
- * General invalid option syntax.
- * Pickup failed. Keep going in the dialplan.
- */
- return 0;
- }
- /* Parse channel */
- for (;;) {
- if (ast_strlen_zero(args.channel)) {
- break;
- }
- pickup = strsep(&args.channel, "&");
- if (ast_strlen_zero(pickup)) {
- continue;
- }
- if (ast_test_flag(&opts, OPT_PICKUPCHAN_PARTIAL)) {
- if (!pickup_by_part(chan, pickup)) {
- /* Pickup successful. Stop the dialplan this channel is a zombie. */
- return -1;
- }
- } else if (!pickup_by_channel(chan, pickup)) {
- /* Pickup successful. Stop the dialplan this channel is a zombie. */
- return -1;
- }
- ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
- }
- /* Pickup failed. Keep going in the dialplan. */
- return 0;
- }
- static int unload_module(void)
- {
- int res;
- res = ast_unregister_application(app);
- res |= ast_unregister_application(app2);
- return res;
- }
- static int load_module(void)
- {
- int res;
- res = ast_register_application_xml(app, pickup_exec);
- res |= ast_register_application_xml(app2, pickupchan_exec);
- return res;
- }
- AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");
|