Merge rgagnon's pedantic string changes (apps n-z) (bug #2038)
[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 <sys/types.h>
66 #include <sys/stat.h>
67 #include <errno.h>
68 #include <dirent.h>
69 #include <ctype.h>
70 #include <sys/stat.h>
71 #include <sys/time.h>
72 #include <sys/file.h>
73 #include "../astconf.h"
74
75 static char qdir[255];
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                         snprintf(fname, sizeof(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_mtime)
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] = "";
175         char dialstr[300];
176         char extstr[300];
177         char ident[300] = "";
178         char reqinp[300] = "";
179         char buf[300];
180         char clid[300],*tele,*context;
181         FILE *fp;
182         int ms = MAXWAITFORANSWER,maxsecs;
183         struct ast_channel *channel;
184         time_t  t;
185
186           /* get the filename from the arg */
187         strncpy(fname,(char *)arg, sizeof(fname) - 1);
188         free(arg);
189         time(&t);
190         fp = fopen(fname,"r");
191         if (!fp) /* if cannot open request file */
192            {
193                 perror("qcall_do:fopen");
194                 fprintf(stderr,"%s\n",fname);
195                 unlink(fname);
196                 pthread_exit(NULL);
197            }
198         /* lock the file */
199         if (flock(fileno(fp),LOCK_EX) == -1)
200            {
201                 perror("qcall_do:flock");
202                 fprintf(stderr,"%s\n",fname);
203                 pthread_exit(NULL);
204            }
205         /* default required input for acknowledgement */
206         reqinp[0] = '1';
207         reqinp[1] = '\0';
208         /* default no ident */
209         ident[0] = '\0';  /* default no ident */
210         if (fscanf(fp,"%s %s %s %d %s %s",dialstr,clid,
211                 extstr,&maxsecs,ident,reqinp) < 4)
212            {
213                 fprintf(stderr,"qcall_do:file line invalid in file %s:\n",fname);
214                 pthread_exit(NULL);
215            }
216         flock(fileno(fp),LOCK_UN);
217         fclose(fp);
218         tele = strchr(dialstr,'/');
219         if (!tele)
220            {
221                 fprintf(stderr,"qcall_do:Dial number must be in format tech/number\n");
222                 unlink(fname);
223                 pthread_exit(NULL);
224            }
225         *tele++ = 0;
226         channel = ast_request(dialstr,AST_FORMAT_SLINEAR,tele);
227         if (channel)
228            {
229                 ast_set_read_format(channel,AST_FORMAT_SLINEAR);
230                 ast_set_write_format(channel,AST_FORMAT_SLINEAR);
231 #ifdef  OURCLID
232                 if (channel->callerid)
233                         free(channel->callerid);
234                 channel->callerid = strdup(OURCLID);
235                 if (channel->ani)
236                         free(channel->ani);
237                 channel->ani = strdup(OURCLID);
238 #endif          
239                 channel->whentohangup = 0;
240                 channel->appl = "AppQcall";
241                 channel->data = "(Outgoing Line)";
242                 if (option_verbose > 2)
243                         ast_verbose(VERBOSE_PREFIX_3 "Qcall initiating call to %s/%s on %s (%s)\n",
244                                 dialstr,tele,channel->name,fname);
245                 ast_call(channel,tele,MAXWAITFORANSWER);
246            }
247         else
248            {
249                 fprintf(stderr,"qcall_do:Sorry unable to obtain channel\n");
250                 pthread_exit(NULL);
251            }
252         if (strcasecmp(clid, "asreceived")) {
253                 if (channel->callerid) free(channel->callerid);
254                 channel->callerid = NULL;
255                 if (channel->ani) free(channel->ani);
256                 channel->ani = NULL;
257         }
258         if (channel->_state == AST_STATE_UP)
259         if (debug) printf("@@@@ Autodial:Line is Up\n");
260         if (option_verbose > 2)
261         ast_verbose(VERBOSE_PREFIX_3 "Qcall waiting for answer on %s\n",
262                 channel->name);
263         while(ms > 0){
264                 struct ast_frame *f;
265                 ms = ast_waitfor(channel,ms);
266                 f = ast_read(channel);
267                 if (!f)
268                    {
269                         if (debug) printf("@@@@ qcall_do:Hung Up\n");
270                         unlink(fname);
271                         break;
272                    }
273                 if (f->frametype == AST_FRAME_CONTROL)
274                    {
275                         if (f->subclass == AST_CONTROL_HANGUP)
276                            {
277                                 if (debug) printf("@@@@ qcall_do:Hung Up\n");
278                                 unlink(fname);
279                                 ast_frfree(f);
280                                 break;
281                            }
282                         if (f->subclass == AST_CONTROL_ANSWER)
283                            {
284                                 if (debug) printf("@@@@ qcall_do:Phone Answered\n");
285                                 if (channel->_state == AST_STATE_UP)
286                                    {
287                                         unlink(fname);
288                                         if (option_verbose > 2)
289                                                 ast_verbose(VERBOSE_PREFIX_3 "Qcall got answer on %s\n",
290                                                         channel->name);
291                                         usleep(1500000);
292                                         if (strlen(ident)) {
293                                                 ast_streamfile(channel,ident,0);
294                                                 if (ast_readstring(channel,buf,strlen(reqinp),10000,5000,"#"))
295                                                 {
296                                                         ast_stopstream(channel);
297                                                         if (debug) printf("@@@@ qcall_do: timeout or hangup in dtmf read\n");
298                                                         ast_frfree(f);
299                                                         break;
300                                                 }
301                                                 ast_stopstream(channel);
302                                                 if (strcmp(buf,reqinp)) /* if not match */
303                                                 {
304                                                         if (debug) printf("@@@@ qcall_do: response (%s) does not match required (%s)\n",buf,reqinp);
305                                                         ast_frfree(f);
306                                                         break;
307                                                 }
308                                                 ast_frfree(f);
309                                         }
310                                         /* okay, now we go for it */
311                                         context = strchr(extstr,'@');
312                                         if (!context) context = "default";
313                                         else *context++ = 0;
314                                         if (option_verbose > 2)
315                                                 ast_verbose(VERBOSE_PREFIX_3 "Qcall got accept, now putting through to %s@%s on %s\n",
316                                                         extstr,context,channel->name);
317                                         if (strlen(ident)) {
318                                                 strncat(ident,"-ok", sizeof(ident) - strlen(ident) - 1);
319                                                 /* if file existant, play it */
320                                                 if (!ast_streamfile(channel,ident,0))
321                                                 {
322                                                         ast_waitstream(channel,"");
323                                                         ast_stopstream(channel);
324                                                 }
325                                         }
326                                         if (strcasecmp(clid, "asreceived")) {
327                                                 channel->callerid = strdup(clid);
328                                                 channel->ani = strdup(clid);
329                                         }
330                                         channel->language[0] = 0;
331                                         channel->dnid = strdup(extstr);
332 #ifdef  AMAFLAGS
333                                         channel->amaflags = AMAFLAGS;
334 #endif
335 #ifdef  ACCTCODE
336                                         strncpy(channel->accountcode, ACCTCODE, sizeof(chan->accountcode) - 1);
337 #else
338                                         channel->accountcode[0] = 0;
339 #endif
340                                         if (maxsecs)  /* if finite length call */
341                                            {
342                                                 time(&channel->whentohangup);
343                                                 channel->whentohangup += maxsecs;
344                                            }
345                                         strncpy(channel->exten, extstr, sizeof(channel->exten) - 1);
346                                         strncpy(channel->context, context, sizeof(channel->context) - 1);
347                                         channel->priority = 1;
348                                         if(debug) printf("Caller ID is %s\n", channel->callerid);
349                                         ast_pbx_run(channel);
350                                         pthread_exit(NULL);
351                                 }
352                         }
353                         else if(f->subclass==AST_CONTROL_RINGING)
354                                 if (debug) printf("@@@@ qcall_do:Phone Ringing end\n");
355                 }
356                 ast_frfree(f);
357         }
358         ast_hangup(channel);
359         if (debug) printf("@@@@ qcall_do:Hung up channel\n");
360         pthread_exit(NULL);
361         return NULL;
362 }
363
364 int unload_module(void)
365 {
366         STANDARD_HANGUP_LOCALUSERS;
367         return 0;
368 }
369
370 int load_module(void)
371 {
372         snprintf(qdir, sizeof(qdir), "%s/%s", ast_config_AST_SPOOL_DIR, "qcall");
373         mkdir(qdir,0760);
374         pthread_create(&qcall_thread,NULL,qcall,NULL);
375         return 0;
376 }
377
378 char *description(void)
379 {
380         return tdesc;
381 }
382
383 int usecount(void)
384 {
385         int res;
386         STANDARD_USECOUNT(res);
387         return res;
388 }
389
390 char *key()
391 {
392         return ASTERISK_GPL_KEY;
393 }