fe63da9e1a83a71f1e04d5e6f49976a261907130
[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 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;
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         s = ast_strdupa(data);
103
104         template=strsep(&s,"|");
105         if(! template) {
106                 ast_log(LOG_WARNING, "PARK: An announce template must be defined\n");
107                 ast_module_user_remove(u);
108                 return -1;
109         }
110   
111         if(s) {
112                 timeout = atoi(strsep(&s, "|"));
113                 timeout *= 1000;
114         }
115         dial=strsep(&s, "|");
116         if(!dial) {
117                 ast_log(LOG_WARNING, "PARK: A dial resource must be specified i.e: Console/dsp or Zap/g1/5551212\n");
118                 ast_module_user_remove(u);
119                 return -1;
120         } else {
121                 dialtech=strsep(&dial, "/");
122                 dialstr=dial;
123                 ast_verbose( VERBOSE_PREFIX_3 "Dial Tech,String: (%s,%s)\n", dialtech,dialstr);
124         }
125
126         return_context = s;
127   
128         if(return_context != NULL) {
129                 /* set the return context. Code borrowed from the Goto builtin */
130     
131                 working = return_context;
132                 context = strsep(&working, "|");
133                 exten = strsep(&working, "|");
134                 if(!exten) {
135                         /* Only a priority in this one */
136                         priority = context;
137                         exten = NULL;
138                         context = NULL;
139                 } else {
140                         priority = strsep(&working, "|");
141                         if(!priority) {
142                                 /* Only an extension and priority in this one */
143                                 priority = exten;
144                                 exten = context;
145                                 context = NULL;
146                 }
147         }
148         if(atoi(priority) < 0) {
149                 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority);
150                 ast_module_user_remove(u);
151                 return -1;
152         }
153         /* At this point we have a priority and maybe an extension and a context */
154         chan->priority = atoi(priority);
155         if (exten)
156                 ast_copy_string(chan->exten, exten, sizeof(chan->exten));
157         if (context)
158                 ast_copy_string(chan->context, context, sizeof(chan->context));
159         } else {  /* increment the priority by default*/
160                 chan->priority++;
161         }
162
163         if(option_verbose > 2) {
164                 ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->cid.cid_num);
165                 if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
166                         ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n");
167                 }
168         }
169   
170         /* we are using masq_park here to protect * from touching the channel once we park it.  If the channel comes out of timeout
171         before we are done announcing and the channel is messed with, Kablooeee.  So we use Masq to prevent this.  */
172
173         ast_masq_park_call(chan, NULL, timeout, &lot);
174
175         res=-1; 
176
177         ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context);
178
179         /* Now place the call to the extention */
180
181         snprintf(buf, sizeof(buf), "%d", lot);
182         memset(&oh, 0, sizeof(oh));
183         oh.parent_channel = chan;
184         oh.vars = ast_variable_new("_PARKEDAT", buf);
185         dchan = __ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->cid.cid_num, chan->cid.cid_name, &oh);
186
187         if(dchan) {
188                 if(dchan->_state == AST_STATE_UP) {
189                         if(option_verbose > 3)
190                                 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", dchan->name);
191                 } else {
192                         if(option_verbose > 3)
193                                 ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", dchan->name);
194                                 ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
195                         ast_hangup(dchan);
196                         ast_module_user_remove(u);
197                         return -1;
198                 }
199         } else {
200                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
201                 ast_module_user_remove(u);
202                 return -1; 
203         }
204
205         ast_stopstream(dchan);
206
207         /* now we have the call placed and are ready to play stuff to it */
208
209         ast_verbose(VERBOSE_PREFIX_4 "Announce Template:%s\n", template);
210
211         tpl_working = template;
212         tpl_current=strsep(&tpl_working, ":");
213
214         while(tpl_current && looptemp < sizeof(tmp)) {
215                 tmp[looptemp]=tpl_current;
216                 looptemp++;
217                 tpl_current=strsep(&tpl_working,":");
218         }
219
220         for(i=0; i<looptemp; i++) {
221                 ast_verbose(VERBOSE_PREFIX_4 "Announce:%s\n", tmp[i]);
222                 if(!strcmp(tmp[i], "PARKED")) {
223                         ast_say_digits(dchan, lot, "", dchan->language);
224                 } else {
225                         dres = ast_streamfile(dchan, tmp[i], dchan->language);
226                         if(!dres) {
227                                 dres = ast_waitstream(dchan, "");
228                         } else {
229                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
230                                 dres = 0;
231                         }
232                 }
233         }
234
235         ast_stopstream(dchan);  
236         ast_hangup(dchan);
237         
238         ast_module_user_remove(u);
239         
240         return res;
241 }
242
243 static int unload_module(void)
244 {
245         return ast_unregister_application(app);
246 }
247
248 static int load_module(void)
249 {
250         /* return ast_register_application(app, park_exec); */
251         return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
252 }
253
254 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Call Parking and Announce Application");