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="Technology/Resource" argsep="&" required="true">
101 <argument name="Technology/Resource" required="true" />
102 <argument name="Technology2/Resource2" required="false" multiple="true" />
104 <parameter name="options" required="false">
107 <para>Channel name specified partial name. Used when find channel by callid.</para>
113 <para>This will pickup a specified <replaceable>channel</replaceable> if ringing.</para>
118 static const char app[] = "Pickup";
119 static const char app2[] = "PickupChan";
121 struct pickup_by_name_args {
126 static int pickup_by_name_cb(void *obj, void *arg, void *data, int flags)
128 struct ast_channel *target = obj;/*!< Potential pickup target */
129 struct pickup_by_name_args *args = data;
131 ast_channel_lock(target);
132 if (!strncasecmp(ast_channel_name(target), args->name, args->len) && ast_can_pickup(target)) {
133 /* Return with the channel still locked on purpose */
134 return CMP_MATCH | CMP_STOP;
136 ast_channel_unlock(target);
141 /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
142 static struct ast_channel *my_ast_get_channel_by_name_locked(const char *channame)
145 struct pickup_by_name_args pickup_args;
147 /* Check if channel name contains a '-'.
148 * In this case the channel name will be interpreted as full channel name.
150 if (strchr(channame, '-')) {
151 /* check full channel name */
152 pickup_args.len = strlen(channame);
153 pickup_args.name = channame;
155 /* need to append a '-' for the comparison so we check full channel name,
156 * i.e SIP/hgc- , use a temporary variable so original stays the same for
159 pickup_args.len = strlen(channame) + 1;
160 chkchan = ast_alloca(pickup_args.len + 1);
161 strcpy(chkchan, channame);
162 strcat(chkchan, "-");
163 pickup_args.name = chkchan;
166 return ast_channel_callback(pickup_by_name_cb, NULL, &pickup_args, 0);
169 /*! \brief Attempt to pick up named channel, does not use context */
170 static int pickup_by_channel(struct ast_channel *chan, char *pickup)
173 struct ast_channel *target;/*!< Potential pickup target */
175 target = my_ast_get_channel_by_name_locked(pickup);
177 /* Just check that we are not picking up the SAME as target. (i.e. ourself) */
178 if (chan != target) {
179 res = ast_do_pickup(chan, target);
181 ast_channel_unlock(target);
182 target = ast_channel_unref(target);
188 /* Attempt to pick up specified extension with context */
189 static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
191 struct ast_channel *target = NULL;/*!< Potential pickup target */
192 struct ast_channel_iterator *iter;
195 if (!(iter = ast_channel_iterator_by_exten_new(exten, context))) {
199 while ((target = ast_channel_iterator_next(iter))) {
200 ast_channel_lock(target);
201 if ((chan != target) && ast_can_pickup(target)) {
202 ast_log(LOG_NOTICE, "%s pickup by %s\n", ast_channel_name(target), ast_channel_name(chan));
205 ast_channel_unlock(target);
206 target = ast_channel_unref(target);
209 ast_channel_iterator_destroy(iter);
212 res = ast_do_pickup(chan, target);
213 ast_channel_unlock(target);
214 target = ast_channel_unref(target);
220 static int find_by_mark(void *obj, void *arg, void *data, int flags)
222 struct ast_channel *target = obj;/*!< Potential pickup target */
223 struct ast_channel *chan = arg;
224 const char *mark = data;
227 if (chan == target) {
228 /* The channel attempting to pickup a call cannot pickup itself. */
232 ast_channel_lock(target);
233 tmp = pbx_builtin_getvar_helper(target, PICKUPMARK);
234 if (tmp && !strcasecmp(tmp, mark) && ast_can_pickup(target)) {
235 /* Return with the channel still locked on purpose */
236 return CMP_MATCH | CMP_STOP;
238 ast_channel_unlock(target);
243 /* Attempt to pick up specified mark */
244 static int pickup_by_mark(struct ast_channel *chan, const char *mark)
246 struct ast_channel *target;/*!< Potential pickup target */
249 /* The found channel is already locked. */
250 target = ast_channel_callback(find_by_mark, chan, (char *) mark, 0);
252 res = ast_do_pickup(chan, target);
253 ast_channel_unlock(target);
254 target = ast_channel_unref(target);
260 static int pickup_by_group(struct ast_channel *chan)
262 struct ast_channel *target;/*!< Potential pickup target */
265 /* The found channel is already locked. */
266 target = ast_pickup_find_by_group(chan);
268 ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));
269 res = ast_do_pickup(chan, target);
270 ast_channel_unlock(target);
271 target = ast_channel_unref(target);
277 /* application entry point for Pickup() */
278 static int pickup_exec(struct ast_channel *chan, const char *data)
284 if (ast_strlen_zero(data)) {
285 return pickup_by_group(chan) ? 0 : -1;
288 /* Parse extension (and context if there) */
289 tmp = ast_strdupa(data);
290 while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
291 if ((context = strchr(exten, '@')))
293 if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
294 if (!pickup_by_mark(chan, exten)) {
295 /* Pickup successful. Stop the dialplan this channel is a zombie. */
299 if (ast_strlen_zero(context)) {
300 context = (char *) ast_channel_context(chan);
302 if (!pickup_by_exten(chan, exten, context)) {
303 /* Pickup successful. Stop the dialplan this channel is a zombie. */
307 ast_log(LOG_NOTICE, "No target channel found for %s@%s.\n", exten, context);
310 /* Pickup failed. Keep going in the dialplan. */
314 /* Find channel for pick up specified by partial channel name */
315 static int find_by_part(void *obj, void *arg, void *data, int flags)
317 struct ast_channel *target = obj;/*!< Potential pickup target */
318 const char *part = data;
319 int len = strlen(part);
321 ast_channel_lock(target);
322 if (len <= strlen(ast_channel_name(target)) && !strncmp(ast_channel_name(target), part, len)
323 && ast_can_pickup(target)) {
324 /* Return with the channel still locked on purpose */
325 return CMP_MATCH | CMP_STOP;
327 ast_channel_unlock(target);
332 /* Attempt to pick up specified by partial channel name */
333 static int pickup_by_part(struct ast_channel *chan, const char *part)
335 struct ast_channel *target;/*!< Potential pickup target */
338 /* The found channel is already locked. */
339 target = ast_channel_callback(find_by_part, NULL, (char *) part, 0);
341 res = ast_do_pickup(chan, target);
342 ast_channel_unlock(target);
343 target = ast_channel_unref(target);
349 /* application entry point for PickupChan() */
350 static int pickupchan_exec(struct ast_channel *chan, const char *data)
352 int partial_pickup = 0;
354 char *parse = ast_strdupa(data);
355 AST_DECLARE_APP_ARGS(args,
356 AST_APP_ARG(channel);
357 AST_APP_ARG(options);
359 AST_STANDARD_APP_ARGS(args, parse);
361 if (ast_strlen_zero(args.channel)) {
362 ast_log(LOG_WARNING, "PickupChan requires an argument (channel)!\n");
363 /* Pickup failed. Keep going in the dialplan. */
367 if (!ast_strlen_zero(args.options) && strchr(args.options, 'p')) {
372 while (!ast_strlen_zero(args.channel) && (pickup = strsep(&args.channel, "&"))) {
373 if (!strncasecmp(ast_channel_name(chan), pickup, strlen(pickup))) {
374 ast_log(LOG_NOTICE, "Cannot pickup your own channel %s.\n", pickup);
376 if (partial_pickup) {
377 if (!pickup_by_part(chan, pickup)) {
378 /* Pickup successful. Stop the dialplan this channel is a zombie. */
381 } else if (!pickup_by_channel(chan, pickup)) {
382 /* Pickup successful. Stop the dialplan this channel is a zombie. */
385 ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
389 /* Pickup failed. Keep going in the dialplan. */
393 static int unload_module(void)
397 res = ast_unregister_application(app);
398 res |= ast_unregister_application(app2);
403 static int load_module(void)
407 res = ast_register_application_xml(app, pickup_exec);
408 res |= ast_register_application_xml(app2, pickupchan_exec);
413 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");