a40c2efd7313f1a2eadc05587f2d23e8021e95e2
[asterisk/asterisk.git] / apps / app_parkandannounce.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, 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  * \ingroup applications
27  */
28
29 #include <sys/types.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <string.h>
33
34 #include "asterisk.h"
35
36 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
37
38 #include "asterisk/file.h"
39 #include "asterisk/logger.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/module.h"
43 #include "asterisk/features.h"
44 #include "asterisk/options.h"
45 #include "asterisk/logger.h"
46 #include "asterisk/say.h"
47 #include "asterisk/lock.h"
48
49 static char *tdesc = "Call Parking and Announce Application";
50
51 static char *app = "ParkAndAnnounce";
52
53 static char *synopsis = "Park and Announce";
54
55 static char *descrip =
56 "  ParkAndAnnounce(announce:template|timeout|dial|[return_context]):\n"
57 "Park a call into the parkinglot and announce the call over the console.\n"
58 "announce template: colon separated list of files to announce, the word PARKED\n"
59 "                   will be replaced by a say_digits of the ext the call is parked in\n"
60 "timeout: time in seconds before the call returns into the return context.\n"
61 "dial: The app_dial style resource to call to make the announcement. Console/dsp calls the console.\n"
62 "return_context: the goto style label to jump the call back into after timeout. default=prio+1\n";
63
64
65 STANDARD_LOCAL_USER;
66
67 LOCAL_USER_DECL;
68
69 static int parkandannounce_exec(struct ast_channel *chan, void *data)
70 {
71         int res=0;
72         char *return_context;
73         int l, lot, timeout = 0, dres;
74         char *working, *context, *exten, *priority, *dial, *dialtech, *dialstr;
75         char *template, *tpl_working, *tpl_current;
76         char *tmp[100];
77         int looptemp=0,i=0;
78         char *s,*orig_s;
79
80         struct ast_channel *dchan;
81         struct outgoing_helper oh;
82         int outstate;
83
84         struct localuser *u;
85
86         if (ast_strlen_zero(data)) {
87                 ast_log(LOG_WARNING, "ParkAndAnnounce requires arguments: (announce:template|timeout|dial|[return_context])\n");
88                 return -1;
89         }
90   
91         LOCAL_USER_ADD(u);
92
93         l=strlen(data)+2;
94         orig_s=malloc(l);
95         if(!orig_s) {
96                 ast_log(LOG_WARNING, "Out of memory\n");
97                 LOCAL_USER_REMOVE(u);
98                 return -1;
99         }
100         s=orig_s;
101         strncpy(s,data,l);
102
103         template=strsep(&s,"|");
104         if(! template) {
105                 ast_log(LOG_WARNING, "PARK: An announce template must be defined\n");
106                 free(orig_s);
107                 LOCAL_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                 free(orig_s);
119                 LOCAL_USER_REMOVE(u);
120                 return -1;
121         } else {
122                 dialtech=strsep(&dial, "/");
123                 dialstr=dial;
124                 ast_verbose( VERBOSE_PREFIX_3 "Dial Tech,String: (%s,%s)\n", dialtech,dialstr);
125         }
126
127         return_context = s;
128   
129         if(return_context != NULL) {
130                 /* set the return context. Code borrowed from the Goto builtin */
131     
132                 working = return_context;
133                 context = strsep(&working, "|");
134                 exten = strsep(&working, "|");
135                 if(!exten) {
136                         /* Only a priority in this one */
137                         priority = context;
138                         exten = NULL;
139                         context = NULL;
140                 } else {
141                         priority = strsep(&working, "|");
142                         if(!priority) {
143                                 /* Only an extension and priority in this one */
144                                 priority = exten;
145                                 exten = context;
146                                 context = NULL;
147                 }
148         }
149         if(atoi(priority) < 0) {
150                 ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority);
151                 free(orig_s);
152                 LOCAL_USER_REMOVE(u);
153                 return -1;
154         }
155         /* At this point we have a priority and maybe an extension and a context */
156         chan->priority = atoi(priority);
157         if(exten && strcasecmp(exten, "BYEXTENSION"))
158                 strncpy(chan->exten, exten, sizeof(chan->exten)-1);
159         if(context)
160                 strncpy(chan->context, context, sizeof(chan->context)-1);
161         } else {  /* increment the priority by default*/
162                 chan->priority++;
163         }
164
165         if(option_verbose > 2) {
166                 ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->cid.cid_num);
167                 if(!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->cid.cid_num)) {
168                         ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n");
169                 }
170         }
171   
172         /* we are using masq_park here to protect * from touching the channel once we park it.  If the channel comes out of timeout
173         before we are done announcing and the channel is messed with, Kablooeee.  So we use Masq to prevent this.  */
174
175         ast_masq_park_call(chan, NULL, timeout, &lot);
176
177         res=-1; 
178
179         ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context);
180
181         /* Now place the call to the extention */
182
183         memset(&oh, 0, sizeof(oh));
184         oh.parent_channel = chan;
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                         free(orig_s);
197                         LOCAL_USER_REMOVE(u);
198                         return -1;
199                 }
200         } else {
201                 ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
202                 free(orig_s);
203                 LOCAL_USER_REMOVE(u);
204                 return -1; 
205         }
206
207         ast_stopstream(dchan);
208
209         /* now we have the call placed and are ready to play stuff to it */
210
211         ast_verbose(VERBOSE_PREFIX_4 "Announce Template:%s\n", template);
212
213         tpl_working = template;
214         tpl_current=strsep(&tpl_working, ":");
215
216         while(tpl_current && looptemp < sizeof(tmp)) {
217                 tmp[looptemp]=tpl_current;
218                 looptemp++;
219                 tpl_current=strsep(&tpl_working,":");
220         }
221
222         for(i=0; i<looptemp; i++) {
223                 ast_verbose(VERBOSE_PREFIX_4 "Announce:%s\n", tmp[i]);
224                 if(!strcmp(tmp[i], "PARKED")) {
225                         ast_say_digits(dchan, lot, "", dchan->language);
226                 } else {
227                         dres = ast_streamfile(dchan, tmp[i], dchan->language);
228                         if(!dres) {
229                                 dres = ast_waitstream(dchan, "");
230                         } else {
231                                 ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
232                                 dres = 0;
233                         }
234                 }
235         }
236
237         ast_stopstream(dchan);  
238         ast_hangup(dchan);
239         free(orig_s);
240         
241         LOCAL_USER_REMOVE(u);
242         
243         return res;
244 }
245
246
247
248 int unload_module(void)
249 {
250         int res;
251
252         res = ast_unregister_application(app);
253
254         STANDARD_HANGUP_LOCALUSERS;
255
256         return res;
257 }
258
259 int load_module(void)
260 {
261         /* return ast_register_application(app, park_exec); */
262         return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
263 }
264
265 char *description(void)
266 {
267         return tdesc;
268 }
269
270 int usecount(void)
271 {
272         int res;
273         STANDARD_USECOUNT(res);
274         return res;
275 }
276
277 char *key()
278 {
279         return ASTERISK_GPL_KEY;
280 }