66203b1b04d98f60ef754f9277a2525c9caa8db7
[asterisk/asterisk.git] / apps / app_parkandannounce.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * Author: Ben Miller <bgmiller@dccinc.com>
9  *    With TONS of help from Mark!
10  *
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.
16  *
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.
20  */
21
22 /*! \file
23  *
24  * \brief ParkAndAnnounce application for Asterisk
25  *
26  * \author Ben Miller <bgmiller@dccinc.com>
27  * \arg With TONS of help from Mark!
28  *
29  * \ingroup applications
30  */
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <unistd.h>
40 #include <sys/types.h>
41
42 #include "asterisk/file.h"
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/features.h"
48 #include "asterisk/options.h"
49 #include "asterisk/logger.h"
50 #include "asterisk/say.h"
51 #include "asterisk/lock.h"
52 #include "asterisk/utils.h"
53
54 static char *app = "ParkAndAnnounce";
55
56 static char *synopsis = "Park and Announce";
57
58 static char *descrip =
59 "  ParkAndAnnounce(announce:template|timeout|dial|[return_context]):\n"
60 "Park a call into the parkinglot and announce the call to another channel.\n"
61 "\n"
62 "announce template: Colon-separated list of files to announce.  The word PARKED\n"
63 "                   will be replaced by a say_digits of the extension in which\n"
64 "                   the call is parked.\n"
65 "timeout:           Time in seconds before the call returns into the return\n"
66 "                   context.\n"
67 "dial:              The app_dial style resource to call to make the\n"
68 "                   announcement.  Console/dsp calls the console.\n"
69 "return_context:    The goto-style label to jump the call back into after\n"
70 "                   timeout.  Default <priority+1>.\n"
71 "\n"
72 "The variable ${PARKEDAT} will contain the parking extension into which the\n"
73 "call was placed.  Use with the Local channel to allow the dialplan to make\n"
74 "use of this information.\n";
75
76
77 static int parkandannounce_exec(struct ast_channel *chan, void *data)
78 {
79         int res=0;
80         char *return_context;
81         int l, lot, timeout = 0, dres;
82         char *working, *context, *exten, *priority, *dial, *dialtech, *dialstr;
83         char *template, *tpl_working, *tpl_current;
84         char *tmp[100];
85         char buf[13];
86         int looptemp=0,i=0;
87         char *s,*orig_s;
88
89         struct ast_channel *dchan;
90         struct outgoing_helper oh;
91         int outstate;
92
93         struct ast_module_user *u;
94
95         if (ast_strlen_zero(data)) {
96                 ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
97                 return -1;
98         }
99   
100         u = ast_module_user_add(chan);
101
102         l=strlen(data)+2;       
103         if (!(orig_s = ast_malloc(l))) {
104                 ast_module_user_remove(u);
105                 return -1;
106         }
107         s=orig_s;
108         strncpy(s,data,l);
109
110         template=strsep(&s,"|");
111         if(! template) {
112                 ast_log(LOG_WARNING, "PARK: An announce template must be defined\n");
113                 free(orig_s);
114                 ast_module_user_remove(u);
115                 return -1;
116         }
117   
118         if(s) {
119                 timeout = atoi(strsep(&s, "|"));
120                 timeout *= 1000;
121         }
122         dial=strsep(&s, "|");
123         if(!dial) {
124                 ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n");
125                 free(orig_s);
126                 ast_module_user_remove(u);
127                 return -1;
128         } else {
129                 dialtech=strsep(&dial, "/");
130                 dialstr=dial;
131                 ast_verbose( VERBOSE_PREFIX_3 "Dial Tech,String: (%s,%s)\n", dialtech,dialstr);
132         }
133
134         return_context = s;
135   
136         if(return_context != NULL) {
137                 /* set the return context. Code borrowed from the Goto builtin */
138     
139                 working = return_context;
140                 context = strsep(&working, "|");
141                 exten = strsep(&working, "|");
142                 if(!exten) {
143                         /* Only a priority in this one */
144                         priority = context;
145                         exten = NULL;
146                         context = NULL;
147                 } else {
148                         priority = strsep(&working, "|");
149                         if(!priority) {
150                                 /* Only an extension and priority in this one */
151                                 priority = exten;
152                                 exten = context;
153                                 context = NULL;
154                 }
155         }
156         if(atoi(priority) < 0) {
157                 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority);
158                 free(orig_s);
159                 ast_module_user_remove(u);
160                 return -1;
161         }
162         /* At this point we have a priority and maybe an extension and a context */
163         chan->priority = atoi(priority);
164         if (exten)
165                 strncpy(chan->exten, exten, sizeof(chan->exten)-1);
166         if (context)
167                 strncpy(chan->context, context, sizeof(chan->context)-1);
168         } else {  /* increment the priority by default*/
169                 chan->priority++;
170         }
171
172         if(option_verbose > 2) {
173                 ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->cid.cid_num);
174                 if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
175                         ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n");
176                 }
177         }
178   
179         /* we are using masq_park here to protect * from touching the channel once we park it.  If the channel comes out of timeout
180         before we are done announcing and the channel is messed with, Kablooeee.  So we use Masq to prevent this.  */
181
182         ast_masq_park_call(chan, NULL, timeout, &lot);
183
184         res=-1; 
185
186         ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context);
187
188         /* Now place the call to the extention */
189
190         snprintf(buf, sizeof(buf), "%d", lot);
191         memset(&oh, 0, sizeof(oh));
192         oh.parent_channel = chan;
193         oh.vars = ast_variable_new("_PARKEDAT", buf);
194         dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
195
196         if(dchan) {
197                 if(dchan->_state == AST_STATE_UP) {
198                         if(option_verbose > 3)
199                                 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", dchan->name);
200                 } else {
201                         if(option_verbose > 3)
202                                 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", dchan->name);
203                                 ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
204                         ast_hangup(dchan);
205                         free(orig_s);
206                         ast_module_user_remove(u);
207                         return -1;
208                 }
209         } else {
210                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
211                 free(orig_s);
212                 ast_module_user_remove(u);
213                 return -1; 
214         }
215
216         ast_stopstream(dchan);
217
218         /* now we have the call placed and are ready to play stuff to it */
219
220         ast_verbose(VERBOSE_PREFIX_4 "Announce Template:%s\n", template);
221
222         tpl_working = template;
223         tpl_current=strsep(&tpl_working, ":");
224
225         while(tpl_current && looptemp < sizeof(tmp)) {
226                 tmp[looptemp]=tpl_current;
227                 looptemp++;
228                 tpl_current=strsep(&tpl_working,":");
229         }
230
231         for(i=0; i<looptemp; i++) {
232                 ast_verbose(VERBOSE_PREFIX_4 "Announce:%s\n", tmp[i]);
233                 if(!strcmp(tmp[i], "PARKED")) {
234                         ast_say_digits(dchan, lot, "", dchan->language);
235                 } else {
236                         dres = ast_streamfile(dchan, tmp[i], dchan->language);
237                         if(!dres) {
238                                 dres = ast_waitstream(dchan, "");
239                         } else {
240                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
241                                 dres = 0;
242                         }
243                 }
244         }
245
246         ast_stopstream(dchan);  
247         ast_hangup(dchan);
248         free(orig_s);
249         
250         ast_module_user_remove(u);
251         
252         return res;
253 }
254
255 static int unload_module(void)
256 {
257         int res;
258
259         res = ast_unregister_application(app);
260
261         ast_module_user_hangup_all();
262
263         return res;
264 }
265
266 static int load_module(void)
267 {
268         /* return ast_register_application(app, park_exec); */
269         return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
270 }
271
272 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");