Use find_user for existsmailbox
[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 #include "../astconf.h"
75
76 static char qdir[255];
77 static  char *tdesc = "Call from Queue";
78 static  pthread_t qcall_thread;
79 static int debug = 0;
80 STANDARD_LOCAL_USER;
81 LOCAL_USER_DECL;
82
83 #define OLDESTOK        14400           /* not any more then this number of secs old */
84 #define INITIALONE      1               /* initial wait before the first one in secs */
85 #define NEXTONE         600             /* wait before trying it again in secs */
86 #define MAXWAITFORANSWER 45000          /* max call time before answer */
87 /* define either one or both of these two if your application requires it */
88 #if     0
89 #define ACCTCODE        "SOMETHING"     /* Account code */
90 #define AMAFLAGS AST_CDR_BILLING        /* AMA flags */
91 #endif
92 /* define this if you want to have a particular CLID display on the user's
93    phone when they receive the call */
94 #if     0
95 #define OURCLID "2564286275"            /* The callerid to be displayed when calling */
96 #endif
97
98 static void *qcall_do(void *arg);
99
100 static void *qcall(void *ignore)
101 {
102 pthread_t dialer_thread;
103 DIR *dirp;
104 FILE *fp;
105 struct dirent *dp;
106 char fname[80];
107 struct stat mystat;
108 time_t  t;
109 void *arg;
110 pthread_attr_t attr;
111
112         time(&t);
113         if (debug) printf("@@@@ qcall starting at %s",ctime(&t));
114         for(;;)
115            {
116                 time(&t);
117                 dirp = opendir(qdir);
118                 if (!dirp)
119                    {
120                         perror("app_qcall:Cannot open queue directory");
121                         break;
122                    }
123                 while((dp = readdir(dirp)) != NULL)
124                    {
125                         if (dp->d_name[0] == '.') continue;
126                         sprintf(fname,"%s/%s",qdir,dp->d_name);
127                         if (stat(fname,&mystat) == -1)
128                            {
129                                 perror("app_qcall:stat");
130                                 fprintf(stderr,"%s\n",fname);
131                                 continue;
132                            }
133                           /* if not a regular file, skip it */
134                         if ((mystat.st_mode & S_IFMT) != S_IFREG) continue;
135                           /* if not yet .... */
136                         if (mystat.st_atime == mystat.st_mtime)
137                            {  /* first time */
138                                 if ((mystat.st_atime + INITIALONE) > t) 
139                                         continue;
140                            }
141                         else
142                            { /* already looked at once */
143                                 if ((mystat.st_atime + NEXTONE) > t) continue;
144                            }
145                           /* if too old */
146                         if (mystat.st_mtime < (t - OLDESTOK))
147                            {
148                                 /* kill it, its too old */
149                                 unlink(fname);
150                                 continue;
151                            }                            
152                          /* "touch" file's access time */
153                         fp = fopen(fname,"r");
154                         if (fp) fclose(fp);
155                         /* make a copy of the filename string, so that we
156                                 may go on and use the buffer */
157                         arg = (void *) strdup(fname);
158                         pthread_attr_init(&attr);
159                         pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
160                         if (pthread_create(&dialer_thread,&attr,qcall_do,arg) == -1)
161                            {
162                                 perror("qcall: Cannot create thread");
163                                 continue;
164                            }
165                    }
166                 closedir(dirp);
167                 sleep(1);
168            }
169         pthread_exit(NULL);
170 }
171         
172 /* single thread with one file (request) to dial */
173 static void *qcall_do(void *arg)
174 {
175 char fname[300],dialstr[300],extstr[300],ident[300],reqinp[300],buf[300];
176 char clid[300],*tele,*context;
177 FILE *fp;
178 int ms = MAXWAITFORANSWER,maxsecs;
179 struct ast_channel *channel;
180 time_t  t;
181
182           /* get the filename from the arg */
183         strcpy(fname,(char *)arg);
184         free(arg);
185         time(&t);
186         fp = fopen(fname,"r");
187         if (!fp) /* if cannot open request file */
188            {
189                 perror("qcall_do:fopen");
190                 fprintf(stderr,"%s\n",fname);
191                 unlink(fname);
192                 pthread_exit(NULL);
193            }
194         /* lock the file */
195         if (flock(fileno(fp),LOCK_EX) == -1)
196            {
197                 perror("qcall_do:flock");
198                 fprintf(stderr,"%s\n",fname);
199                 pthread_exit(NULL);
200            }
201         strcpy(reqinp,"1");  /* default required input for acknowledgement */
202         strcpy(ident, "");      /* default no ident */
203         if (fscanf(fp,"%s %s %s %d %s %s",dialstr,clid,
204                 extstr,&maxsecs,ident,reqinp) < 4)
205            {
206                 fprintf(stderr,"qcall_do:file line invalid in file %s:\n",fname);
207                 pthread_exit(NULL);
208            }
209         flock(fileno(fp),LOCK_UN);
210         fclose(fp);
211         tele = strchr(dialstr,'/');
212         if (!tele)
213            {
214                 fprintf(stderr,"qcall_do:Dial number must be in format tech/number\n");
215                 unlink(fname);
216                 pthread_exit(NULL);
217            }
218         *tele++ = 0;
219         channel = ast_request(dialstr,AST_FORMAT_SLINEAR,tele);
220         if (channel)
221            {
222                 ast_set_read_format(channel,AST_FORMAT_SLINEAR);
223                 ast_set_write_format(channel,AST_FORMAT_SLINEAR);
224 #ifdef  OURCLID
225                 if (channel->callerid)
226                         free(channel->callerid);
227                 channel->callerid = strdup(OURCLID);
228                 if (channel->ani)
229                         free(channel->ani);
230                 channel->ani = strdup(OURCLID);
231 #endif          
232                 channel->whentohangup = 0;
233                 channel->appl = "AppQcall";
234                 channel->data = "(Outgoing Line)";
235                 if (option_verbose > 2)
236                         ast_verbose(VERBOSE_PREFIX_3 "Qcall initiating call to %s/%s on %s (%s)\n",
237                                 dialstr,tele,channel->name,fname);
238                 ast_call(channel,tele,MAXWAITFORANSWER);
239            }
240         else
241            {
242                 fprintf(stderr,"qcall_do:Sorry unable to obtain channel\n");
243                 pthread_exit(NULL);
244            }
245         if (strcasecmp(clid, "asreceived")) {
246                 if (channel->callerid) free(channel->callerid);
247                 channel->callerid = NULL;
248                 if (channel->ani) free(channel->ani);
249                 channel->ani = NULL;
250         }
251         if (channel->_state == AST_STATE_UP)
252         if (debug) printf("@@@@ Autodial:Line is Up\n");
253         if (option_verbose > 2)
254         ast_verbose(VERBOSE_PREFIX_3 "Qcall waiting for answer on %s\n",
255                 channel->name);
256         while(ms > 0){
257                 struct ast_frame *f;
258                 ms = ast_waitfor(channel,ms);
259                 f = ast_read(channel);
260                 if (!f)
261                    {
262                         if (debug) printf("@@@@ qcall_do:Hung Up\n");
263                         unlink(fname);
264                         break;
265                    }
266                 if (f->frametype == AST_FRAME_CONTROL)
267                    {
268                         if (f->subclass == AST_CONTROL_HANGUP)
269                            {
270                                 if (debug) printf("@@@@ qcall_do:Hung Up\n");
271                                 unlink(fname);
272                                 ast_frfree(f);
273                                 break;
274                            }
275                         if (f->subclass == AST_CONTROL_ANSWER)
276                            {
277                                 if (debug) printf("@@@@ qcall_do:Phone Answered\n");
278                                 if (channel->_state == AST_STATE_UP)
279                                    {
280                                         unlink(fname);
281                                         if (option_verbose > 2)
282                                                 ast_verbose(VERBOSE_PREFIX_3 "Qcall got answer on %s\n",
283                                                         channel->name);
284                                         usleep(1500000);
285                                         if (strlen(ident)) {
286                                                 ast_streamfile(channel,ident,0);
287                                                 if (ast_readstring(channel,buf,strlen(reqinp),10000,5000,"#"))
288                                                 {
289                                                         ast_stopstream(channel);
290                                                         if (debug) printf("@@@@ qcall_do: timeout or hangup in dtmf read\n");
291                                                         ast_frfree(f);
292                                                         break;
293                                                 }
294                                                 ast_stopstream(channel);
295                                                 if (strcmp(buf,reqinp)) /* if not match */
296                                                 {
297                                                         if (debug) printf("@@@@ qcall_do: response (%s) does not match required (%s)\n",buf,reqinp);
298                                                         ast_frfree(f);
299                                                         break;
300                                                 }
301                                                 ast_frfree(f);
302                                         }
303                                         /* okay, now we go for it */
304                                         context = strchr(extstr,'@');
305                                         if (!context) context = "default";
306                                         else *context++ = 0;
307                                         if (option_verbose > 2)
308                                                 ast_verbose(VERBOSE_PREFIX_3 "Qcall got accept, now putting through to %s@%s on %s\n",
309                                                         extstr,context,channel->name);
310                                         if (strlen(ident)) {
311                                                 strcat(ident,"-ok");
312                                                 /* if file existant, play it */
313                                                 if (!ast_streamfile(channel,ident,0))
314                                                 {
315                                                         ast_waitstream(channel,"");
316                                                         ast_stopstream(channel);
317                                                 }
318                                         }
319                                         if (strcasecmp(clid, "asreceived")) {
320                                                 channel->callerid = strdup(clid);
321                                                 channel->ani = strdup(clid);
322                                         }
323                                         channel->language[0] = 0;
324                                         channel->dnid = strdup(extstr);
325 #ifdef  AMAFLAGS
326                                         channel->amaflags = AMAFLAGS;
327 #endif
328 #ifdef  ACCTCODE
329                                         strcpy(channel->accountcode,ACCTCODE);
330 #else
331                                         channel->accountcode[0] = 0;
332 #endif
333                                         if (maxsecs)  /* if finite length call */
334                                            {
335                                                 time(&channel->whentohangup);
336                                                 channel->whentohangup += maxsecs;
337                                            }
338                                         strcpy(channel->exten,extstr);
339                                         strcpy(channel->context,context);
340                                         channel->priority = 1;
341                                         if(debug) printf("Caller ID is %s\n", channel->callerid);
342                                         ast_pbx_run(channel);
343                                         pthread_exit(NULL);
344                                 }
345                         }
346                         else if(f->subclass==AST_CONTROL_RINGING)
347                                 if (debug) printf("@@@@ qcall_do:Phone Ringing end\n");
348                 }
349                 ast_frfree(f);
350         }
351         ast_hangup(channel);
352         if (debug) printf("@@@@ qcall_do:Hung up channel\n");
353         pthread_exit(NULL);
354         return NULL;
355 }
356
357 int unload_module(void)
358 {
359         STANDARD_HANGUP_LOCALUSERS;
360         return 0;
361 }
362
363 int load_module(void)
364 {
365         snprintf((char *)qdir,sizeof(qdir)-1,"%s/%s",(char *)ast_config_AST_SPOOL_DIR,"qcall");
366         mkdir(qdir,0760);
367         pthread_create(&qcall_thread,NULL,qcall,NULL);
368         return 0;
369 }
370
371 char *description(void)
372 {
373         return tdesc;
374 }
375
376 int usecount(void)
377 {
378         int res;
379         STANDARD_USECOUNT(res);
380         return res;
381 }
382
383 char *key()
384 {
385         return ASTERISK_GPL_KEY;
386 }