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