mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	add privacy/screening functionality to app_dial (bug #752)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@6102 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										314
									
								
								apps/app_dial.c
									
									
									
									
									
								
							
							
						
						
									
										314
									
								
								apps/app_dial.c
									
									
									
									
									
								
							| @@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
| #include "asterisk/app.h" | ||||
| #include "asterisk/causes.h" | ||||
| #include "asterisk/manager.h" | ||||
| #include "asterisk/privacy.h" | ||||
|  | ||||
| static char *tdesc = "Dialing Application"; | ||||
|  | ||||
| @@ -63,6 +64,10 @@ static char *descrip = | ||||
| "n is the priority of the dialer instance), then it will be the next\n" | ||||
| "executed extension (this allows you to setup different behavior on busy from\n" | ||||
| "no-answer).\n" | ||||
| "  For the Privacy and Screening Modes, the DIALSTATUS variable will be set to DONTCALL, \n" | ||||
| "if the called party chooses to send the calling party to the 'Go Away' script, and \n" | ||||
| "the DIALSTATUS variable will be set to TORTURE, if the called party wants to send the caller to \n" | ||||
| "the TORTURE scripts\n" | ||||
| "  This application returns -1 if the originating channel hangs up, or if the\n" | ||||
| "call is bridged and either of the parties in the bridge terminate the call.\n" | ||||
| "The option string may contain zero or more of the following characters:\n" | ||||
| @@ -92,7 +97,10 @@ static char *descrip = | ||||
| "      'h' -- allow callee to hang up by hitting *.\n" | ||||
| "      'H' -- allow caller to hang up by hitting *.\n" | ||||
| "      'C' -- reset call detail record for this call.\n" | ||||
| "      'P[(x)]' -- privacy mode, using 'x' as database if provided.\n" | ||||
| "      'P[(x)]' -- privacy mode, using 'x' as database if provided, or the extension is used if not provided.\n" | ||||
| "      'p' -- screening mode.  Basically Privacy mode without memory.\n" | ||||
| "       'n' -- modifier for screen/privacy mode. No intros are to be saved in the priv-callerintros dir.\n" | ||||
| "       'N' -- modifier for screen/privacy mode. if callerID is present, do not screen the call.\n" | ||||
| "      'g' -- goes on in context if the destination channel hangs up\n" | ||||
| "      'G(context^exten^pri)' -- If the call is answered transfer both parties to the specified exten.\n" | ||||
| "      'A(x)' -- play an announcement to the called party, using x as file\n" | ||||
| @@ -112,7 +120,7 @@ static char *descrip = | ||||
| "             * LIMIT_WARNING_FILE        File to play as warning if 'y' is defined.\n" | ||||
| "                        'timeleft' is a special sound macro to auto-say the time \n" | ||||
| "                        left and is the default.\n" | ||||
| "       'n' -- Do not jump to n+101 if all of the channels were busy.\n\n" | ||||
| "       'j' -- Do not jump to n+101 if all of the channels were busy.\n\n" | ||||
| "  In addition to transferring the call, a call may be parked and then picked\n" | ||||
| "up by another user.\n" | ||||
| "  The optional URL will be sent to the called party if the channel supports it.\n" | ||||
| @@ -122,7 +130,7 @@ static char *descrip = | ||||
| "      DIALEDTIME    Time from dial to answer\n"  | ||||
| "      ANSWEREDTIME  Time for actual call\n" | ||||
| "      DIALSTATUS    The status of the call as a text string, one of\n" | ||||
| "             CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL\n" | ||||
| "             CHANUNAVAIL | CONGESTION | NOANSWER | BUSY | ANSWER | CANCEL | DONTCALL | TORTURE\n" | ||||
| ""; | ||||
|  | ||||
| /* RetryDial App by Anthony Minessale II <anthmct@yahoo.com> Jan/2005 */ | ||||
| @@ -610,12 +618,17 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags | ||||
| 	struct localuser *u; | ||||
| 	char *info, *peers, *timeout, *tech, *number, *rest, *cur; | ||||
| 	char  privdb[256] = "", *s; | ||||
| 	char  privcid[256] = ""; | ||||
| 	char privintro[1024]; | ||||
| 	char  announcemsg[256] = "", *ann; | ||||
| 	struct localuser *outgoing=NULL, *tmp; | ||||
| 	struct ast_channel *peer; | ||||
| 	int to; | ||||
| 	int hasmacro = 0; | ||||
| 	int privacy=0; | ||||
| 	int screen=0; | ||||
| 	int no_save_intros = 0; | ||||
| 	int no_screen_callerid = 0; | ||||
| 	int announce=0; | ||||
| 	int resetcdr=0; | ||||
| 	int numbusy = 0; | ||||
| @@ -629,6 +642,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags | ||||
| 	char *newnum; | ||||
| 	char *l; | ||||
| 	char *url=NULL; /* JDG */ | ||||
| 	int privdb_val=0; | ||||
| 	unsigned int calldurationlimit=0; | ||||
| 	char *cdl; | ||||
| 	time_t now; | ||||
| @@ -814,7 +828,7 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags | ||||
| 				*(ann++) = 'X'; | ||||
| 			if (*ann) | ||||
| 				*ann = 'X'; | ||||
| 			/* Now find the end of the privdb */ | ||||
| 			/* Now find the end of the announce */ | ||||
| 			ann = strchr(announcemsg, ')'); | ||||
| 			if (ann) | ||||
| 				*ann = '\0'; | ||||
| @@ -906,11 +920,19 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags | ||||
| 		} else if (strchr(transfer, 'P')) { | ||||
| 			/* No specified privdb */ | ||||
| 			privacy = 1; | ||||
| 		} else if (strchr(transfer, 'p')) { | ||||
| 			screen = 1; | ||||
| 		} else if (strchr(transfer, 'C')) { | ||||
| 			resetcdr = 1; | ||||
| 		} else if (strchr(transfer, 'n')) { | ||||
| 		} else if (strchr(transfer, 'j')) { | ||||
| 			nojump = 1; | ||||
| 		} | ||||
| 		if (strchr(transfer, 'n')) { | ||||
| 			no_save_intros = 1; | ||||
| 		}  | ||||
| 		if (strchr(transfer, 'N')) { | ||||
| 			no_screen_callerid = 1; | ||||
| 		} | ||||
| 	} | ||||
| 	if (resetcdr && chan->cdr) | ||||
| 		ast_cdr_reset(chan->cdr, 0); | ||||
| @@ -918,11 +940,97 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags | ||||
| 		/* If privdb is not specified and we are using privacy, copy from extension */ | ||||
| 		ast_copy_string(privdb, chan->exten, sizeof(privdb)); | ||||
| 	} | ||||
| 	if (privacy) { | ||||
| 	if (privacy || screen) { | ||||
| 		char callerid[60]; | ||||
|  | ||||
| 		l = chan->cid.cid_num; | ||||
| 		if (!l) | ||||
| 			l = ""; | ||||
| 		ast_log(LOG_NOTICE, "Privacy DB is '%s', privacy is %d, clid is '%s'\n", privdb, privacy, l); | ||||
| 		if (l && !ast_strlen_zero(l)) { | ||||
| 			ast_shrink_phone_number(l); | ||||
| 			if( privacy ) { | ||||
| 				if (option_verbose > 2) | ||||
| 					ast_verbose( VERBOSE_PREFIX_3  "Privacy DB is '%s', privacy is %d, clid is '%s'\n", privdb, privacy, l); | ||||
| 				privdb_val = ast_privacy_check(privdb, l); | ||||
| 			} | ||||
| 			else { | ||||
| 				if (option_verbose > 2) | ||||
| 					ast_verbose( VERBOSE_PREFIX_3  "Privacy Screening, clid is '%s'\n", l); | ||||
| 				privdb_val = AST_PRIVACY_UNKNOWN; | ||||
| 			} | ||||
| 		} else { | ||||
| 			char *tnam, *tn2; | ||||
|  | ||||
| 			tnam = ast_strdupa(chan->name); | ||||
| 			/* clean the channel name so slashes don't try to end up in disk file name */ | ||||
| 			for(tn2 = tnam; *tn2; tn2++) { | ||||
| 				if( *tn2=='/') | ||||
| 					*tn2 = '=';  /* any other chars to be afraid of? */ | ||||
| 			} | ||||
| 			if (option_verbose > 2) | ||||
| 				ast_verbose( VERBOSE_PREFIX_3  "Privacy-- callerid is empty\n"); | ||||
|  | ||||
| 			snprintf(callerid, sizeof(callerid), "NOCALLERID_%s%s", chan->exten, tnam); | ||||
| 			l = callerid; | ||||
| 			privdb_val = AST_PRIVACY_UNKNOWN; | ||||
| 		} | ||||
| 		 | ||||
| 		ast_copy_string(privcid,l,sizeof(privcid)); | ||||
|  | ||||
| 		if( strncmp(privcid,"NOCALLERID",10) != 0 && no_screen_callerid ) { /* if callerid is set, and no_screen_callerid is set also */   | ||||
| 			if (option_verbose > 2) | ||||
| 				ast_verbose( VERBOSE_PREFIX_3  "CallerID set (%s); N option set; Screening should be off\n", privcid); | ||||
| 			privdb_val = AST_PRIVACY_ALLOW; | ||||
| 		} | ||||
| 		else if( no_screen_callerid && strncmp(privcid,"NOCALLERID",10) == 0 ) { | ||||
| 			if (option_verbose > 2) | ||||
| 				ast_verbose( VERBOSE_PREFIX_3  "CallerID blank; N option set; Screening should happen; dbval is %d\n", privdb_val); | ||||
| 		} | ||||
| 		 | ||||
| 		if( privdb_val == AST_PRIVACY_DENY ) { | ||||
| 			ast_verbose( VERBOSE_PREFIX_3  "Privacy DB reports PRIVACY_DENY for this callerid. Dial reports unavailable\n"); | ||||
| 			res=0; | ||||
| 			goto out; | ||||
| 		} | ||||
| 		else if( privdb_val == AST_PRIVACY_KILL ) { | ||||
| 			if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 201, chan->cid.cid_num))  | ||||
| 				chan->priority+=200; | ||||
|  | ||||
| 			res = 0; | ||||
| 			goto out; /* Is this right? */ | ||||
| 		} | ||||
| 		else if( privdb_val == AST_PRIVACY_TORTURE ) { | ||||
| 			if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 301, chan->cid.cid_num))  | ||||
| 				chan->priority+=300; | ||||
| 			res = 0; | ||||
| 			goto out; /* is this right??? */ | ||||
|  | ||||
| 		} | ||||
| 		else if( privdb_val == AST_PRIVACY_UNKNOWN ) { | ||||
| 			/* Get the user's intro, store it in priv-callerintros/$CID,  | ||||
| 			   unless it is already there-- this should be done before the  | ||||
| 			   call is actually dialed  */ | ||||
|  | ||||
| 			/* make sure the priv-callerintros dir exists? */ | ||||
|  | ||||
| 			snprintf(privintro,sizeof(privintro),"priv-callerintros/%s", privcid); | ||||
| 			if( ast_fileexists(privintro,NULL,NULL ) > 0 && strncmp(privcid,"NOCALLERID",10) != 0) { | ||||
| 				/* the DELUX version of this code would allow this caller the | ||||
| 				   option to hear and retape their previously recorded intro. | ||||
| 				*/ | ||||
| 			} | ||||
| 			else { | ||||
| 				int duration; /* for feedback from play_and_wait */ | ||||
| 				/* the file doesn't exist yet. Let the caller submit his | ||||
| 				   vocal intro for posterity */ | ||||
| 				/* priv-recordintro script: | ||||
|  | ||||
| 				   "At the tone, please say your name:" | ||||
|  | ||||
| 				*/ | ||||
| 				ast_play_and_record(chan, "priv-recordintro", privintro, 4, "gsm", &duration, 128, 2000, 0);  /* NOTE: I've reduced the total time to 4 sec */ | ||||
| 															/* don't think we'll need a lock removed, we took care of | ||||
| 															   conflicts by naming the privintro file */ | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* If a channel group has been specified, get it for use when we create peer channels */ | ||||
| @@ -1172,6 +1280,194 @@ static int dial_exec_full(struct ast_channel *chan, void *data, struct ast_flags | ||||
|  			ast_log(LOG_DEBUG, "app_dial: sendurl=%s.\n", url); | ||||
|  			ast_channel_sendurl( peer, url ); | ||||
|  		} /* /JDG */ | ||||
| 		if( privacy || screen ) { | ||||
| 			int res2; | ||||
| 			int loopcount = 0; | ||||
| 			if( privdb_val == AST_PRIVACY_UNKNOWN ) { | ||||
|  | ||||
| 				/* Get the user's intro, store it in priv-callerintros/$CID,  | ||||
| 				   unless it is already there-- this should be done before the  | ||||
| 				   call is actually dialed  */ | ||||
|  | ||||
| 				/* all ring indications and moh for the caller has been halted as soon as the  | ||||
| 				   target extension was picked up. We are going to have to kill some | ||||
| 				   time and make the caller believe the peer hasn't picked up yet */ | ||||
|  | ||||
| 				if ( strchr(transfer, 'm') ) { | ||||
| 					ast_indicate(chan, -1); | ||||
| 					ast_moh_start(chan, mohclass); | ||||
| 				} else if ( strchr(transfer, 'r') ) { | ||||
| 					ast_indicate(chan, AST_CONTROL_RINGING); | ||||
| 					sentringing++; | ||||
| 				} | ||||
|  | ||||
| 				/* Start autoservice on the other chan ?? */ | ||||
| 				res2 = ast_autoservice_start(chan); | ||||
| 				/* Now Stream the File */ | ||||
| 				if (!res2) { | ||||
| 					do { | ||||
| 						if (!res2) | ||||
| 							res2 = ast_play_and_wait(peer,"priv-callpending"); | ||||
| 						if( res2 < '1' || (privacy && res2>'5') || (screen && res2 > '4') ) /* uh, interrupting with a bad answer is ... ignorable! */ | ||||
| 							res2 = 0; | ||||
| 						 | ||||
| 						/* priv-callpending script:  | ||||
| 						   "I have a caller waiting, who introduces themselves as:" | ||||
| 						*/ | ||||
| 						if (!res2) | ||||
| 							res2 = ast_play_and_wait(peer,privintro); | ||||
| 						if( res2 < '1' || (privacy && res2>'5') || (screen && res2 > '4') ) /* uh, interrupting with a bad answer is ... ignorable! */ | ||||
| 							res2 = 0; | ||||
| 						/* now get input from the called party, as to their choice */ | ||||
| 						if( !res2 ) { | ||||
| 							if( privacy ) | ||||
| 								res2 = ast_play_and_wait(peer,"priv-callee-options"); | ||||
| 							if( screen ) | ||||
| 								res2 = ast_play_and_wait(peer,"screen-callee-options"); | ||||
| 						} | ||||
| 						/* priv-callee-options script: | ||||
| 							"Dial 1 if you wish this caller to reach you directly in the future, | ||||
| 								and immediately connect to their incoming call | ||||
| 							 Dial 2 if you wish to send this caller to voicemail now and  | ||||
| 								forevermore. | ||||
| 							 Dial 3 to send this callerr to the torture menus, now and forevermore. | ||||
| 							 Dial 4 to send this caller to a simple "go away" menu, now and forevermore. | ||||
| 							 Dial 5 to allow this caller to come straight thru to you in the future, | ||||
| 						but right now, just this once, send them to voicemail." | ||||
| 						*/ | ||||
| 				 | ||||
| 						/* screen-callee-options script: | ||||
| 							"Dial 1 if you wish to immediately connect to the incoming call | ||||
| 							 Dial 2 if you wish to send this caller to voicemail. | ||||
| 							 Dial 3 to send this callerr to the torture menus. | ||||
| 							 Dial 4 to send this caller to a simple "go away" menu. | ||||
| 						*/ | ||||
| 						if( !res2 || res2 < '1' || (privacy && res2 > '5') || (screen && res2 > '4') ) { | ||||
| 							/* invalid option */ | ||||
| 							res2 = ast_play_and_wait(peer,"vm-sorry"); | ||||
| 						} | ||||
| 						loopcount++; /* give the callee a couple chances to make a choice */ | ||||
| 					} while( (!res2 || res2 < '1' || (privacy && res2 > '5') || (screen && res2 > '4')) && loopcount < 2 ); | ||||
| 				} | ||||
|  | ||||
| 				switch(res2) { | ||||
| 				case '1': | ||||
| 					if( privacy ) { | ||||
| 						if (option_verbose > 2) | ||||
| 							ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n", privdb, privcid); | ||||
| 						ast_privacy_set(privdb,privcid,AST_PRIVACY_ALLOW); | ||||
| 					} | ||||
| 					break; | ||||
| 				case '2': | ||||
| 					if( privacy ) { | ||||
| 						if (option_verbose > 2) | ||||
| 							ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to DENY\n", privdb, privcid); | ||||
| 						ast_privacy_set(privdb,privcid,AST_PRIVACY_DENY); | ||||
| 					} | ||||
| 					if ( strchr(transfer, 'm') ) { | ||||
| 						ast_moh_stop(chan); | ||||
| 					} else if ( strchr(transfer, 'r') ) { | ||||
| 						ast_indicate(chan, -1); | ||||
| 						sentringing=0; | ||||
| 					} | ||||
| 					res2 = ast_autoservice_stop(chan); | ||||
| 					ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */ | ||||
| 					res=0; | ||||
| 					goto out; | ||||
| 					break; | ||||
| 				case '3': | ||||
| 					if( privacy ) { | ||||
| 						ast_privacy_set(privdb,privcid,AST_PRIVACY_TORTURE); | ||||
| 						if (option_verbose > 2) | ||||
| 							ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to TORTURE\n", privdb, privcid); | ||||
| 					} | ||||
| 					ast_copy_string(status, "TORTURE", sizeof(status)); | ||||
| 					 | ||||
| 					res = 0; | ||||
| 					if ( strchr(transfer, 'm') ) { | ||||
| 						ast_moh_stop(chan); | ||||
| 					} else if ( strchr(transfer, 'r') ) { | ||||
| 						ast_indicate(chan, -1); | ||||
| 						sentringing=0; | ||||
| 					} | ||||
| 					res2 = ast_autoservice_stop(chan); | ||||
| 					ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ | ||||
| 					goto out; /* Is this right? */ | ||||
| 					break; | ||||
| 				case '4': | ||||
| 					if( privacy ) { | ||||
| 						if (option_verbose > 2) | ||||
| 							ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to KILL\n", privdb, privcid); | ||||
| 						ast_privacy_set(privdb,privcid,AST_PRIVACY_KILL); | ||||
| 					} | ||||
|  | ||||
| 					ast_copy_string(status, "DONTCALL", sizeof(status)); | ||||
| 					res = 0; | ||||
| 					if ( strchr(transfer, 'm') ) { | ||||
| 						ast_moh_stop(chan); | ||||
| 					} else if ( strchr(transfer, 'r') ) { | ||||
| 						ast_indicate(chan, -1); | ||||
| 						sentringing=0; | ||||
| 					} | ||||
| 					res2 = ast_autoservice_stop(chan); | ||||
| 					ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ | ||||
| 					goto out; /* Is this right? */ | ||||
| 					break; | ||||
| 				case '5': | ||||
| 					if( privacy ) { | ||||
| 						if (option_verbose > 2) | ||||
| 							ast_verbose( VERBOSE_PREFIX_3 "--Set privacy database entry %s/%s to ALLOW\n", privdb, privcid); | ||||
| 						ast_privacy_set(privdb,privcid,AST_PRIVACY_ALLOW); | ||||
| 					 | ||||
| 						if ( strchr(transfer, 'm') ) { | ||||
| 							ast_moh_stop(chan); | ||||
| 						} else if ( strchr(transfer, 'r') ) { | ||||
| 							ast_indicate(chan, -1); | ||||
| 							sentringing=0; | ||||
| 						} | ||||
| 						res2 = ast_autoservice_stop(chan); | ||||
| 						ast_hangup(peer); /* hang up on the caller -- he didn't want to talk anyway! */ | ||||
| 						res=0; | ||||
| 						goto out; | ||||
| 						break; | ||||
| 					} /* if not privacy, then 5 is the same as "default" case */ | ||||
| 				default: | ||||
| 					/* well, if the user messes up, ... he had his chance... What Is The Best Thing To Do?  */ | ||||
| 					/* well, there seems basically two choices. Just patch the caller thru immediately, | ||||
| 				                  or,... put 'em thru to voicemail. */ | ||||
| 					/* since the callee may have hung up, let's do the voicemail thing, no database decision */ | ||||
| 					if (option_verbose > 2) | ||||
| 						ast_log(LOG_NOTICE,"privacy: no valid response from the callee. Sending the caller to voicemail, the callee isn't responding\n"); | ||||
| 					if ( strchr(transfer, 'm') ) { | ||||
| 						ast_moh_stop(chan); | ||||
| 					} else if ( strchr(transfer, 'r') ) { | ||||
| 						ast_indicate(chan, -1); | ||||
| 						sentringing=0; | ||||
| 					} | ||||
| 					res2 = ast_autoservice_stop(chan); | ||||
| 					ast_hangup(peer); /* hang up on the callee -- he didn't want to talk anyway! */ | ||||
| 					res=0; | ||||
| 					goto out; | ||||
| 					break; | ||||
| 				} | ||||
| 				if ( strchr(transfer, 'm') ) { | ||||
| 					ast_moh_stop(chan); | ||||
| 				} else if ( strchr(transfer, 'r') ) { | ||||
| 					ast_indicate(chan, -1); | ||||
| 					sentringing=0; | ||||
| 				} | ||||
| 				res2 = ast_autoservice_stop(chan); | ||||
| 				/* if the intro is NOCALLERID, then there's no reason to leave it on disk, it'll  | ||||
| 				   just clog things up, and it's not useful information, not being tied to a CID */ | ||||
| 				if( strncmp(privcid,"NOCALLERID",10) == 0 || no_save_intros ) { | ||||
| 					ast_filedelete(privintro, NULL); | ||||
| 					if( ast_fileexists(privintro,NULL,NULL ) > 0 ) | ||||
| 						ast_log(LOG_NOTICE,"privacy: ast_filedelete didn't do its job on %s\n", privintro); | ||||
| 					else if (option_verbose > 2) | ||||
| 						ast_verbose( VERBOSE_PREFIX_3 "Successfully deleted %s intro file\n", privintro); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if (announce && announcemsg) { | ||||
| 			/* Start autoservice on the other chan */ | ||||
| 			res = ast_autoservice_start(chan); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user