mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-30 07:41:39 +00:00 
			
		
		
		
	git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@919 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			387 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			387 lines
		
	
	
		
			11 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). Leave as
 | |
|  *  "asreceived" to use the default Caller*ID
 | |
|  *
 | |
|  *  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".  If omitted no prompt is given
 | |
|  *
 | |
|  *  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/lock.h>
 | |
| #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>
 | |
| #include "../astconf.h"
 | |
| 
 | |
| static char qdir[255];
 | |
| 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	1		/* 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_mtime)
 | |
| 			   {  /* 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 */
 | |
| 	strcpy(ident, "");	/* default no ident */
 | |
| 	if (fscanf(fp,"%s %s %s %d %s %s",dialstr,clid,
 | |
| 		extstr,&maxsecs,ident,reqinp) < 4)
 | |
| 	   {
 | |
| 		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);
 | |
| #ifdef	OURCLID
 | |
| 		if (channel->callerid)
 | |
| 			free(channel->callerid);
 | |
| 		channel->callerid = strdup(OURCLID);
 | |
| 		if (channel->ani)
 | |
| 			free(channel->ani);
 | |
| 		channel->ani = 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 (strcasecmp(clid, "asreceived")) {
 | |
| 		if (channel->callerid) free(channel->callerid);
 | |
| 		channel->callerid = NULL;
 | |
| 		if (channel->ani) free(channel->ani);
 | |
| 		channel->ani = 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");
 | |
| 			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);
 | |
| 					if (strlen(ident)) {
 | |
| 						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);
 | |
| 					if (strlen(ident)) {
 | |
| 						strcat(ident,"-ok");
 | |
| 						/* if file existant, play it */
 | |
| 						if (!ast_streamfile(channel,ident,0))
 | |
| 						{
 | |
| 							ast_waitstream(channel,"");
 | |
| 							ast_stopstream(channel);
 | |
| 						}
 | |
| 					}
 | |
| 					if (strcasecmp(clid, "asreceived")) {
 | |
| 						channel->callerid = strdup(clid);
 | |
| 						channel->ani = 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;
 | |
| 					printf("Caller ID is %s\n", channel->callerid);
 | |
| 					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)
 | |
| {
 | |
| 	snprintf((char *)qdir,sizeof(qdir)-1,"%s/%s",(char *)ast_config_AST_SPOOL_DIR,"qcall");
 | |
| 	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;
 | |
| }
 |