Version 0.1.10 from FTP
authorMark Spencer <markster@digium.com>
Mon, 10 Dec 2001 21:47:23 +0000 (21:47 +0000)
committerMark Spencer <markster@digium.com>
Mon, 10 Dec 2001 21:47:23 +0000 (21:47 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394 65c4cc65-6c06-0410-ace0-fbb531ad65f3

apps/app_qcall.c [new file with mode: 0755]

diff --git a/apps/app_qcall.c b/apps/app_qcall.c
new file mode 100755 (executable)
index 0000000..87672e5
--- /dev/null
@@ -0,0 +1,370 @@
+/** @file app_qcall.c 
+ *
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Call back a party and connect them to a running pbx thread
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ *
+ * Call a user from a file contained within a queue (/var/spool/asterisk/qcall)
+ * 
+ * The queue is a directory containing files with the call request information
+ * as a single line of text as follows:
+ * 
+ * Dialstring Caller-ID Extension Maxsecs Identifier [Required-response]
+ *
+ *  Dialstring -- A Dial String (The number to be called) in the
+ *  format Technology/Number, such IAX/mysys/1234 or Zap/g1/1234
+ * 
+ *  Caller-ID -- A Standard nomalized representation of the Caller-ID of
+ *  the number being dialed (generally 10 digits in the US).
+ *
+ *  Extension -- The Extension (optionally Extension@context) that the
+ *  user should be "transferred" to after acceptance of the call.
+ *
+ *  Maxsecs -- The Maximum time of the call in seconds. Specify 0 for infinite.
+ *
+ *  Identifier -- The "Identifier" of the request. This is used to determine
+ *  the names of the audio prompt files played. The first prompt, the one that
+ *  asks for the input, is just the exact string specified as the identifier.
+ *  The second prompt, the one that is played after the correct input is given,
+ *  (generally a "thank you" recording), is the specified string with "-ok" 
+ *  added to the end. So, if you specify "foo" as the identifier, your first
+ *  prompt file that will be played will be "foo" and the second one will be
+ *  "foo-ok".
+ *
+ *  Required-Response (Optional) -- Specify a digit string to be used as the
+ *  acceptance "code" if you desire it to be something other then "1". This
+ *  can be used to implement some sort of PIN or security system. It may be
+ *  more then a single character.
+ *
+ * NOTE: It is important to remember that the process that creates these
+ * files needs keep and maintain a write lock (using flock with the LOCK_EX
+ * option) when writing these files.
+ *
+ */
+#include <asterisk/file.h>
+#include <asterisk/logger.h>
+#include <asterisk/channel.h>
+#include <asterisk/pbx.h>
+#include <asterisk/module.h>
+#include <asterisk/translate.h>
+#include <asterisk/options.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+const   char *qdir="/var/spool/asterisk/qcall";
+static  char *tdesc = "Call from Queue";
+static  pthread_t qcall_thread;
+static int debug = 0;
+STANDARD_LOCAL_USER;
+LOCAL_USER_DECL;
+
+#define        OLDESTOK        14400           /* not any more then this number of secs old */
+#define        INITIALONE      20              /* initial wait before the first one in secs */
+#define        NEXTONE         600             /* wait before trying it again in secs */
+#define        MAXWAITFORANSWER 45000          /* max call time before answer */
+/* define either one or both of these two if your application requires it */
+#if    0
+#define        ACCTCODE        "SOMETHING"     /* Account code */
+#define        AMAFLAGS AST_CDR_BILLING        /* AMA flags */
+#endif
+/* define this if you want to have a particular CLID display on the user's
+   phone when they receive the call */
+#if    0
+#define        OURCLID "2564286275"            /* The callerid to be displayed when calling */
+#endif
+
+static void *qcall_do(void *arg);
+
+static void *qcall(void *ignore)
+{
+pthread_t dialer_thread;
+DIR *dirp;
+FILE *fp;
+struct dirent *dp;
+char fname[80];
+struct stat mystat;
+time_t t;
+void *arg;
+pthread_attr_t attr;
+
+       time(&t);
+       if (debug) printf("@@@@ qcall starting at %s",ctime(&t));
+       for(;;)
+          {
+               time(&t);
+               dirp = opendir(qdir);
+               if (!dirp)
+                  {
+                       perror("app_qcall:Cannot open queue directory");
+                       break;
+                  }
+               while((dp = readdir(dirp)) != NULL)
+                  {
+                       if (dp->d_name[0] == '.') continue;
+                       sprintf(fname,"%s/%s",qdir,dp->d_name);
+                       if (stat(fname,&mystat) == -1)
+                          {
+                               perror("app_qcall:stat");
+                               fprintf(stderr,"%s\n",fname);
+                               continue;
+                          }
+                         /* if not a regular file, skip it */
+                       if ((mystat.st_mode & S_IFMT) != S_IFREG) continue;
+                         /* if not yet .... */
+                       if (mystat.st_atime == mystat.st_ctime)
+                          {  /* first time */
+                               if ((mystat.st_atime + INITIALONE) > t) continue;
+                          }
+                       else
+                          { /* already looked at once */
+                               if ((mystat.st_atime + NEXTONE) > t) continue;
+                          }
+                         /* if too old */
+                       if (mystat.st_mtime < (t - OLDESTOK))
+                          {
+                               /* kill it, its too old */
+                               unlink(fname);
+                               continue;
+                          }                            
+                        /* "touch" file's access time */
+                       fp = fopen(fname,"r");
+                       if (fp) fclose(fp);
+                       /* make a copy of the filename string, so that we
+                               may go on and use the buffer */
+                       arg = (void *) strdup(fname);
+                       pthread_attr_init(&attr);
+                       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+                       if (pthread_create(&dialer_thread,&attr,qcall_do,arg) == -1)
+                          {
+                               perror("qcall: Cannot create thread");
+                               continue;
+                          }
+                  }
+               closedir(dirp);
+               sleep(1);
+          }
+       pthread_exit(NULL);
+}
+       
+/* single thread with one file (request) to dial */
+static void *qcall_do(void *arg)
+{
+char fname[300],dialstr[300],extstr[300],ident[300],reqinp[300],buf[300];
+char clid[300],*tele,*context;
+FILE *fp;
+int ms = MAXWAITFORANSWER,maxsecs;
+struct ast_channel *channel;
+time_t t;
+
+         /* get the filename from the arg */
+       strcpy(fname,(char *)arg);
+       free(arg);
+       time(&t);
+       fp = fopen(fname,"r");
+       if (!fp) /* if cannot open request file */
+          {
+               perror("qcall_do:fopen");
+               fprintf(stderr,"%s\n",fname);
+               unlink(fname);
+               pthread_exit(NULL);
+          }
+       /* lock the file */
+       if (flock(fileno(fp),LOCK_EX) == -1)
+          {
+               perror("qcall_do:flock");
+               fprintf(stderr,"%s\n",fname);
+               pthread_exit(NULL);
+          }
+       strcpy(reqinp,"1");  /* default required input for acknowledgement */
+       if (fscanf(fp,"%s %s %s %d %s %s",dialstr,clid,
+               extstr,&maxsecs,ident,reqinp) < 5)
+          {
+               fprintf(stderr,"qcall_do:file line invalid in file %s:\n",fname);
+               pthread_exit(NULL);
+          }
+       flock(fileno(fp),LOCK_UN);
+       fclose(fp);
+       tele = strchr(dialstr,'/');
+       if (!tele)
+          {
+               fprintf(stderr,"qcall_do:Dial number must be in format tech/number\n");
+               unlink(fname);
+               pthread_exit(NULL);
+          }
+       *tele++ = 0;
+       channel = ast_request(dialstr,AST_FORMAT_SLINEAR,tele);
+       if (channel)
+          {
+               ast_set_read_format(channel,AST_FORMAT_SLINEAR);
+               ast_set_write_format(channel,AST_FORMAT_SLINEAR);
+               channel->callerid = NULL;
+               channel->hidden_callerid = NULL;
+#ifdef OURCLID
+               channel->callerid = strdup(OURCLID);
+               channel->hidden_callerid = strdup(OURCLID);
+#endif         
+               channel->whentohangup = 0;
+               channel->appl = "AppQcall";
+               channel->data = "(Outgoing Line)";
+               if (option_verbose > 2)
+                       ast_verbose(VERBOSE_PREFIX_3 "Qcall initiating call to %s/%s on %s (%s)\n",
+                               dialstr,tele,channel->name,fname);
+               ast_call(channel,tele,MAXWAITFORANSWER);
+          }
+       else
+          {
+               fprintf(stderr,"qcall_do:Sorry unable to obtain channel\n");
+               pthread_exit(NULL);
+          }
+       if (channel->callerid) free(channel->callerid);
+       channel->callerid = NULL;
+       if (channel->hidden_callerid) free(channel->hidden_callerid);
+       channel->hidden_callerid = NULL;
+       if (channel->state == AST_STATE_UP)
+       if (debug) printf("@@@@ Autodial:Line is Up\n");
+       if (option_verbose > 2)
+       ast_verbose(VERBOSE_PREFIX_3 "Qcall waiting for answer on %s\n",
+               channel->name);
+       while(ms > 0){
+               struct ast_frame *f;
+               ms = ast_waitfor(channel,ms);
+               f = ast_read(channel);
+               if (!f)
+                  {
+                       if (debug) printf("@@@@ qcall_do:Hung Up\n");
+                       ast_frfree(f);
+                       unlink(fname);
+                       break;
+                  }
+               if (f->frametype == AST_FRAME_CONTROL)
+                  {
+                       if (f->subclass == AST_CONTROL_HANGUP)
+                          {
+                               if (debug) printf("@@@@ qcall_do:Hung Up\n");
+                               unlink(fname);
+                               ast_frfree(f);
+                               break;
+                          }
+                       if (f->subclass == AST_CONTROL_ANSWER)
+                          {
+                               if (debug) printf("@@@@ qcall_do:Phone Answered\n");
+                               if (channel->state == AST_STATE_UP)
+                                  {
+                                       unlink(fname);
+                                       if (option_verbose > 2)
+                                               ast_verbose(VERBOSE_PREFIX_3 "Qcall got answer on %s\n",
+                                                       channel->name);
+                                       usleep(1500000);
+                                       ast_streamfile(channel,ident,0);
+                                       if (ast_readstring(channel,buf,strlen(reqinp),10000,5000,"#"))
+                                          {
+                                               ast_stopstream(channel);
+                                               if (debug) printf("@@@@ qcall_do: timeout or hangup in dtmf read\n");
+                                               ast_frfree(f);
+                                               break;
+                                          }
+                                       ast_stopstream(channel);
+                                       if (strcmp(buf,reqinp)) /* if not match */
+                                          {
+                                               if (debug) printf("@@@@ qcall_do: response (%s) does not match required (%s)\n",buf,reqinp);
+                                               ast_frfree(f);
+                                               break;
+                                          }
+                                       ast_frfree(f);
+                                       /* okay, now we go for it */
+                                       context = strchr(extstr,'@');
+                                       if (!context) context = "default";
+                                       else *context++ = 0;
+                                       if (option_verbose > 2)
+                                               ast_verbose(VERBOSE_PREFIX_3 "Qcall got accept, now putting through to %s@%s on %s\n",
+                                                       extstr,context,channel->name);
+                                       strcat(ident,"-ok");
+                                         /* if file existant, play it */
+                                       if (!ast_streamfile(channel,ident,0))
+                                          {
+                                               ast_waitstream(channel,"");
+                                               ast_stopstream(channel);
+                                          }
+                                       channel->callerid = strdup(clid);
+                                       channel->hidden_callerid = strdup(clid);
+                                       channel->language[0] = 0;
+                                       channel->dnid = strdup(extstr);
+#ifdef AMAFLAGS
+                                       channel->amaflags = AMAFLAGS;
+#endif
+#ifdef ACCTCODE
+                                       strcpy(channel->accountcode,ACCTCODE);
+#else
+                                       channel->accountcode[0] = 0;
+#endif
+                                       if (maxsecs)  /* if finite length call */
+                                          {
+                                               time(&channel->whentohangup);
+                                               channel->whentohangup += maxsecs;
+                                          }
+                                       strcpy(channel->exten,extstr);
+                                       strcpy(channel->context,context);
+                                       channel->priority = 1;
+                                       ast_pbx_run(channel);
+                                       pthread_exit(NULL);
+                               }
+                       }
+                       else if(f->subclass==AST_CONTROL_RINGING)
+                               if (debug) printf("@@@@ qcall_do:Phone Ringing end\n");
+               }
+               ast_frfree(f);
+       }
+       ast_hangup(channel);
+       if (debug) printf("@@@@ qcall_do:Hung up channel\n");
+       pthread_exit(NULL);
+       return NULL;
+}
+
+int unload_module(void)
+{
+       STANDARD_HANGUP_LOCALUSERS;
+       return 0;
+}
+
+int load_module(void)
+{
+       mkdir(qdir,0660);
+       pthread_create(&qcall_thread,NULL,qcall,NULL);
+       return 0;
+}
+
+char *description(void)
+{
+       return tdesc;
+}
+
+int usecount(void)
+{
+       int res;
+       STANDARD_USECOUNT(res);
+       return res;
+}
+
+char *key()
+{
+       return ASTERISK_GPL_KEY;
+}