2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005, Joshua Colp
6 * Joshua Colp <jcolp@digium.com>
8 * Portions merged from app_pickupchan, which was
9 * Copyright (C) 2008, Gary Cook
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.
17 * This program is free software, distributed under the terms of
18 * the GNU General Public License Version 2. See the LICENSE file
19 * at the top of the source tree.
24 * \brief Directed Call Pickup Support
26 * \author Joshua Colp <jcolp@digium.com>
29 * \ingroup applications
33 <support_level>core</support_level>
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
40 #include "asterisk/file.h"
41 #include "asterisk/channel.h"
42 #include "asterisk/pbx.h"
43 #include "asterisk/module.h"
44 #include "asterisk/lock.h"
45 #include "asterisk/app.h"
46 #include "asterisk/pickup.h"
47 #include "asterisk/manager.h"
48 #include "asterisk/callerid.h"
50 #define PICKUPMARK "PICKUPMARK"
53 <application name="Pickup" language="en_US">
55 Directed extension call pickup.
58 <parameter name="targets" argsep="&">
59 <argument name="extension" argsep="@" required="true">
60 <para>Specification of the pickup target.</para>
61 <argument name="extension" required="true"/>
62 <argument name="context" />
64 <argument name="extension2" argsep="@" multiple="true">
65 <para>Additional specifications of pickup targets.</para>
66 <argument name="extension2" required="true"/>
67 <argument name="context2"/>
72 <para>This application can pickup a specified ringing channel. The channel
73 to pickup can be specified in the following ways.</para>
74 <para>1) If no <replaceable>extension</replaceable> targets are specified,
75 the application will pickup a channel matching the pickup group of the
76 requesting channel.</para>
77 <para>2) If the <replaceable>extension</replaceable> is specified with a
78 <replaceable>context</replaceable> of the special string
79 <literal>PICKUPMARK</literal> (for example 10@PICKUPMARK), the application
80 will pickup a channel which has defined the channel variable
81 <variable>PICKUPMARK</variable> with the same value as
82 <replaceable>extension</replaceable> (in this example,
83 <literal>10</literal>).</para>
84 <para>3) If the <replaceable>extension</replaceable> is specified
85 with or without a <replaceable>context</replaceable>, the channel with a
86 matching <replaceable>extension</replaceable> and <replaceable>context</replaceable>
87 will be picked up. If no <replaceable>context</replaceable> is specified,
88 the current context will be used.</para>
89 <note><para>The <replaceable>extension</replaceable> is typically set on
90 matching channels by the dial application that created the channel. The
91 <replaceable>context</replaceable> is set on matching channels by the
92 channel driver for the device.</para></note>
95 <application name="PickupChan" language="en_US">
97 Pickup a ringing channel.
100 <parameter name="channel" argsep="&" required="true">
101 <argument name="channel" required="true" />
102 <argument name="channel2" required="false" multiple="true" />
103 <para>List of channel names or channel uniqueids to pickup if ringing.
104 For example, a channel name could be <literal>SIP/bob</literal> or
105 <literal>SIP/bob-00000000</literal> to find
106 <literal>SIP/bob-00000000</literal>.
109 <parameter name="options" required="false">
112 <para>Supplied channel names are prefixes. For example,
113 <literal>SIP/bob</literal> will match
114 <literal>SIP/bob-00000000</literal> and
115 <literal>SIP/bobby-00000000</literal>.
122 <para>Pickup a specified <replaceable>channel</replaceable> if ringing.</para>
127 static const char app[] = "Pickup";
128 static const char app2[] = "PickupChan";
130 struct pickup_by_name_args {
131 /*! Channel attempting to pickup a call. */
132 struct ast_channel *chan;
133 /*! Channel uniqueid or partial channel name to match. */
135 /*! Length of partial channel name to match. */
139 static int find_by_name(void *obj, void *arg, void *data, int flags)
141 struct ast_channel *target = obj;/*!< Potential pickup target */
142 struct pickup_by_name_args *args = data;
144 if (args->chan == target) {
145 /* The channel attempting to pickup a call cannot pickup itself. */
149 ast_channel_lock(target);
150 if (!strncasecmp(ast_channel_name(target), args->name, args->len)
151 && ast_can_pickup(target)) {
152 /* Return with the channel still locked on purpose */
153 return CMP_MATCH | CMP_STOP;
155 ast_channel_unlock(target);
160 static int find_by_uniqueid(void *obj, void *arg, void *data, int flags)
162 struct ast_channel *target = obj;/*!< Potential pickup target */
163 struct pickup_by_name_args *args = data;
165 if (args->chan == target) {
166 /* The channel attempting to pickup a call cannot pickup itself. */
170 ast_channel_lock(target);
171 if (!strcasecmp(ast_channel_uniqueid(target), args->name)
172 && ast_can_pickup(target)) {
173 /* Return with the channel still locked on purpose */
174 return CMP_MATCH | CMP_STOP;
176 ast_channel_unlock(target);
181 /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
182 static struct ast_channel *find_by_channel(struct ast_channel *chan, const char *channame)
184 struct ast_channel *target;
186 struct pickup_by_name_args pickup_args;
188 pickup_args.chan = chan;
190 if (strchr(channame, '-')) {
192 * Use the given channel name string as-is. This allows a full channel
193 * name with a typical sequence number to be used as well as still
194 * allowing the odd partial channel name that has a '-' in it to still
195 * work, i.e. Local/bob@en-phone.
197 pickup_args.len = strlen(channame);
198 pickup_args.name = channame;
201 * Append a '-' for the comparison so we check the channel name less
202 * a sequence number, i.e Find SIP/bob- and not SIP/bobby.
204 pickup_args.len = strlen(channame) + 1;
205 chkchan = ast_alloca(pickup_args.len + 1);
206 strcpy(chkchan, channame);/* Safe */
207 strcat(chkchan, "-");
208 pickup_args.name = chkchan;
210 target = ast_channel_callback(find_by_name, NULL, &pickup_args, 0);
215 /* Now try a search for uniqueid. */
216 pickup_args.name = channame;
218 return ast_channel_callback(find_by_uniqueid, NULL, &pickup_args, 0);
221 /*! \brief Attempt to pick up named channel. */
222 static int pickup_by_channel(struct ast_channel *chan, const char *name)
225 struct ast_channel *target;/*!< Potential pickup target */
227 /* The found channel is already locked. */
228 target = find_by_channel(chan, name);
230 res = ast_do_pickup(chan, target);
231 ast_channel_unlock(target);
232 target = ast_channel_unref(target);
238 /* Attempt to pick up specified extension with context */
239 static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
241 struct ast_channel *target = NULL;/*!< Potential pickup target */
242 struct ast_channel_iterator *iter;
245 if (!(iter = ast_channel_iterator_by_exten_new(exten, context))) {
249 while ((target = ast_channel_iterator_next(iter))) {
250 ast_channel_lock(target);
251 if ((chan != target) && ast_can_pickup(target)) {
252 ast_log(LOG_NOTICE, "%s pickup by %s\n", ast_channel_name(target), ast_channel_name(chan));
255 ast_channel_unlock(target);
256 target = ast_channel_unref(target);
259 ast_channel_iterator_destroy(iter);
262 res = ast_do_pickup(chan, target);
263 ast_channel_unlock(target);
264 target = ast_channel_unref(target);
270 static int find_by_mark(void *obj, void *arg, void *data, int flags)
272 struct ast_channel *target = obj;/*!< Potential pickup target */
273 struct ast_channel *chan = arg;
274 const char *mark = data;
277 if (chan == target) {
278 /* The channel attempting to pickup a call cannot pickup itself. */
282 ast_channel_lock(target);
283 tmp = pbx_builtin_getvar_helper(target, PICKUPMARK);
284 if (tmp && !strcasecmp(tmp, mark) && ast_can_pickup(target)) {
285 /* Return with the channel still locked on purpose */
286 return CMP_MATCH | CMP_STOP;
288 ast_channel_unlock(target);
293 /* Attempt to pick up specified mark */
294 static int pickup_by_mark(struct ast_channel *chan, const char *mark)
296 struct ast_channel *target;/*!< Potential pickup target */
299 /* The found channel is already locked. */
300 target = ast_channel_callback(find_by_mark, chan, (char *) mark, 0);
302 res = ast_do_pickup(chan, target);
303 ast_channel_unlock(target);
304 target = ast_channel_unref(target);
310 static int pickup_by_group(struct ast_channel *chan)
312 struct ast_channel *target;/*!< Potential pickup target */
315 /* The found channel is already locked. */
316 target = ast_pickup_find_by_group(chan);
318 ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
319 res = ast_do_pickup(chan, target);
320 ast_channel_unlock(target);
321 target = ast_channel_unref(target);
327 /* application entry point for Pickup() */
328 static int pickup_exec(struct ast_channel *chan, const char *data)
334 if (ast_strlen_zero(data)) {
335 return pickup_by_group(chan) ? 0 : -1;
338 /* Parse extension (and context if there) */
339 parse = ast_strdupa(data);
341 if (ast_strlen_zero(parse)) {
344 exten = strsep(&parse, "&");
345 if (ast_strlen_zero(exten)) {
349 context = strchr(exten, '@');
353 if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
354 if (!pickup_by_mark(chan, exten)) {
355 /* Pickup successful. Stop the dialplan this channel is a zombie. */
359 if (ast_strlen_zero(context)) {
360 context = (char *) ast_channel_context(chan);
362 if (!pickup_by_exten(chan, exten, context)) {
363 /* Pickup successful. Stop the dialplan this channel is a zombie. */
367 ast_log(LOG_NOTICE, "No target channel found for %s@%s.\n", exten, context);
370 /* Pickup failed. Keep going in the dialplan. */
374 /* Find channel for pick up specified by partial channel name */
375 static struct ast_channel *find_by_part(struct ast_channel *chan, const char *part)
377 struct ast_channel *target;
378 struct pickup_by_name_args pickup_args;
380 pickup_args.chan = chan;
382 /* Try a partial channel name search. */
383 pickup_args.name = part;
384 pickup_args.len = strlen(part);
385 target = ast_channel_callback(find_by_name, NULL, &pickup_args, 0);
390 /* Now try a search for uniqueid. */
391 pickup_args.name = part;
393 return ast_channel_callback(find_by_uniqueid, NULL, &pickup_args, 0);
396 /* Attempt to pick up specified by partial channel name */
397 static int pickup_by_part(struct ast_channel *chan, const char *part)
399 struct ast_channel *target;/*!< Potential pickup target */
402 /* The found channel is already locked. */
403 target = find_by_part(chan, part);
405 res = ast_do_pickup(chan, target);
406 ast_channel_unlock(target);
407 target = ast_channel_unref(target);
413 enum OPT_PICKUPCHAN_FLAGS {
414 OPT_PICKUPCHAN_PARTIAL = (1 << 0), /* Channel name is a partial name. */
417 AST_APP_OPTIONS(pickupchan_opts, BEGIN_OPTIONS
418 AST_APP_OPTION('p', OPT_PICKUPCHAN_PARTIAL),
421 /* application entry point for PickupChan() */
422 static int pickupchan_exec(struct ast_channel *chan, const char *data)
425 char *parse = ast_strdupa(data);
426 AST_DECLARE_APP_ARGS(args,
427 AST_APP_ARG(channel);
428 AST_APP_ARG(options);
429 AST_APP_ARG(other); /* Any remining unused arguments */
431 struct ast_flags opts;
433 AST_STANDARD_APP_ARGS(args, parse);
435 if (ast_strlen_zero(args.channel)) {
436 ast_log(LOG_WARNING, "PickupChan requires an argument (channel)!\n");
437 /* Pickup failed. Keep going in the dialplan. */
440 if (ast_app_parse_options(pickupchan_opts, &opts, NULL, args.options)) {
442 * General invalid option syntax.
443 * Pickup failed. Keep going in the dialplan.
450 if (ast_strlen_zero(args.channel)) {
453 pickup = strsep(&args.channel, "&");
454 if (ast_strlen_zero(pickup)) {
458 if (ast_test_flag(&opts, OPT_PICKUPCHAN_PARTIAL)) {
459 if (!pickup_by_part(chan, pickup)) {
460 /* Pickup successful. Stop the dialplan this channel is a zombie. */
463 } else if (!pickup_by_channel(chan, pickup)) {
464 /* Pickup successful. Stop the dialplan this channel is a zombie. */
467 ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
470 /* Pickup failed. Keep going in the dialplan. */
474 static int unload_module(void)
478 res = ast_unregister_application(app);
479 res |= ast_unregister_application(app2);
484 static int load_module(void)
488 res = ast_register_application_xml(app, pickup_exec);
489 res |= ast_register_application_xml(app2, pickupchan_exec);
494 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");