Version 0.2.0 from FTP
[asterisk/asterisk.git] / apps / app_qcall.c
1 /** @file app_qcall.c 
2  *
3  * Asterisk -- A telephony toolkit for Linux.
4  *
5  * Call back a party and connect them to a running pbx thread
6  * 
7  * Copyright (C) 1999, Mark Spencer
8  *
9  * Mark Spencer <markster@linux-support.net>
10  *
11  * This program is free software, distributed under the terms of
12  * the GNU General Public License
13  *
14  * Call a user from a file contained within a queue (/var/spool/asterisk/qcall)
15  * 
16  * The queue is a directory containing files with the call request information
17  * as a single line of text as follows:
18  * 
19  * Dialstring Caller-ID Extension Maxsecs [Identifier] [Required-response]
20  *
21  *  Dialstring -- A Dial String (The number to be called) in the
22  *  format Technology/Number, such IAX/mysys/1234 or Zap/g1/1234
23  * 
24  *  Caller-ID -- A Standard nomalized representation of the Caller-ID of
25  *  the number being dialed (generally 10 digits in the US). Leave as
26  *  "asreceived" to use the default Caller*ID
27  *
28  *  Extension -- The Extension (optionally Extension@context) that the
29  *  user should be "transferred" to after acceptance of the call.
30  *
31  *  Maxsecs -- The Maximum time of the call in seconds. Specify 0 for infinite.
32  *
33  *  Identifier -- The "Identifier" of the request. This is used to determine
34  *  the names of the audio prompt files played. The first prompt, the one that
35  *  asks for the input, is just the exact string specified as the identifier.
36  *  The second prompt, the one that is played after the correct input is given,
37  *  (generally a "thank you" recording), is the specified string with "-ok" 
38  *  added to the end. So, if you specify "foo" as the identifier, your first
39  *  prompt file that will be played will be "foo" and the second one will be
40  *  "foo-ok".  If omitted no prompt is given
41  *
42  *  Required-Response (Optional) -- Specify a digit string to be used as the
43  *  acceptance "code" if you desire it to be something other then "1". This
44  *  can be used to implement some sort of PIN or security system. It may be
45  *  more then a single character.
46  *
47  * NOTE: It is important to remember that the process that creates these
48  * files needs keep and maintain a write lock (using flock with the LOCK_EX
49  * option) when writing these files.
50  *
51  */
52  
53 #include <asterisk/lock.h>
54 #include <asterisk/file.h>
55 #include <asterisk/logger.h>
56 #include <asterisk/channel.h>
57 #include <asterisk/pbx.h>
58 #include <asterisk/module.h>
59 #include <asterisk/translate.h>
60 #include <asterisk/options.h>
61 #include <stdio.h>
62 #include <unistd.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <pthread.h>
66 #include <sys/types.h>
67 #include <sys/stat.h>
68 #include <errno.h>
69 #include <dirent.h>
70 #include <ctype.h>
71 #include <sys/stat.h>
72 #include <sys/time.h>
73 #include <sys/file.h>
74
75 const   char *qdir="/var/spool/asterisk/qcall";
76 static  char *tdesc = "Call from Queue";
77 static  pthread_t qcall_thread;
78 static int debug = 0;
79 STANDARD_LOCAL_USER;
80 LOCAL_USER_DECL;
81
82 #define OLDESTOK        14400           /* not any more then this number of secs old */
83 #define INITIALONE      1               /* initial wait before the first one in secs */
84 #define NEXTONE         600             /* wait before trying it again in secs */
85 #define MAXWAITFORANSWER 45000          /* max call time before answer */
86 /* define either one or both of these two if your application requires it */
87 #if     0
88 #define ACCTCODE        "SOMETHING"     /* Account code */
89 #define AMAFLAGS AST_CDR_BILLING        /* AMA flags */
90 #endif
91 /* define this if you want to have a particular CLID display on the user's
92    phone when they receive the call */
93 #if     0
94 #define OURCLID "2564286275"            /* The callerid to be displayed when calling */
95 #endif
96
97 static void *qcall_do(void *arg);
98
99 static void *qcall(void *ignore)
100 {
101 pthread_t dialer_thread;
102 DIR *dirp;
103 FILE *fp;
104 struct dirent *dp;
105 char fname[80];
106 struct stat mystat;
107 time_t  t;
108 void *arg;
109 pthread_attr_t attr;
110
111         time(&t);
112         if (debug) printf("@@@@ qcall starting at %s",ctime(&t));
113         for(;;)
114            {
115                 time(&t);
116                 dirp = opendir(qdir);
117                 if (!dirp)
118                    {
119                         perror("app_qcall:Cannot open queue directory");
120                         break;
121                    }
122                 while((dp = readdir(dirp)) != NULL)
123                    {
124                         if (dp->d_name[0] == '.') continue;
125                         sprintf(fname,"%s/%s",qdir,dp->d_name);
126                         if (stat(fname,&mystat) == -1)
127                            {
128                                 perror("app_qcall:stat");
129                                 fprintf(stderr,"%s\n",fname);
130                                 continue;
131                            }
132                           /* if not a regular file, skip it */
133                         if ((mystat.st_mode & S_IFMT) != S_IFREG) continue;
134                           /* if not yet .... */
135                         if (mystat.st_atime == mystat.st_ctime)
136                            {  /* first time */
137                                 if ((mystat.st_atime + INITIALONE) > t) 
138                                         continue;
139                            }
140                         else
141                            { /* already looked at once */
142                                 if ((mystat.st_atime + NEXTONE) > t) continue;
143                            }
144                           /* if too old */
145                         if (mystat.st_mtime < (t - OLDESTOK))
146                            {
147                                 /* kill it, its too old */
148                                 unlink(fname);
149                                 continue;
150                            }                            
151                          /* "touch" file's access time */
152                         fp = fopen(fname,"r");
153                         if (fp) fclose(fp);
154                         /* make a copy of the filename string, so that we
155                                 may go on and use the buffer */
156                         arg = (void *) strdup(fname);
157                         pthread_attr_init(&attr);
158                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
159                         if (pthread_create(&dialer_thread,&attr,qcall_do,arg) == -1)
160                            {
161                                 perror("qcall: Cannot create thread");
162                                 continue;
163                            }
164                    }
165                 closedir(dirp);
166                 sleep(1);
167            }
168         pthread_exit(NULL);
169 }
170         
171 /* single thread with one file (request) to dial */
172 static void *qcall_do(void *arg)
173 {
174 char fname[300],dialstr[300],extstr[300],ident[300],reqinp[300],buf[300];
175 char clid[300],*tele,*context;
176 FILE *fp;
177 int ms = MAXWAITFORANSWER,maxsecs;
178 struct ast_channel *channel;
179 time_t  t;
180
181           /* get the filename from the arg */
182         strcpy(fname,(char *)arg);
183         free(arg);
184         time(&t);
185         fp = fopen(fname,"r");
186         if (!fp) /* if cannot open request file */
187            {
188                 perror("qcall_do:fopen");
189                 fprintf(stderr,"%s\n",fname);
190                 unlink(fname);
191                 pthread_exit(NULL);
192            }
193         /* lock the file */
194         if (flock(fileno(fp),LOCK_EX) == -1)
195            {
196                 perror("qcall_do:flock");
197                 fprintf(stderr,"%s\n",fname);
198                 pthread_exit(NULL);
199            }
200         strcpy(reqinp,"1");  /* default required input for acknowledgement */
201         strcpy(ident, "");      /* default no ident */
202         if (fscanf(fp,"%s %s %s %d %s %s",dialstr,clid,
203                 extstr,&maxsecs,ident,reqinp) < 4)
204            {
205                 fprintf(stderr,"qcall_do:file line invalid in file %s:\n",fname);
206                 pthread_exit(NULL);
207            }
208         flock(fileno(fp),LOCK_UN);
209         fclose(fp);
210         tele = strchr(dialstr,'/');
211         if (!tele)
212            {
213                 fprintf(stderr,"qcall_do:Dial number must be in format tech/number\n");
214                 unlink(fname);
215                 pthread_exit(NULL);
216            }
217         *tele++ = 0;
218         channel = ast_request(dialstr,AST_FORMAT_SLINEAR,tele);
219         if (channel)
220            {
221                 ast_set_read_format(channel,AST_FORMAT_SLINEAR);
222                 ast_set_write_format(channel,AST_FORMAT_SLINEAR);
223 #ifdef  OURCLID
224                 if (channel->callerid)
225                         free(channel->callerid);
226                 channel->callerid = strdup(OURCLID);
227                 if (channel->ani)
228                         free(channel->ani);
229                 channel->ani = strdup(OURCLID);
230 #endif          
231                 channel->whentohangup = 0;
232                 channel->appl = "AppQcall";
233                 channel->data = "(Outgoing Line)";
234                 if (option_verbose > 2)
235                         ast_verbose(VERBOSE_PREFIX_3 "Qcall initiating call to %s/%s on %s (%s)\n",
236                                 dialstr,tele,channel->name,fname);
237                 ast_call(channel,tele,MAXWAITFORANSWER);
238            }
239         else
240            {
241                 fprintf(stderr,"qcall_do:Sorry unable to obtain channel\n");
242                 pthread_exit(NULL);
243            }
244         if (strcasecmp(clid, "asreceived")) {
245                 if (channel->callerid) free(channel->callerid);
246                 channel->callerid = NULL;
247                 if (channel->ani) free(channel->ani);
248                 channel->ani = NULL;
249         }
250         if (channel->_state == AST_STATE_UP)
251         if (debug) printf("@@@@ Autodial:Line is Up\n");
252         if (option_verbose > 2)
253         ast_verbose(VERBOSE_PREFIX_3 "Qcall waiting for answer on %s\n",
254                 channel->name);
255         while(ms > 0){
256                 struct ast_frame *f;
257                 ms = ast_waitfor(channel,ms);
258                 f = ast_read(channel);
259                 if (!f)
260                    {
261                         if (debug) printf("@@@@ qcall_do:Hung Up\n");
262                         unlink(fname);
263                         break;
264                    }
265                 if (f->frametype == AST_FRAME_CONTROL)
266                    {
267                         if (f->subclass == AST_CONTROL_HANGUP)
268                            {
269                                 if (debug) printf("@@@@ qcall_do:Hung Up\n");
270                                 unlink(fname);
271                                 ast_frfree(f);
272                                 break;
273                            }
274                         if (f->subclass == AST_CONTROL_ANSWER)
275                            {
276                                 if (debug) printf("@@@@ qcall_do:Phone Answered\n");
277                                 if (channel->_state == AST_STATE_UP)
278                                    {
279                                         unlink(fname);
280                                         if (option_verbose > 2)
281                                                 ast_verbose(VERBOSE_PREFIX_3 "Qcall got answer on %s\n",
282                                                         channel->name);
283                                         usleep(1500000);
284                                         if (strlen(ident)) {
285                                                 ast_streamfile(channel,ident,0);
286                                                 if (ast_readstring(channel,buf,strlen(reqinp),10000,5000,"#"))
287                                                 {
288                                                         ast_stopstream(channel);
289                                                         if (debug) printf("@@@@ qcall_do: timeout or hangup in dtmf read\n");
290                                                         ast_frfree(f);
291                                                         break;
292                                                 }
293                                                 ast_stopstream(channel);
294                                                 if (strcmp(buf,reqinp)) /* if not match */
295                                                 {
296                                                         if (debug) printf("@@@@ qcall_do: response (%s) does not match required (%s)\n",buf,reqinp);
297                                                         ast_frfree(f);
298                                                         break;
299                                                 }
300                                                 ast_frfree(f);
301                                         }
302                                         /* okay, now we go for it */
303                                         context = strchr(extstr,'@');
304                                         if (!context) context = "default";
305                                         else *context++ = 0;
306                                         if (option_verbose > 2)
307                                                 ast_verbose(VERBOSE_PREFIX_3 "Qcall got accept, now putting through to %s@%s on %s\n",
308                                                         extstr,context,channel->name);
309                                         if (strlen(ident)) {
310                                                 strcat(ident,"-ok");
311                                                 /* if file existant, play it */
312                                                 if (!ast_streamfile(channel,ident,0))
313                                                 {
314                                                         ast_waitstream(channel,"");
315                                                         ast_stopstream(channel);
316                                                 }
317                                         }
318                                         if (strcasecmp(clid, "asreceived")) {
319                                                 channel->callerid = strdup(clid);
320                                                 channel->ani = strdup(clid);
321                                         }
322                                         channel->language[0] = 0;
323                                         channel->dnid = strdup(extstr);
324 #ifdef  AMAFLAGS
325                                         channel->amaflags = AMAFLAGS;
326 #endif
327 #ifdef  ACCTCODE
328                                         strcpy(channel->accountcode,ACCTCODE);
329 #else
330                                         channel->accountcode[0] = 0;
331 #endif
332                                         if (maxsecs)  /* if finite length call */
333                                            {
334                                                 time(&channel->whentohangup);
335                                                 channel->whentohangup += maxsecs;
336                                            }
337                                         strcpy(channel->exten,extstr);
338                                         strcpy(channel->context,context);
339                                         channel->priority = 1;
340                                         printf("Caller ID is %s\n", channel->callerid);
341                                         ast_pbx_run(channel);
342                                         pthread_exit(NULL);
343                                 }
344                         }
345                         else if(f->subclass==AST_CONTROL_RINGING)
346                                 if (debug) printf("@@@@ qcall_do:Phone Ringing end\n");
347                 }
348                 ast_frfree(f);
349         }
350         ast_hangup(channel);
351         if (debug) printf("@@@@ qcall_do:Hung up channel\n");
352         pthread_exit(NULL);
353         return NULL;
354 }
355
356 int unload_module(void)
357 {
358         STANDARD_HANGUP_LOCALUSERS;
359         return 0;
360 }
361
362 int load_module(void)
363 {
364         mkdir(qdir,0660);
365         pthread_create(&qcall_thread,NULL,qcall,NULL);
366         return 0;
367 }
368
369 char *description(void)
370 {
371         return tdesc;
372 }
373
374 int usecount(void)
375 {
376         int res;
377         STANDARD_USECOUNT(res);
378         return res;
379 }
380
381 char *key()
382 {
383         return ASTERISK_GPL_KEY;
384 }