Add PICKUPMARK support to app_directed_pickup (issue #7104 reported by thaeger)
[asterisk/asterisk.git] / apps / app_directed_pickup.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2005, Joshua Colp
5  *
6  * Joshua Colp <jcolp@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Directed Call Pickup Support
22  *
23  * \author Joshua Colp <jcolp@digium.com>
24  *
25  * \ingroup applications
26  */
27
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "asterisk.h"
34
35 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36
37 #include "asterisk/file.h"
38 #include "asterisk/logger.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/pbx.h"
41 #include "asterisk/module.h"
42 #include "asterisk/lock.h"
43 #include "asterisk/app.h"
44
45 #define PICKUPMARK "PICKUPMARK"
46
47 static const char *app = "Pickup";
48 static const char *synopsis = "Directed Call Pickup";
49 static const char *descrip =
50 "  Pickup(extension[@context][&extension2@context...]): This application can pickup any ringing channel\n"
51 "that is calling the specified extension. If no context is specified, the current\n"
52 "context will be used. If you use the special string \"PICKUPMARK\" for the context parameter, for example\n"
53 "10@PICKUPMARK, this application tries to find a channel which has defined a channel variable with the same context\n"
54 "as \"extension\".";
55
56 LOCAL_USER_DECL;
57
58 static int pickup_exec(struct ast_channel *chan, void *data)
59 {
60         int res = 0;
61         struct localuser *u = NULL;
62         struct ast_channel *origin = NULL, *target = NULL;
63         char *tmp = NULL, *exten = NULL, *context = NULL, *rest=data;
64         char workspace[256] = "";
65         const char *tmp2 = NULL;
66
67         if (ast_strlen_zero(data)) {
68                 ast_log(LOG_WARNING, "Pickup requires an argument (extension) !\n");
69                 return -1;      
70         }
71
72         LOCAL_USER_ADD(u);
73         
74         while (!target && (exten = rest) ) {
75                 res = 0;
76                 rest = strchr(exten, '&');
77                 if (rest)
78                         *rest++ = 0;
79
80                 /* Get the extension and context if present */
81                 context = strchr(exten, '@');
82                 if (context)
83                         *context++ = '\0';
84
85                 /* If the context is the pickup mark, iterate through all channels finding the right origin one */
86                 if (!strcmp(context, PICKUPMARK)) {
87                         while ((origin = ast_channel_walk_locked(origin))) {
88                                 if (origin) {
89                                         tmp2 = pbx_builtin_getvar_helper(origin, PICKUPMARK);
90                                         if (tmp2 && !strcmp(tmp2, exten))
91                                                 break;
92                                         ast_mutex_unlock(&origin->lock);
93                                 }
94                         }
95                 } else {
96                         /* Use the classic mode of searching */
97                         origin = ast_get_channel_by_exten_locked(exten, context);
98                 }
99
100                 if (origin) {
101                         ast_cdr_getvar(origin->cdr, "dstchannel", &tmp, workspace,
102                                         sizeof(workspace), 0, 0);
103                         if (tmp) {
104                                 /* We have a possible channel... now we need to find it! */
105                                 target = ast_get_channel_by_name_locked(tmp);
106                         } else {
107                                 ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten);
108                                 res = -1;
109                         }
110                         ast_mutex_unlock(&origin->lock);
111
112                 } else {
113                         ast_log(LOG_DEBUG, "No originating channel found.\n");
114                 }
115
116                 if (res)
117                         continue;
118
119                 if (target && (!target->pbx) && ((target->_state == AST_STATE_RINGING) || (target->_state == AST_STATE_RING) ) ) {
120                         ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n", target->name,
121                                         chan->name);
122                         res = ast_answer(chan);
123                         if (res) {
124                                 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
125                                 res = -1;
126                                 break;
127                         }
128                         res = ast_queue_control(chan, AST_CONTROL_ANSWER);
129                         if (res) {
130                                 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n",
131                                                 chan->name);
132                                 res = -1;
133                                 break;
134                         }
135                         res = ast_channel_masquerade(target, chan);
136                         if (res) {
137                                 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
138                                 res = -1;
139                                 break;
140                         }
141                 } else {
142                         ast_log(LOG_NOTICE, "No call pickup possible for %s...\n", exten);
143                         res = -1;
144                 }
145         }
146         if (target) 
147                 ast_mutex_unlock(&target->lock);
148         
149         LOCAL_USER_REMOVE(u);
150
151         return res;
152 }
153
154 static int unload_module(void *mod)
155 {
156         int res;
157
158         res = ast_unregister_application(app);
159         
160         return res;
161 }
162
163 static int load_module(void *mod)
164 {
165         __mod_desc = mod;
166         return ast_register_application(app, pickup_exec, synopsis, descrip);
167 }
168
169 static const char *description(void)
170 {
171         return "Directed Call Pickup Application";
172 }
173
174 static const char *key(void)
175 {
176         return ASTERISK_GPL_KEY;
177 }
178
179 STD_MOD(MOD_1, NULL, NULL, NULL);