mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +00:00 
			
		
		
		
	git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@394 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			371 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			371 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/** @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;
 | 
						|
}
 |