Minor formatting change to test a mantis change ...
[asterisk-bristuff.git] / apps / app_directed_pickup.c
blob1ead1d77256b812efd01325e5d1a6aa0c7bcba1a
1 /*
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.
22 /*! \file
24 * \brief Directed Call Pickup Support
26 * \author Joshua Colp <jcolp@digium.com>
27 * \author Gary Cook
29 * \ingroup applications
32 #include "asterisk.h"
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include "asterisk/file.h"
37 #include "asterisk/channel.h"
38 #include "asterisk/pbx.h"
39 #include "asterisk/module.h"
40 #include "asterisk/lock.h"
41 #include "asterisk/app.h"
42 #include "asterisk/features.h"
44 #define PICKUPMARK "PICKUPMARK"
46 static const char *app = "Pickup";
47 static const char *synopsis = "Directed Call Pickup";
48 static const char *descrip =
49 " Pickup([extension[@context][&extension2@[context]...]]): This application can\n"
50 "pickup any ringing channel that is calling the specified extension. If no\n"
51 "context is specified, the current context will be used. If you use the special\n"
52 "string \"PICKUPMARK\" for the context parameter, for example 10@PICKUPMARK,\n"
53 "this application tries to find a channel which has defined a ${PICKUPMARK}\n"
54 "channel variable with the same value as \"extension\" (in this example, \"10\").\n"
55 "When no parameter is specified, the application will pickup a channel matching\n"
56 "the pickup group of the active channel.";
58 static const char *app2 = "PickupChan";
59 static const char *synopsis2 = "Pickup a ringing channel";
60 static const char *descrip2 =
61 " PickupChan(channel[&channel...]): This application can pickup any ringing channel\n";
63 /*! \todo This application should return a result code, like PICKUPRESULT */
65 /* Perform actual pickup between two channels */
66 static int pickup_do(struct ast_channel *chan, struct ast_channel *target)
68 int res = 0;
70 ast_debug(1, "Call pickup on '%s' by '%s'\n", target->name, chan->name);
72 if ((res = ast_answer(chan))) {
73 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
74 return -1;
77 if ((res = ast_queue_control(chan, AST_CONTROL_ANSWER))) {
78 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan->name);
79 return -1;
82 if ((res = ast_channel_masquerade(target, chan))) {
83 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
84 return -1;
87 return res;
90 /* Helper function that determines whether a channel is capable of being picked up */
91 static int can_pickup(struct ast_channel *chan)
93 if (!chan->pbx && (chan->_state == AST_STATE_RINGING || chan->_state == AST_STATE_RING))
94 return 1;
95 else
96 return 0;
99 /*! \brief Helper Function to walk through ALL channels checking NAME and STATE */
100 static struct ast_channel *my_ast_get_channel_by_name_locked(char *channame)
102 struct ast_channel *chan;
103 char *chkchan = alloca(strlen(channame) + 2);
105 /* need to append a '-' for the comparison so we check full channel name,
106 * i.e SIP/hgc- , use a temporary variable so original stays the same for
107 * debugging.
109 strcpy(chkchan, channame);
110 strcat(chkchan, "-");
112 for (chan = ast_walk_channel_by_name_prefix_locked(NULL, channame, strlen(channame));
113 chan;
114 chan = ast_walk_channel_by_name_prefix_locked(chan, channame, strlen(channame))) {
115 if (!strncasecmp(chan->name, chkchan, strlen(chkchan)) && can_pickup(chan))
116 return chan;
117 ast_channel_unlock(chan);
119 return NULL;
122 /*! \brief Attempt to pick up specified channel named , does not use context */
123 static int pickup_by_channel(struct ast_channel *chan, char *pickup)
125 int res = 0;
126 struct ast_channel *target;
128 if (!(target = my_ast_get_channel_by_name_locked(pickup)))
129 return -1;
131 /* Just check that we are not picking up the SAME as target */
132 if (chan->name != target->name && chan != target) {
133 res = pickup_do(chan, target);
134 ast_channel_unlock(target);
137 return res;
140 /* Attempt to pick up specified extension with context */
141 static int pickup_by_exten(struct ast_channel *chan, const char *exten, const char *context)
143 int res = -1;
144 struct ast_channel *target = NULL;
146 while ((target = ast_channel_walk_locked(target))) {
147 if ((!strcasecmp(target->macroexten, exten) || !strcasecmp(target->exten, exten)) &&
148 !strcasecmp(target->dialcontext, context) &&
149 can_pickup(target)) {
150 res = pickup_do(chan, target);
151 ast_channel_unlock(target);
152 break;
154 ast_channel_unlock(target);
157 return res;
160 /* Attempt to pick up specified mark */
161 static int pickup_by_mark(struct ast_channel *chan, const char *mark)
163 int res = -1;
164 const char *tmp = NULL;
165 struct ast_channel *target = NULL;
167 while ((target = ast_channel_walk_locked(target))) {
168 if ((tmp = pbx_builtin_getvar_helper(target, PICKUPMARK)) &&
169 !strcasecmp(tmp, mark) &&
170 can_pickup(target)) {
171 res = pickup_do(chan, target);
172 ast_channel_unlock(target);
173 break;
175 ast_channel_unlock(target);
178 return res;
181 /* application entry point for Pickup() */
182 static int pickup_exec(struct ast_channel *chan, void *data)
184 int res = 0;
185 char *tmp = ast_strdupa(data);
186 char *exten = NULL, *context = NULL;
188 if (ast_strlen_zero(data)) {
189 res = ast_pickup_call(chan);
190 return res;
193 /* Parse extension (and context if there) */
194 while (!ast_strlen_zero(tmp) && (exten = strsep(&tmp, "&"))) {
195 if ((context = strchr(exten, '@')))
196 *context++ = '\0';
197 if (!ast_strlen_zero(context) && !strcasecmp(context, PICKUPMARK)) {
198 if (!pickup_by_mark(chan, exten))
199 break;
200 } else {
201 if (!pickup_by_exten(chan, exten, !ast_strlen_zero(context) ? context : chan->context))
202 break;
204 ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten);
207 return res;
210 /* application entry point for PickupChan() */
211 static int pickupchan_exec(struct ast_channel *chan, void *data)
213 int res = 0;
214 char *tmp = ast_strdupa(data);
215 char *pickup = NULL;
217 if (ast_strlen_zero(data)) {
218 ast_log(LOG_WARNING, "PickupChan requires an argument (channel)!\n");
219 return -1;
222 /* Parse channel */
223 while (!ast_strlen_zero(tmp) && (pickup = strsep(&tmp, "&"))) {
224 if (!strncasecmp(chan->name, pickup, strlen(pickup))) {
225 ast_log(LOG_NOTICE, "Cannot pickup your own channel %s.\n", pickup);
226 } else {
227 if (!pickup_by_channel(chan, pickup)) {
228 break;
230 ast_log(LOG_NOTICE, "No target channel found for %s.\n", pickup);
234 return res;
237 static int unload_module(void)
239 int res;
241 res = ast_unregister_application(app);
242 res |= ast_unregister_application(app2);
244 return res;
247 static int load_module(void)
249 int res;
251 res = ast_register_application(app, pickup_exec, synopsis, descrip);
252 res |= ast_register_application(app2, pickupchan_exec, synopsis2, descrip2);
254 return res;
257 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");