dom mar 16 23:37:23 CET 2003
[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, Mark Spencer
10  *
11  * Mark Spencer <markster@linux-support.net>
12  *
13  * This program is free software, distributed under the terms of
14  * the GNU General Public License
15  */
16
17 #include <asterisk/file.h>
18 #include <asterisk/logger.h>
19 #include <asterisk/channel.h>
20 #include <asterisk/channel_pvt.h>
21 #include <asterisk/pbx.h>
22 #include <asterisk/module.h>
23 #include <asterisk/parking.h>
24 #include <asterisk/options.h>
25 #include <asterisk/logger.h>
26 #include <asterisk/say.h>
27
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <stdlib.h>
32
33 #include <pthread.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, 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) {
71     ast_log(LOG_WARNING, "Park requires an argument (parkinglot)\n");
72     return -1;
73   }
74   
75   l=strlen(data)+2;
76   orig_s=malloc(l);
77   s=orig_s;
78   strncpy(s,data,l);
79
80   template=strsep(&s,"|");
81   if (! template) {
82     ast_log(LOG_WARNING, "PARK: An announce template must be defined\n");
83     free(orig_s);
84     return -1;
85   }
86   
87   timeout = atoi(strsep(&s, "|"));
88   timeout *= 1000;
89
90   dial=strsep(&s, "|");
91   if (! dial) {
92     ast_log(LOG_WARNING, "PARK: A dial resouce must be specified i.e: Console/dsp or Zap/g1/5551212\n");
93     free(orig_s);
94     return -1;
95   }
96   else {
97     dialtech=strsep(&dial, "/");
98     dialstr=dial;
99     ast_verbose( VERBOSE_PREFIX_3 "Dial Tech,String: (%s,%s)\n", dialtech,dialstr);
100   }
101
102   return_context = s;
103   
104   if (return_context != NULL) {
105     /* set the return context. Code borrowed from the Goto builtin */
106     
107     working = return_context;
108     context = strsep(&working, "|");
109     exten = strsep(&working, "|");
110     if (!exten) {
111       /* Only a priority in this one */
112       priority = context;
113       exten = NULL;
114       context = NULL;
115     } else {
116       priority = strsep(&working, "|");
117       if (!priority) {
118         /* Only an extension and priority in this one */
119         priority = exten;
120         exten = context;
121         context = NULL;
122       }
123     }
124     if (atoi(priority) < 0) {
125       ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", priority);
126       free(orig_s);
127       return -1;
128     }
129     /* At this point we have a priority and maybe an extension and a context */
130     chan->priority = atoi(priority);
131     if (exten && strcasecmp(exten, "BYEXTENSION"))
132       strncpy(chan->exten, exten, sizeof(chan->exten)-1);
133     if (context)
134       strncpy(chan->context, context, sizeof(chan->context)-1);
135   }
136   else {  /* increment the priority by default*/
137     chan->priority++;
138   }
139
140
141   if (option_verbose > 2) {
142     ast_verbose( VERBOSE_PREFIX_3 "Return Context: (%s,%s,%d) ID: %s\n", chan->context,chan->exten, chan->priority, chan->callerid);
143     if (!ast_exists_extension(chan, chan->context, chan->exten, chan->priority, chan->callerid)) {
144       ast_verbose( VERBOSE_PREFIX_3 "Warning: Return Context Invalid, call will return to default|s\n");
145     }
146   }
147   
148   LOCAL_USER_ADD(u);
149
150   /* we are using masq_park here to protect * from touching the channel once we park it.  If the channel comes out of timeout
151      before we are done announcing and the channel is messed with, Kablooeee.  So we use Masq to prevent this.  */
152
153   ast_masq_park_call(chan, NULL, timeout, &lot);
154
155   res=-1; 
156
157   ast_verbose( VERBOSE_PREFIX_3 "Call Parking Called, lot: %d, timeout: %d, context: %s\n", lot, timeout, return_context);
158
159   /* Now place the call to the extention */
160
161   dchan = ast_request_and_dial(dialtech, AST_FORMAT_SLINEAR, dialstr,30000, &outstate, chan->callerid);
162
163   if (dchan) {
164     if (dchan->_state == AST_STATE_UP) {
165       if (option_verbose > 3)
166         ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", dchan->name);
167     } else {
168       if (option_verbose > 3)
169         ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", dchan->name);
170         ast_log(LOG_WARNING, "PARK: Channel %s was never answered for the announce.\n", dchan->name);
171       ast_hangup(dchan);
172       free(orig_s);
173       LOCAL_USER_REMOVE(u);
174       return -1;
175     }
176   }
177   else {
178     ast_log(LOG_WARNING, "PARK: Unable to allocate announce channel.\n");
179     free(orig_s);
180     LOCAL_USER_REMOVE(u);
181     return -1; 
182   }
183
184   ast_stopstream(dchan);
185
186   /* now we have the call placed and are ready to play stuff to it */
187
188   ast_verbose(VERBOSE_PREFIX_4 "Announce Template:%s\n", template);
189
190   tpl_working = template;
191   tpl_current=strsep(&tpl_working, ":");
192
193   while ( tpl_current && looptemp < sizeof(tmp)) {
194     tmp[looptemp]=tpl_current;
195     looptemp++;
196     tpl_current=strsep(&tpl_working,":");
197   }
198
199   for(i=0; i<looptemp; i++) {
200     ast_verbose(VERBOSE_PREFIX_4 "Announce:%s\n", tmp[i]);
201     if (!strcmp(tmp[i], "PARKED")) {
202       ast_say_digits(dchan, lot, "", dchan->language);
203     }
204     else {
205       dres = ast_streamfile(dchan, tmp[i], dchan->language);
206       if (!dres)
207         dres = ast_waitstream(dchan, "");
208       else {
209         ast_log(LOG_WARNING, "ast_streamfile of %s failed on %s\n", tmp[i], dchan->name);
210         dres = 0;
211       }
212     }
213   }
214
215   ast_stopstream(dchan);  
216   ast_hangup(dchan);
217
218   LOCAL_USER_REMOVE(u);
219   free(orig_s);
220   return res;
221 }
222
223
224
225 int unload_module(void)
226 {
227         STANDARD_HANGUP_LOCALUSERS;
228         return ast_unregister_application(app);
229 }
230
231 int load_module(void)
232 {
233   /*    return ast_register_application(app, park_exec); */
234   return ast_register_application(app, parkandannounce_exec, synopsis, descrip);
235 }
236
237 char *description(void)
238 {
239         return tdesc;
240 }
241
242 int usecount(void)
243 {
244         int res;
245         STANDARD_USECOUNT(res);
246         return res;
247 }
248
249 char *key()
250 {
251         return ASTERISK_GPL_KEY;
252 }