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