merge new_loader_completion branch, including (at least):
[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 "asterisk.h"
29
30 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <unistd.h>
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
57 static int pickup_exec(struct ast_channel *chan, void *data)
58 {
59         int res = 0;
60         struct ast_module_user *u = NULL;
61         struct ast_channel *origin = NULL, *target = NULL;
62         char *tmp = NULL, *exten = NULL, *context = NULL, *rest=data;
63         char workspace[256] = "";
64         const char *tmp2 = NULL;
65
66         if (ast_strlen_zero(data)) {
67                 ast_log(LOG_WARNING, "Pickup requires an argument (extension) !\n");
68                 return -1;      
69         }
70
71         u = ast_module_user_add(chan);
72         
73         while (!target && (exten = rest) ) {
74                 res = 0;
75                 rest = strchr(exten, '&');
76                 if (rest)
77                         *rest++ = 0;
78
79                 /* Get the extension and context if present */
80                 context = strchr(exten, '@');
81                 if (context)
82                         *context++ = '\0';
83
84                 /* If the context is the pickup mark, iterate through all channels finding the right origin one */
85                 if (context && !strcmp(context, PICKUPMARK)) {
86                         while ((origin = ast_channel_walk_locked(origin))) {
87                                 if (origin) {
88                                         tmp2 = pbx_builtin_getvar_helper(origin, PICKUPMARK);
89                                         if (tmp2 && !strcmp(tmp2, exten))
90                                                 break;
91                                         ast_mutex_unlock(&origin->lock);
92                                 }
93                         }
94                 } else {
95                         /* Use the classic mode of searching */
96                         origin = ast_get_channel_by_exten_locked(exten, context);
97                 }
98
99                 if (origin) {
100                         ast_cdr_getvar(origin->cdr, "dstchannel", &tmp, workspace,
101                                         sizeof(workspace), 0, 0);
102                         if (tmp) {
103                                 /* We have a possible channel... now we need to find it! */
104                                 target = ast_get_channel_by_name_locked(tmp);
105                         } else {
106                                 ast_log(LOG_NOTICE, "No target channel found for %s.\n", exten);
107                                 res = -1;
108                         }
109                         ast_mutex_unlock(&origin->lock);
110
111                 } else {
112                         ast_log(LOG_DEBUG, "No originating channel found.\n");
113                 }
114
115                 if (res)
116                         continue;
117
118                 if (target && (!target->pbx) && ((target->_state == AST_STATE_RINGING) || (target->_state == AST_STATE_RING) ) ) {
119                         ast_log(LOG_DEBUG, "Call pickup on chan '%s' by '%s'\n", target->name,
120                                         chan->name);
121                         res = ast_answer(chan);
122                         if (res) {
123                                 ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan->name);
124                                 res = -1;
125                                 break;
126                         }
127                         res = ast_queue_control(chan, AST_CONTROL_ANSWER);
128                         if (res) {
129                                 ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n",
130                                                 chan->name);
131                                 res = -1;
132                                 break;
133                         }
134                         res = ast_channel_masquerade(target, chan);
135                         if (res) {
136                                 ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan->name, target->name);
137                                 res = -1;
138                                 break;
139                         }
140                 } else {
141                         ast_log(LOG_NOTICE, "No call pickup possible for %s...\n", exten);
142                         res = -1;
143                 }
144         }
145         if (target) 
146                 ast_mutex_unlock(&target->lock);
147         
148         ast_module_user_remove(u);
149
150         return res;
151 }
152
153 static int unload_module(void)
154 {
155         int res;
156
157         res = ast_unregister_application(app);
158         
159         return res;
160 }
161
162 static int load_module(void)
163 {
164         return ast_register_application(app, pickup_exec, synopsis, descrip);
165 }
166
167 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Directed Call Pickup Application");