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