Index: apps/app_voicemail.c
===================================================================
RCS file: /usr/cvsroot/asterisk/apps/app_voicemail.c,v
retrieving revision 1.60
diff -u -r1.60 app_voicemail.c
--- apps/app_voicemail.c	8 Mar 2004 05:04:11 -0000	1.60
+++ apps/app_voicemail.c	8 Mar 2004 16:15:46 -0000
@@ -38,6 +38,7 @@
 #include <sys/types.h>
 #include <time.h>
 #include <dirent.h>
+#include <ctype.h> //needed for convert
 
 /* we define USESQLVM when we have MySQL or POSTGRES */
 #ifdef USEMYSQLVM
@@ -84,7 +85,12 @@
 #define eol "\r\n"
 
 #define MAX_DATETIME_FORMAT	512
+#define MAX_NUM_CID_CONTEXTS 10
+
 #define DIGITS_DIR	AST_SOUNDS "/digits/"
+
+static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller);
+
 struct baseio {
 	int iocp;
 	int iolen;
@@ -104,8 +110,11 @@
 	char serveremail[80];
 	char mailcmd[160];	/* Configurable mail command */
 	char zonetag[80];
+	char callback[80];
+	char dialout[80];
 	int attach;
 	int alloced;
+	int saycid;
 	struct ast_vm_user *next;
 };
 
@@ -133,6 +142,39 @@
 	int repeats;
 };
 
+//to be used as a linked list of members in a voicemail group
+struct ast_vm_group_member {
+	char mailbox[80]; //this is the voice mailbox number for a user
+	char context[80]; //the context that this mailbox exists in
+	struct ast_vm_group_member *next; //what is the next member in the list?
+};
+
+/*
+** this is to store members into groups, so that we can forward 1 message to many
+** Edit voicemail.conf adding, note the name *must* be [groups]
+**  [groups]
+**  ;format
+**  ;GROUPNAME,GROUPANNOUNCEMSG,MEMBER1@context,MEMBER2@context,MEMBER3@context,...
+**  ;example:
+**  Engineering,vm-engineeringgroup,4001@corporate,4002@corporate,4006@corporate,4010@someother
+*/
+struct ast_vm_group {
+	char name[80]; //the group name (must be unique)
+	char id[80]; //the id number that is read off for groups (must be unique)
+	char announce[80]; //the audio file to play to the forwarder
+	struct ast_vm_group_member *member; //this is our list of members
+	struct ast_vm_group *next; //this is the next group
+};
+
+static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
+static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
+static char *ast_vm_convert(char *lastname, int total_digits);
+static struct ast_vm_user *ast_vm_directory_main(struct ast_channel *chan, char *dir, char *context, int use_first_name);
+static int ast_vm_forwardgroup_main(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *curdir, int curmsg, char *fmt);
+static struct ast_vm_group *ast_vm_find_group(char *groupId);
+static struct ast_channel *find_channel_for_name(char *name);
+
+
 static char *tdesc = "Comedian Mail (Voicemail System)";
 
 static char *adapp = "CoMa";
@@ -183,6 +225,11 @@
 struct ast_vm_user *usersl;
 struct vm_zone *zones = NULL;
 struct vm_zone *zonesl = NULL;
+
+//groups
+struct ast_vm_group *groups = NULL; //this is our global for the current groups
+struct ast_vm_group *groupsl = NULL; //this is our global for groups
+
 static int attach_voicemail;
 static int maxsilence;
 static int silencethreshold = 128;
@@ -195,6 +242,9 @@
 static int skipms;
 static int maxlogins;
 
+static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
+
+
 static char *emailbody = NULL;
 static int pbxskip = 0;
 static char fromstring[100];
@@ -223,6 +273,16 @@
 				strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
 			} else if (!strcasecmp(var, "tz")) {
 				strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
+			} else if (!strcasecmp(var, "saycid")){
+				if(ast_true(value))
+					vmu->saycid = 1;
+				else
+					vmu->saycid = 0;
+			} else if (!strcasecmp(var, "callback")) {
+				strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
+			} else if (!strcasecmp(var, "dialout")) {
+				strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
+
 			}
 		}
 	}
@@ -260,7 +320,6 @@
 	PQfinish(dbhandler);
 }
 
-
 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
 {
 	PGresult *PGSQLres;
@@ -920,6 +979,36 @@
 	return d;
 }
 
+/* This function simplifies making a large amount of calls to play and wait by hand
+** Example:
+**  play_and_wait_multi(chan,"vm-this,vm-that,vm-theother")
+** There is only one drawback, you don't know when you have returned, just that you have
+** Note:
+**  originally this was using va_args, but I decided against it since the last value == NULL
+**  is annoying to add, and there may not be gurantees of order
+*/
+static int play_and_wait_multi(struct ast_channel *chan, char *files)
+{
+	int result;
+	char *fn;
+	char *tmp_files;
+
+	//strncpy(tmp_files,files,sizeof(tmp_files)); //we need a copy so we don't mangle the original list
+	tmp_files = ast_strdupa(files);//is this the preferred way to copy strings in asterisk?
+
+	result = 0;
+
+	//for each filename passed in, execute play_and_wait if return == 0 return from multi
+	while((fn = strsep(&tmp_files, ","))){
+		result = play_and_wait(chan,fn);
+		if(result){ //was there an issue or key press? if so return now
+			break;
+		}
+	}
+
+	return result;
+}
+
 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int beep)
 {
 	char d = 0, *fmts;
@@ -935,13 +1024,13 @@
 	int totalsilence = 0;
 	int dspsilence = 0;
 	int gotsilence = 0;		/* did we timeout for silence? */
-	int rfmt=0;	
+	int rfmt=0;
 	char prependfile[80];
-	
-	ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
+
+	ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
 	snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
 
-	if (playfile || beep) {	
+	if (playfile || beep) {
 		if (!beep)
 			d = play_and_wait(chan, playfile);
 		if (d > -1)
@@ -951,16 +1040,16 @@
 		if (d < 0)
 			return -1;
 	}
-	strncpy(prependfile, recordfile, sizeof(prependfile) -1);	
+	strncpy(prependfile, recordfile, sizeof(prependfile) -1);
 	strcat(prependfile, "-prepend");
-			
+
 	fmts = ast_strdupa(fmt);
-	
+
 	stringp=fmts;
 	strsep(&stringp, "|");
-	ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);	
+	ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
 	sfmt[0] = ast_strdupa(fmts);
-	
+
 	while((fmt = strsep(&stringp, "|"))) {
 		if (fmtcnt > MAX_OTHER_FORMATS - 1) {
 			ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
@@ -978,7 +1067,7 @@
 			break;
 		}
 	}
-	
+
 	sildet = ast_dsp_new(); //Create the silence detector
 	if (!sildet) {
 		ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
@@ -994,7 +1083,7 @@
 			return -1;
 		}
 	}
-						
+
 	if (x == fmtcnt) {
 	/* Loop forever, writing the packets we read to the writer(s), until
 	   we read a # or get a hangup */
@@ -1025,7 +1114,7 @@
 						break;
 					res = ast_writestream(others[x], f);
 				}
-				
+
 				/* Silence Detection */
 				if (maxsilence > 0) {
 					dspsilence = 0;
@@ -1089,7 +1178,7 @@
 #endif
 		}
 	} else {
-		ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
+		ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]);
 	}
 #if 0
 	if (outmsg > 1) {
@@ -1131,7 +1220,7 @@
 			ast_streamfile(chan, "vm-msgsaved", chan->language);
 			ast_waitstream(chan, "");
 		}
-	}	
+	}
 	return res;
 }
 
@@ -1149,12 +1238,12 @@
 	int totalsilence = 0;
 	int dspsilence = 0;
 	int gotsilence = 0;		/* did we timeout for silence? */
-	int rfmt=0;	
-	
+	int rfmt=0;
+
 	ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
 	snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
 
-	if (playfile) {	
+	if (playfile) {
 		d = play_and_wait(chan, playfile);
 		if (d > -1)
 			d = ast_streamfile(chan, "beep",chan->language);
@@ -1163,14 +1252,14 @@
 		if (d < 0)
 			return -1;
 	}
-	
+
 	fmts = ast_strdupa(fmt);
-	
+
 	stringp=fmts;
 	strsep(&stringp, "|");
-	ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);	
+	ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
 	sfmt[0] = ast_strdupa(fmts);
-	
+
 	while((fmt = strsep(&stringp, "|"))) {
 		if (fmtcnt > MAX_OTHER_FORMATS - 1) {
 			ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
@@ -1189,7 +1278,7 @@
 			break;
 		}
 	}
-	
+
 	sildet = ast_dsp_new(); //Create the silence detector
 	if (!sildet) {
 		ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
@@ -1205,7 +1294,7 @@
 			return -1;
 		}
 	}
-						
+
 	if (x == fmtcnt) {
 	/* Loop forever, writing the packets we read to the writer(s), until
 	   we read a # or get a hangup */
@@ -1221,7 +1310,7 @@
 					res = -1;
 				}
 			}
-			
+
 			if (res < 0) {
 				f = NULL;
 				break;
@@ -1243,7 +1332,7 @@
 						totalsilence = dspsilence;
 					else
 						totalsilence = 0;
-					
+
 					if (totalsilence > maxsilence) {
 					/* Ended happily with silence */
 					ast_frfree(f);
@@ -1263,7 +1352,7 @@
 				ast_writestream(others[0], f);
 			} else if (f->frametype == AST_FRAME_DTMF) {
 				if (f->subclass == '#') {
-					if (option_verbose > 2) 
+					if (option_verbose > 2)
 						ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
 					res = '#';
 					outmsg = 2;
@@ -1284,7 +1373,7 @@
 			ast_frfree(f);
 		}
 		if (!f) {
-			if (option_verbose > 2) 
+			if (option_verbose > 2)
 				ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
 			res = -1;
 			outmsg=1;
@@ -1310,13 +1399,13 @@
 	}
 	if (outmsg) {
 		if (outmsg > 1) {
-		/* Let them know it worked */
-			ast_streamfile(chan, "vm-msgsaved", chan->language);
+		/* Let them know recording is stopped */
+			ast_streamfile(chan, "auth-thankyou", chan->language);
 			ast_waitstream(chan, "");
 		}
-	}	
+	}
+
 
-	
 	return res;
 }
 
@@ -1331,13 +1420,65 @@
 	free(z);
 }
 
-static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
+static void free_vm_group_member(struct ast_vm_group_member *member)
+{
+	if(member){
+		free(member);
+	}
+}
+
+/*
+** scan the list of global groups and try to find the group in question
+** returns NULL on failure
+** else returns address to the group
+*/
+static struct ast_vm_group *ast_vm_find_group(char *groupId){
+	struct ast_vm_group *tmp_group;
+	if(groups){
+		tmp_group = groups;
+		while(tmp_group){
+			if(!strncmp(tmp_group->id,groupId,sizeof(tmp_group->id))){
+				//we found it
+				return tmp_group;
+			}
+			tmp_group = tmp_group->next;
+		}
+	}
+	return NULL;
+}
+
+//clean up our group
+static void free_vm_group(struct ast_vm_group *group)
+{
+	struct ast_vm_group_member *tmp_member;
+	tmp_member = NULL;
+	if(group){
+		//do we have member undeneath us?
+		if(group->member){
+			while(group->member){
+				tmp_member = group->member;
+				ast_verbose( VERBOSE_PREFIX_3 "free_vm_group: freeing member '%s@%s'\n",tmp_member->mailbox,tmp_member->context);
+				group->member = group->member->next;
+				free_vm_group_member(tmp_member);
+			}
+		}
+		//time to free ourselves
+		free(group);
+	}
+}
+
+/* 02-26-04
+** Jesse Janzer, I made a modification to leave voicemail even though I really
+**  didn't want to, which is that msgnum gets passed in via psuedo-reference.
+**  This is so I know where the msg was created, else I seem to be out of luck
+**   and would have had to create another function
+*/
+static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail, int *msgnum)
 {
 	char comment[256];
 	char txtfile[256];
 	FILE *txt;
 	int res = 0;
-	int msgnum;
 	int fd;
 	char date[256];
 	char dir[256];
@@ -1352,7 +1493,7 @@
 	char tmp[256] = "";
 	struct ast_vm_user *vmu;
 	struct ast_vm_user svm;
-	
+
 	strncpy(tmp, ext, sizeof(tmp) - 1);
 	ext = tmp;
 	context = strchr(tmp, '@');
@@ -1383,10 +1524,14 @@
 		/* Play the beginning intro if desired */
 		if (strlen(prefile)) {
 			if (ast_fileexists(prefile, NULL, NULL) > 0) {
-				if (ast_streamfile(chan, prefile, chan->language) > -1) 
+				if (ast_streamfile(chan, prefile, chan->language) > -1)
 				    res = ast_waitstream(chan, "#0");
+						//Jesse Janzer, I added the stopstream
+						// otherwise we have to listen to the wholeeeee message when hitting 0
+						ast_stopstream(chan);
 			} else {
 				ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
+				ast_log(LOG_WARNING, "Calling invent_message\n");
 				res = invent_message(chan, vmu->context, ext, busy, ecodes);
 			}
 			if (res < 0) {
@@ -1400,17 +1545,10 @@
 			silent = 1;
 			res = 0;
 		}
-		if (!res && !silent) {
-			res = ast_streamfile(chan, INTRO, chan->language);
-			if (!res)
-				res = ast_waitstream(chan, ecodes);
-			if (res == '#') {
-				silent = 1;
-				res = 0;
-			}
-		}
+
 		/* Check for a '0' here */
 		if (res == '0') {
+		transfer:
 			strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
 			if (strlen(chan->macrocontext))
 				strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
@@ -1418,6 +1556,16 @@
 			free_user(vmu);
 			return 0;
 		}
+
+		if (!res && !silent) {
+			res = ast_streamfile(chan, INTRO, chan->language);
+			if (!res)
+				res = ast_waitstream(chan, ecodes);
+			if (res == '#') {
+				silent = 1;
+				res = 0;
+			}
+		}
 		if (res >= 0) {
 			/* Unless we're *really* silent, try to send the beep */
 			res = ast_streamfile(chan, "beep", chan->language);
@@ -1431,30 +1579,31 @@
 		/* The meat of recording the message...  All the announcements and beeps have been played*/
 		strncpy(fmt, vmfmts, sizeof(fmt) - 1);
 		if (strlen(fmt)) {
-			msgnum = 0;
+			(*msgnum) = 0;
 			do {
-				make_file(fn, sizeof(fn), dir, msgnum);
+				make_file(fn, sizeof(fn), dir, (*msgnum));
 				snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
 									(chan->callerid ? chan->callerid : "Unknown"), 
 									vmu->fullname, ext, chan->name);
 				if (ast_fileexists(fn, NULL, chan->language) <= 0) 
 					break;
-				msgnum++;
-			} while(msgnum < MAXMSG);
-			if (msgnum < MAXMSG) {
+				(*msgnum)++;
+			} while((*msgnum) < MAXMSG);
+			if ((*msgnum) < MAXMSG) {
 				/* Store information */
 				snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
  				txt = fopen(txtfile, "w+");
 				if (txt) {
 					get_date(date, sizeof(date));
 					time(&start);
-					fprintf(txt, 
+					fprintf(txt,
 ";\n"
 "; Message Information file\n"
 ";\n"
 "[message]\n"
 "origmailbox=%s\n"
 "context=%s\n"
+"macrocontext=%s\n"
 "exten=%s\n"
 "priority=%d\n"
 "callerchan=%s\n"
@@ -1463,6 +1612,7 @@
 "origtime=%ld\n",
 	ext,
 	chan->context,
+	chan->macrocontext, 
 	chan->exten,
 	chan->priority,
 	chan->name,
@@ -1471,7 +1621,9 @@
 					fclose(txt);
 				} else
 					ast_log(LOG_WARNING, "Error opening text file for output\n");
-				res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
+				res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 0); //NOTE: i turned off outside caller
+				if (res == '0')
+					goto transfer;
 				if (res > 0)
 					res = 0;
 				fd = open(txtfile, O_APPEND | O_WRONLY);
@@ -1494,13 +1646,13 @@
 						attach_user_voicemail = vmu->attach;
 					if (strlen(vmu->serveremail))
 						myserveremail = vmu->serveremail;
-					sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
+					sendmail(myserveremail, vmu, (*msgnum), ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
 				}
 				if (strlen(vmu->pager)) {
 					char *myserveremail = serveremail;
 					if (strlen(vmu->serveremail))
 						myserveremail = vmu->serveremail;
-					sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
+					sendpage(myserveremail, vmu->pager, (*msgnum), ext, chan->callerid, end - start, vmu);
 				}
 			} else {
 				res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
@@ -1509,7 +1661,7 @@
 				ast_log(LOG_WARNING, "No more messages possible\n");
 			}
 		} else
-			ast_log(LOG_WARNING, "No format for saving voicemail?\n");					
+			ast_log(LOG_WARNING, "No format for saving voicemail?\n");
 		free_user(vmu);
 	} else {
 		ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
@@ -1993,7 +2145,7 @@
 	}
 
 	/* If deleted, show "undeleted" */
-	if (deleted) 
+	if (deleted)
 		keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
 
 	/* Except "Exit" */
@@ -2178,6 +2330,7 @@
 			char file[200];
 			snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
 			cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, 1);
+			cmd = 't';
 			break;
 		}
 		case '2': 
@@ -2186,7 +2339,7 @@
 		case '#':
 			cmd = '#';
 			break;
-		default: 
+		default:
 			cmd = play_and_wait(chan,"vm-forwardoptions");
 			if (!cmd)
 				cmd = ast_waitfordigit(chan,6000);
@@ -2198,14 +2351,21 @@
 	}
 	if (cmd == 't')
 		cmd = 0;
+
 	return cmd;
 }
 
-static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
+/* Jesse Janzer, I added vmtmp to be an arg, if set it assumes silence and doesn't give any prompting
+**  I also added vms, so I can properly delete messages, vms has to be passed along
+*/
+
+static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, struct vm_state *vms, char *fmt, struct ast_vm_user *vmtmp)
 {
 	char username[70];
 	char sys[256];
 	char todir[256];
+	char greeting[256];
+	char buff[80];
 	int todircount=0;
 	long duration;
 	struct ast_config *mif;
@@ -2213,34 +2373,66 @@
 	char fn[256];
 	char callerid[512];
 	int res = 0, cmd = 0;
-	struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
+	struct ast_vm_user *receiver = NULL, *extensions = NULL, *vmfree = NULL;
 	char tmp[256];
-	char *stringp, *s;
+	char *stringp = NULL, *s = NULL;
+	char group_option_key[2];
 	int saved_messages = 0, found = 0;
 	int valid_extensions = 0;
-	while (!res && !valid_extensions) {
-		res = ast_streamfile(chan, "vm-extension", chan->language);
-		if (res)
-			break;
-		if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
+	int silent = 0;
+	group_option_key[0] = '*';
+	group_option_key[1] = '\0';
+	if(vmtmp)
+		silent = 1;
+	//if we already have a user, don't bother prompting
+	while (!silent && (!res && !valid_extensions)) {
+		username[0] = '\0';
+		buff[0] = '\0';
+		res = play_and_wait(chan, "vm-fwd-forwardinstr");
+		if(res > 0){ //did they push a key while we were in the prompt?
+			username[0] = res;
+			username[1] = '\0';
+		}
+
+		if ((res = ast_readstring(chan, buff, sizeof(buff) - 1, 2000, 10000, "#") < 0))
 			break;
+
+		strncat(username, buff, sizeof(username) - 1); //copy any remaining keys onto the username
+
 		/* start all over if no username */
 		if (!strlen(username))
 			continue;
 		stringp = username;
+
+		if(!strncmp(stringp, group_option_key, strlen(stringp))){
+			ast_verbose( VERBOSE_PREFIX_3 "User pressed * for more forwarding group options\n");
+			//jump out to the group code
+			cmd = ast_vm_forwardgroup_main(chan,sender,vms,dir,curmsg,NULL);
+			return 0;
+		}
 		s = strsep(&stringp, "*");
 		/* start optimistic */
 		valid_extensions = 1;
 		while (s) {
 			/* find_user is going to malloc since we have a NULL as first argument */
+			//looping with this sounds like a potential memory leak w/o this check
+			if(receiver){
+				free_user(receiver);
+				receiver = NULL;
+			}
 			if ((receiver = find_user(NULL, context, s))) {
-				if (!extensions)
+				/*if (!extensions)
 					vmtmp = extensions = receiver;
+					vmtmp->next = NULL;
 				else {
-					vmtmp->next = receiver;
 					vmtmp = receiver;
-				}
+					vmtmp->next = NULL;
+				}*/
+				//we found a match
+				vmtmp = extensions = receiver;
+				vmtmp->next = NULL;
 				found++;
+				break; //jump out we found one
 			} else {
 				valid_extensions = 0;
 				break;
@@ -2248,21 +2440,48 @@
 			s = strsep(&stringp, "*");
 		}
 		/* break from the loop of reading the extensions */
-		if (valid_extensions)
-			break;
-		/* invalid extension, try again */
-		res = play_and_wait(chan, "pbx-invalid");
+		if (valid_extensions){
+
+			if(!silent){
+				//tell them who they are forwarding to and let them cancel if they want
+				snprintf(greeting, sizeof(greeting), "%s/voicemail/%s/%s/greet", (char *)ast_config_AST_SPOOL_DIR, receiver->context, receiver->mailbox);
+				if (ast_fileexists(greeting, NULL, chan->language) > 0) {
+					cmd = play_and_wait(chan,greeting);
+				} else {
+					//they don't have a greeting, so let's play the digits
+					cmd = ast_say_digit_str(chan, receiver->mailbox, AST_DIGIT_ANY, chan->language);
+				}
+				if(!cmd) //allow them to cancel by hitting '*'
+					cmd = play_and_wait_multi(chan,"vm-fwd-tocancel,vm-press,digits/star");
+				if(!cmd) //they have 3 seconds to cancel
+					cmd = ast_waitfordigit(chan, 3000);
+				switch(cmd){
+					case '*': //they want to cancel
+						valid_extensions = 0; //reset valid_extensions triggering another loop
+						break;
+					default:
+						break;
+				}
+			}
+
+			//double check ourselves again
+			if(valid_extensions)
+				break;
+
+		} else {
+			/* invalid extension, try again */
+			res = play_and_wait(chan, "pbx-invalid");
+		}
 	}
 	/* check if we're clear to proceed */
-	if (!extensions || !valid_extensions)
+	if (!silent && (!extensions || !valid_extensions))
 		return res;
-	vmtmp = extensions;
-	cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
+	if(!vmtmp) //if not already set, set now
+		vmtmp = extensions;
+	if(!silent) //if we aren't silent, give them more options
+		cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
 
 	while(!res && vmtmp) {
-		/* if (play_and_wait(chan, "vm-savedto"))
-			break;
-		*/
 		snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
 		snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
 		ast_log(LOG_DEBUG, sys);
@@ -2322,7 +2541,7 @@
 		vmtmp = vmtmp->next;
 		free_user(vmfree);
 	}
-	if (saved_messages > 0) {
+	if (!silent && (saved_messages > 0)) {
 		/* give confirmatopm that the message was saved */
 		if (saved_messages == 1)
 			res = play_and_wait(chan, "vm-message");
@@ -2345,7 +2564,7 @@
 	return res;
 }
 
-static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
+static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
 {
 	int res;
 	if ((res = ast_streamfile(chan, file, chan->language)))
@@ -2355,31 +2574,18 @@
 	return res;
 }
 
-static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
+static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
 {
 	int res = 0;
-	char filename[256], *origtime;
 	struct vm_zone *the_zone = NULL;
-	struct ast_config *msg_cfg;
 	time_t t;
 	long tin;
 
-	make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
-	snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
-	msg_cfg = ast_load(filename);
-	if (!msg_cfg) {
-		ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
-		return 0;
-	}
-
-	if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
-		return 0;
 	if (sscanf(origtime,"%ld",&tin) < 1) {
 		ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
 		return 0;
 	}
 	t = tin;
-	ast_destroy(msg_cfg);
 
 	/* Does this user have a timezone specified? */
 	if (strlen(vmu->zonetag)) {
@@ -2422,10 +2628,76 @@
 	return res;
 }
 
+
+
+static int play_message_callerid( struct ast_channel *chan, struct vm_state *vms, char *cid, char *context)
+{
+	int res = 0;
+	int i;
+	char *callerid, *name;
+	char prefile[256]="";
+	
+
+	/* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
+	/* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
+	if((cid == NULL)||(context == NULL))
+		return res;
+
+	/* Strip off caller ID number from name */
+	ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
+	ast_callerid_parse(cid, &name, &callerid);
+	if((callerid != NULL)&&(!res)&&(strlen(callerid))){
+		/* Check for internal contexts and only */
+		/* say extension when the call didn't come from an internal context in the list */
+		for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
+			ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
+			if((strcmp(cidinternalcontexts[i], context) == 0))
+				break;
+		}
+		if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
+			if(!res) {
+				snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
+				if (strlen(prefile)) {
+				/* See if we can find a recorded name for this person instead of their extension number */
+					if (ast_fileexists(prefile, NULL, NULL) > 0) {
+						ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
+						res = wait_file2(chan, vms, "vm-message-from");
+						res = ast_streamfile(chan, prefile, chan->language) > -1;
+						res = ast_waitstream(chan, "");
+					} else {
+						ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
+						/* BB: Say "from extension" as one saying to sound smoother */
+						res = wait_file2(chan, vms, "vm-message-from");
+						res = ast_say_digit_str(chan, callerid, "", chan->language);
+					}
+				}
+			}
+		}
+
+		else if (!res){
+			ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
+			/* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
+			res = wait_file2(chan, vms, "vm-message-from");
+			res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
+		}
+	}
+	else{
+		/* Number unknown */
+		ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
+		if(!res)
+			/* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
+			res = wait_file2(chan, vms, "vm-unknown-caller");
+	}
+	return res;                                                               
+}
+
 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
 {
 	int res = 0;
-	vms->starting = 0; 
+	char filename[256],*origtime, *cid, *context;
+	struct ast_config *msg_cfg;
+
+	vms->starting = 0;
 	make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
 	adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
 	if (!msg)
@@ -2440,8 +2712,32 @@
 		}
 	}
 
+	/* Retrieve info from VM attribute file */
+
+        make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+        snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
+        msg_cfg = ast_load(filename);
+        if (!msg_cfg) {
+                ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
+                return 0;
+        }
+                                                                                                                                 
+        if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
+                return 0;
+                                                                                                                                 
+        cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
+
+        context = ast_variable_retrieve(msg_cfg, "message", "context");
+	if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
+		context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
+
 	if (!res)
-		res = play_message_datetime(chan,vmu,vms);
+		res = play_message_datetime(chan, vmu, origtime, filename);
+
+	if ((!res)&&(vmu->saycid))
+		res = play_message_callerid(chan, vms, cid, context);
+
+	ast_destroy(msg_cfg);
 
 	if (!res) {
 		make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
@@ -2464,39 +2760,39 @@
 	int x;
 	char ntxt[256] = "";
 	char txt[256] = "";
-	if (vms->lastmsg > -1) { 
-		/* Get the deleted messages fixed */ 
-		vms->curmsg = -1; 
-		for (x=0;x < MAXMSG;x++) { 
-			if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
-				/* Save this message.  It's not in INBOX or hasn't been heard */ 
-				make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
-				if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
+	if (vms->lastmsg > -1) {
+		/* Get the deleted messages fixed */
+		vms->curmsg = -1;
+		for (x=0;x < MAXMSG;x++) {
+			if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
+				/* Save this message.  It's not in INBOX or hasn't been heard */
+				make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
+				if (ast_fileexists(vms->fn, NULL, NULL) < 1)
 					break;
-				vms->curmsg++; 
-				make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
-				if (strcmp(vms->fn, vms->fn2)) { 
-					snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
-					snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
-					ast_filerename(vms->fn, vms->fn2, NULL); 
-					rename(txt, ntxt); 
-				} 
-			} else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
-				/* Move to old folder before deleting */ 
-				save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
-			} 
-		} 
-		for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
-			make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
-			if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
-				break;
-			snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
-			ast_filedelete(vms->fn, NULL); 
-			unlink(txt); 
-		} 
-	} 
-	memset(vms->deleted, 0, sizeof(vms->deleted)); 
-	memset(vms->heard, 0, sizeof(vms->heard)); 
+				vms->curmsg++;
+				make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+				if (strcmp(vms->fn, vms->fn2)) {
+					snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
+					snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2);
+					ast_filerename(vms->fn, vms->fn2, NULL);
+					rename(txt, ntxt);
+				}
+			} else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
+				/* Move to old folder before deleting */
+				save_to_folder(vms->curdir, x, vmu->context, vms->username, 1);
+			}
+		}
+		for (x = vms->curmsg + 1; x <= MAXMSG; x++) {
+			make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
+			if (ast_fileexists(vms->fn, NULL, NULL) < 1)
+				break;
+			snprintf(txt, sizeof(txt), "%s.txt", vms->fn);
+			ast_filedelete(vms->fn, NULL);
+			unlink(txt);
+		}
+	}
+	memset(vms->deleted, 0, sizeof(vms->deleted));
+	memset(vms->heard, 0, sizeof(vms->heard));
 }
 
 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
@@ -2615,15 +2911,15 @@
 		switch (cmd) {
 		case '1':
 			snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
-			cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
+			cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0);
 			break;
 		case '2': 
 			snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
-			cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
+			cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0);
 			break;
 		case '3': 
 			snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
-			cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
+			cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0);
 			break;
 		case '4':
 			newpassword[1] = '\0';
@@ -2690,6 +2986,7 @@
 	int logretries = 0;
 	struct ast_vm_user *vmu = NULL, vmus;
 	char *context=NULL;
+	int silentexit = 0;
 
 	LOCAL_USER_ADD(u);
 	memset(&vms, 0, sizeof(vms));
@@ -2861,6 +3158,116 @@
 					cmd = play_and_wait(chan, "vm-messages");
 				vms.starting = 1;
 				break;
+			case '3': /* Advanced options */
+				cmd = 0;
+				vms.repeats = 0;
+				while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
+					switch(cmd) {
+					case '1': /* Reply */
+						if(vms.lastmsg > -1)
+							cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1);
+						else
+							cmd = play_and_wait(chan, "vm-sorry");
+						cmd = 't';
+						break;
+					case '2': /* Callback */
+						ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
+						if (strlen(vmu->callback) && vms.lastmsg > -1) {
+							cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2);
+							if (cmd == 9) {
+								silentexit = 1;
+								goto out;
+							}
+						}
+						else 
+							cmd = play_and_wait(chan, "vm-sorry");
+						cmd = 't';
+						break;
+					case '3': /* Envelope */
+						if(vms.lastmsg > -1)
+							cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3);
+						else
+							cmd = play_and_wait(chan, "vm-sorry");
+						cmd = 't';
+						break;
+					case '4': /* Dialout */
+						if (strlen(vmu->dialout)) {
+							cmd = dialout(chan, vmu, NULL, vmu->dialout);
+							if (cmd == 9) {
+								silentexit = 1;
+								goto out;
+							}
+						}
+						else
+							cmd = play_and_wait(chan, "vm-sorry");
+						cmd = 't';
+						break;
+					case '5' : /* Send message to Group */
+						cmd = ast_vm_forwardgroup_main(chan,vmu,&vms,vms.curdir,-1,NULL);
+						cmd = 't';
+						break;
+
+					case '*': /* Return to main menu */
+						cmd = 't';
+						break;
+
+					default:
+						cmd = 0;
+						if (!vms.starting) {
+							cmd = play_and_wait(chan, "vm-press");
+							if (!cmd)
+								cmd = play_and_wait(chan, "digits/1");
+							if (!cmd)
+								cmd = play_and_wait(chan, "vm-reply");
+						}
+						if (strlen(vmu->callback) && !vms.starting && !cmd) {
+							cmd = play_and_wait(chan, "vm-press");
+							if (!cmd)
+								cmd = play_and_wait(chan, "digits/2");
+							if (!cmd)
+								cmd = play_and_wait(chan, "vm-callsender");
+						}
+
+						if (!cmd && !vms.starting) {
+							cmd = play_and_wait(chan, "vm-press");
+							if (!cmd)
+								cmd = play_and_wait(chan, "digits/3");
+							if (!cmd)
+								cmd = play_and_wait(chan, "vm-envelope");
+						}
+						if (strlen(vmu->dialout) && !cmd) {
+							cmd = play_and_wait(chan, "vm-press");
+							if (!cmd)
+								cmd = play_and_wait(chan, "digits/4");
+							if (!cmd)
+								cmd = play_and_wait(chan, "vm-outgoing");
+						}
+
+						//give the prompt to forward a message to a group
+						if (1){
+							cmd = play_and_wait(chan, "vm-fwd-5tosendamessage");
+						}
+
+						if (!cmd)
+							cmd = play_and_wait(chan, "vm-starmain");
+						if (!cmd)
+							cmd = ast_waitfordigit(chan,6000);
+						if (!cmd)
+							vms.repeats++;
+						if (vms.repeats > 3)
+							cmd = 't';
+					}
+				}
+				if (cmd == 't') {
+					cmd = 0;
+					vms.repeats = 0;
+				}
+				break;
+
+
+
+
+
 			case '4':
 				if (vms.curmsg) {
 					vms.curmsg--;
@@ -2881,14 +3288,14 @@
 				vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
 				if (useadsi)
 					adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
-				if (vms.deleted[vms.curmsg]) 
+				if (vms.deleted[vms.curmsg])
 					cmd = play_and_wait(chan, "vm-deleted");
 				else
 					cmd = play_and_wait(chan, "vm-undeleted");
 				break;
 			case '8':
 				if(vms.lastmsg > -1)
-					cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
+					cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, &vms, vmfmts, NULL);
 				else
 					cmd = play_and_wait(chan, "vm-nomore");
 				break;
@@ -2956,7 +3363,10 @@
 		ast_stopstream(chan);
 		adsi_goodbye(chan);
 		if(valid) {
-			res = play_and_wait(chan, "vm-goodbye");
+			if (silentexit)
+				res = play_and_wait(chan, "vm-dialout");
+			else 
+				res = play_and_wait(chan, "vm-goodbye");
 			if (res > 0)
 				res = 0;
 		}
@@ -2977,7 +3387,7 @@
 
 static int vm_exec(struct ast_channel *chan, void *data)
 {
-	int res=0, silent=0, busy=0, unavail=0;
+	int res=0, silent=0, busy=0, unavail=0, msgnum=0;
 	struct localuser *u;
 	char tmp[256], *ext;
 	
@@ -3007,11 +3417,116 @@
 		} else 
 			break;
 	}
-	res = leave_voicemail(chan, ext, silent, busy, unavail);
+	res = leave_voicemail(chan, ext, silent, busy, unavail, &msgnum);
 	LOCAL_USER_REMOVE(u);
 	return res;
 }
 
+//This code inserts a member into a voicemail group
+static int append_vm_group_member(struct ast_vm_group *group, char *mbox, char *context){
+	struct ast_vm_group_member *member;
+	struct ast_vm_group_member *tmp;
+	member = malloc(sizeof(struct ast_vm_group_member));
+	if(member){
+		//ok we need to copy the mbox and context into the member and insert that into the group
+		memset(member,0,sizeof(struct ast_vm_group_member));
+		strncpy(member->mailbox,mbox,sizeof(member->mailbox));
+		strncpy(member->context,context,sizeof(member->context));
+		member->next = NULL; //there is no one ahead of us
+
+		//does this group already have a member?
+		if(!group->member){
+			//nope so lets point this member into it
+			group->member = member;
+		} else {
+			//yes it does, so loop until we get to the last member then insert it there
+			tmp = group->member;
+			while(tmp){
+				//check for duplicate
+				if(!strncmp(tmp->mailbox,member->mailbox,strlen(tmp->mailbox)) &&
+					!strncmp(tmp->context,member->context,strlen(tmp->context))){
+					//this is a dupe
+					ast_log(LOG_WARNING, "Member already in group: voicemail member, '%s@%s'\n",mbox,context);
+					//free the member since we don't need it anymore
+					free_vm_group_member(member);
+					return 0;
+				}
+				if(!tmp->next){
+					//we're at the last one break out so we can set this 'next' to our new member
+					break;
+				}
+				tmp = tmp->next;
+			}
+			//ok we're at the end so insert
+			tmp->next = member;
+		}
+
+	} else {
+		ast_log(LOG_WARNING, "Failed to allocate voicemail member, '%s@%s'\n",mbox,context);
+		return -1; //failure
+	}
+	return 0;
+}
+
+/* This code will explode the users into the voicemail groups
+** Example:
+**  append_vm_group("groups","Engineering","4001@corporate,4002@corporate|vm-engineering");
+** Where "groups" is the [groups] section
+*/
+static int append_vm_group(char *section, char *groupName, char *data){
+	struct ast_vm_group *group;
+	char *users;
+	char *user;
+	char *mbox;
+	char *context;
+	char *id;
+	char *announce;
+
+	//lets alloc a new voicemail group
+	group = malloc(sizeof(struct ast_vm_group));
+	if(group){
+		//erase this group
+		memset(group,0,sizeof(struct ast_vm_group));
+		strncpy(group->name,groupName,sizeof(group->name));
+		//if the group head pointer doesn't exist set it to point to this entry
+		if(!groups){
+			groups = group;
+		}
+		if(groupsl){
+			groupsl->next = group;
+		}
+		groupsl = group; //set the last pointer to this one
+
+		//set the group id
+		id = strsep(&data,"|");
+		strncpy(group->id,id,sizeof(group->id));
+
+		//allocate group members (we can change 'data', no one cares)
+		users = strsep(&data,"|"); //get all the users before options
+		while((user = strsep(&users,","))){
+			//loop until we get no more users
+			//now we will either have something like 1234@context
+			mbox = strsep(&user,"@");
+			context = user;
+			//time to append the user
+			append_vm_group_member(group, mbox, context); //we don't really care if they fail or succeed here
+		}
+		//do we have a group announce?
+		announce = strsep(&data,"|");
+		if(announce){
+			strncpy(group->announce,announce,sizeof(group->announce));
+		}
+
+		//time to handle options
+
+	} else {
+		//allocation failed, maybe we're out of memory?
+		ast_log(LOG_WARNING, "Failed to allocate voicemail group, '%s'\n",groupName);
+		return -1; //failure
+	}
+	return 0;
+}
+
 static int append_mailbox(char *context, char *mbox, char *data)
 {
 	/* Assumes lock is already held */
@@ -3027,15 +3542,15 @@
 		strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox) - 1);
 		vmu->attach = -1;
 		stringp = tmp;
-		if ((s = strsep(&stringp, ","))) 
+		if ((s = strsep(&stringp, ",")))
 			strncpy(vmu->password, s, sizeof(vmu->password) - 1);
-		if (stringp && (s = strsep(&stringp, ","))) 
+		if (stringp && (s = strsep(&stringp, ",")))
 			strncpy(vmu->fullname, s, sizeof(vmu->fullname) - 1);
-		if (stringp && (s = strsep(&stringp, ","))) 
+		if (stringp && (s = strsep(&stringp, ",")))
 			strncpy(vmu->email, s, sizeof(vmu->email) - 1);
-		if (stringp && (s = strsep(&stringp, ","))) 
+		if (stringp && (s = strsep(&stringp, ",")))
 			strncpy(vmu->pager, s, sizeof(vmu->pager) - 1);
-		if (stringp && (s = strsep(&stringp, ","))) 
+		if (stringp && (s = strsep(&stringp, ",")))
 			apply_options(vmu, s);
 		vmu->next = NULL;
 		if (usersl)
@@ -3056,6 +3571,10 @@
 "Usage: show voicemail zones\n"
 "       Lists zone message formats\n";
 
+static char show_voicemail_groups_help[] =
+"Usage: show voicemail groups\n"
+"       Lists mailboxes@context in voicemail groups\n";
+
 static int handle_show_voicemail_users(int fd, int argc, char *argv[])
 {
 	struct ast_vm_user *vmu = users;
@@ -3130,6 +3649,34 @@
 	return RESULT_SUCCESS;
 }
 
+//do we need a lock here?
+static int handle_show_voicemail_groups(int fd, int argc, char *argv[])
+{
+	struct ast_vm_group *tmp_group;
+	struct ast_vm_group_member *tmp_member;
+
+	tmp_group = groups;
+	if(!tmp_group){
+		ast_cli(fd, "There are no voicemail groups loaded\n");
+		return RESULT_FAILURE;
+	}
+
+	//since there are some, lets print them
+	while(tmp_group){
+		//print the info about this group
+		ast_cli(fd, "%s [%s]:\n",tmp_group->name,tmp_group->id);
+		//do we have any members?
+		tmp_member = tmp_group->member;
+		while(tmp_member){
+			ast_cli(fd, "   %s@%s\n",tmp_member->mailbox,tmp_member->context);
+			tmp_member = tmp_member->next;
+		}
+		ast_cli(fd, "\n");
+		tmp_group = tmp_group->next;
+	}
+	return RESULT_SUCCESS;
+}
+
 static char *complete_show_voicemail_users(char *line, char *word, int pos, int state)
 {
 	int which = 0;
@@ -3169,10 +3716,17 @@
 	handle_show_voicemail_zones, "List zone message formats",
 	show_voicemail_zones_help, NULL };
 
+//TODO: add completion for groups
+static struct ast_cli_entry show_voicemail_groups_cli =
+	{ { "show", "voicemail", "groups", NULL },
+	handle_show_voicemail_groups, "Lists mailboxes@context in voicemail groups",
+	show_voicemail_groups_help, NULL };
+
 
 static int load_config(void)
 {
 	struct ast_vm_user *cur, *l;
+	struct ast_vm_group *curGroup, *curGroupl;
 	struct vm_zone *zcur, *zl;
 	struct ast_config *cfg;
 	char *cat;
@@ -3183,7 +3737,7 @@
 	char *fmt;
 	char *astemail;
  	char *astmailcmd = SENDMAIL;
-	char *s;
+	char *s,*q,*stringp;
 	int x;
 
 	cfg = ast_load(VOICEMAIL_CONFIG);
@@ -3201,10 +3755,23 @@
 		zcur = zcur->next;
 		free_zone(zl);
 	}
+
+	//purge all the groups and members
+	curGroup = groups;
+	while(curGroup){
+		curGroupl = curGroup;
+		curGroup = curGroup->next;
+		free_vm_group(curGroupl);
+	}
 	zones = NULL;
 	zonesl = NULL;
 	users = NULL;
 	usersl = NULL;
+	groups = NULL;
+	groupsl = NULL;
+	curGroup = NULL;
+	curGroupl = NULL;
+
 	if (cfg) {
 		/* General settings */
 
@@ -3231,7 +3798,7 @@
 		silencethreshold = 256;
 		if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
 			silencethreshold = atoi(thresholdstr);
-		
+
 		if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
 			astemail = ASTERISK_USERNAME;
 		strncpy(serveremail, astemail, sizeof(serveremail) - 1);
@@ -3246,7 +3813,7 @@
 		}
 		fmt = ast_variable_retrieve(cfg, "general", "format");
 		if (!fmt)
-			fmt = "wav";	
+			fmt = "wav";
 		strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
 
 		skipms = 3000;
@@ -3275,6 +3842,21 @@
 			}
 		}
 
+		if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
+			ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
+			stringp = ast_strdupa(s);
+			for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
+				if ((stringp)&&(strlen(stringp))){
+					q = strsep(&stringp,",");
+					while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
+						q++;
+					strcpy(cidinternalcontexts[x],q);
+					ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
+				} else {
+					cidinternalcontexts[x][0] = '\0';
+				}
+			}
+		}
 #ifdef USEMYSQLVM
 		if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
 			strcpy(dbuser, "test");
@@ -3309,7 +3891,15 @@
 		while(cat) {
 			if (strcasecmp(cat, "general")) {
 				var = ast_variable_browse(cfg, cat);
-				if (strcasecmp(cat, "zonemessages")) {
+				if (!strcasecmp(cat, "groups")) {
+					/* Process the voicemail groups */
+					while(var){
+						ast_log(LOG_WARNING, "Found voice mail groups: %s | %s | %s\n", cat, var->name, var->value);
+						append_vm_group(cat, var->name, var->value);
+						var = var->next;
+					}
+				}
+				else if (strcasecmp(cat, "zonemessages")) {
 #ifndef USESQLVM
 					/* Process mailboxes in this context */
 					while(var) {
@@ -3414,6 +4004,7 @@
 	sql_close();
 	ast_cli_unregister(&show_voicemail_users_cli);
 	ast_cli_unregister(&show_voicemail_zones_cli);
+	ast_cli_unregister(&show_voicemail_groups_cli);
 	return res;
 }
 
@@ -3437,6 +4028,7 @@
 	}
 	ast_cli_register(&show_voicemail_users_cli);
 	ast_cli_register(&show_voicemail_zones_cli);
+	ast_cli_register(&show_voicemail_groups_cli);
 	return res;
 }
 
@@ -3445,6 +4037,966 @@
 	return tdesc;
 }
 
+static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
+{
+	int cmd = 0;
+	char destination[80] = "";
+	int retries = 0;
+
+	if (!num) {
+		ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
+		while (retries < 3 && cmd != 't') {
+			destination[1] = '\0';
+			destination[0] = cmd = play_and_wait(chan,"vm-enter-num-to-call");
+			if (!cmd)
+				destination[0] = cmd = play_and_wait(chan, "vm-then-pound");
+			if (!cmd)
+				destination[0] = cmd = play_and_wait(chan, "vm-star-cancel");
+			if (!cmd)
+				cmd = ast_waitfordigit(chan, 6000);
+			if (!cmd) {
+				retries++;
+			} else {
+
+				if (cmd < 0)
+					return 0;
+				if (cmd == '*') {
+					ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
+					return 0;
+				}
+				if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
+					retries++;
+				else
+					cmd = 't';
+			}
+		}
+		if (retries >= 3) {
+			return 0;
+		}
+	} else {
+		ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
+		strncpy(destination, num, sizeof(destination) -1);
+	}
+
+	if (strlen(destination)) {
+		ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
+		strncpy(chan->exten, destination, sizeof(chan->exten) - 1);
+		strncpy(chan->context, outgoing_context, sizeof(chan->context) - 1);
+		chan->priority = 0;
+		return 9;
+	}
+	return 0;
+}
+
+/* This function converts a name->numbers, it has been taken from app_directory:convert
+**  There is a good reason why I just cut-and-pasted it with little modifications...
+*/
+static char *ast_vm_convert(char *lastname, int total_digits)
+{
+	char *tmp;
+	int lcount = 0;
+	tmp = malloc(total_digits + 1);
+	if (tmp) {
+		while((*lastname > 32) && lcount < total_digits) {
+			switch(toupper(*lastname)) {
+			case '1':
+				tmp[lcount++] = '1';
+				break;
+			case '2':
+			case 'A':
+			case 'B':
+			case 'C':
+				tmp[lcount++] = '2';
+				break;
+			case '3':
+			case 'D':
+			case 'E':
+			case 'F':
+				tmp[lcount++] = '3';
+				break;
+			case '4':
+			case 'G':
+			case 'H':
+			case 'I':
+				tmp[lcount++] = '4';
+				break;
+			case '5':
+			case 'J':
+			case 'K':
+			case 'L':
+				tmp[lcount++] = '5';
+				break;
+			case '6':
+			case 'M':
+			case 'N':
+			case 'O':
+				tmp[lcount++] = '6';
+				break;
+			case '7':
+			case 'P':
+			case 'Q':
+			case 'R':
+			case 'S':
+				tmp[lcount++] = '7';
+				break;
+			case '8':
+			case 'T':
+			case 'U':
+			case 'V':
+				tmp[lcount++] = '8';
+				break;
+			case '9':
+			case 'W':
+			case 'X':
+			case 'Y':
+			case 'Z':
+				tmp[lcount++] = '9';
+				break;
+			}
+			lastname++;
+		}
+		tmp[lcount] = '\0';
+	}
+	return tmp;
+}
+
+/*
+** Jesse Janzer, 03-02-04
+** This function returns a pointer to a voicemail user upon success
+**  success is determined if the user has 'chosen' a valid lastname-extension
+** This function was based heavily off of app_directory
+** NOTE: you can pass NULL into context if you don't want to match-by-context
+** Returns NULL on failure, pointer to the ast_vm_user on success
+*/
+static struct ast_vm_user *ast_vm_directory_main(struct ast_channel *chan, char *dir, char *context, int use_first_name){
+	struct ast_vm_user *tmp_user;
+	int cmd, res, cnt, match, wait_for_key, found_at_least_one;
+	char digits[4];
+	char greeting[256];
+	char *last_name, *pos, *buff;
+	cmd = 0;
+	res = 0;
+	match = 0;
+	cnt = 0;
+	wait_for_key = 0;
+	found_at_least_one = 0;
+	tmp_user=NULL;
+	last_name=NULL;
+	pos=NULL;
+	buff=NULL;
+
+	//we need to tell the user how to use this system
+	while(cmd == 0){
+		//much like directory, we're going to sit in a loop until they hit something
+		cmd = play_and_wait(chan,"dir-intro");
+		//wait for three digits
+		if(cmd > 0){
+			digits[cnt++] = cmd;
+		} else {
+			//they didn't push a key, so let's sit tight for 5 seconds
+			cmd = ast_waitfordigit(chan, 5000);
+		}
+	}
+	while(cmd >= 0 && cnt < 3){ //let them bail on a hangup
+		cmd = ast_waitfordigit(chan, 5000);
+		if(cmd > 0){
+			digits[cnt++] = cmd;
+		} else{
+			//bail out
+			break;
+		}
+	}
+	digits[cnt] = '\0'; //terminate our string
+
+	//ok let's see if we can find this user
+
+	if(cnt > 2){ //only if we made at least 3 characters
+
+		//now we need to sit into a loop until they hangup, cancel, or select a user
+		tmp_user = users; //point us to the first user
+		while(res >= 0 && !match && tmp_user){
+			//do we grab the first word or last word?
+			if(use_first_name){
+				//first word
+				buff = ast_strdupa(tmp_user->fullname);
+
+				if(buff){
+					pos = strsep(&buff," ");
+				}
+
+			} else {
+				//last word
+				pos = strrchr(tmp_user->fullname, ' '); //jump to the last space
+				pos++;
+			}
+			if(pos){
+				last_name = ast_vm_convert(pos,3); //get a last name
+			}
+			if(last_name
+				&& !strncasecmp(last_name,digits,(sizeof(last_name) - 1))
+				&& (context ? !strncmp(context,tmp_user->context,sizeof(tmp_user->context) - 1) : 1)){
+
+				ast_verbose( VERBOSE_PREFIX_3 "match at last_name '%s' digits '%s'\n",last_name,digits);
+				//we found match (maybe), let's give them a prompt
+				found_at_least_one = 1;
+				snprintf(greeting, sizeof(greeting), "%s/voicemail/%s/%s/greet", (char *)ast_config_AST_SPOOL_DIR, context ? context : tmp_user->context, tmp_user->mailbox);
+				if (ast_fileexists(greeting, NULL, chan->language) > 0) {
+					res = play_and_wait(chan,greeting);
+				} else {
+					//they don't have a greeting, so let's play the digits
+					res = ast_say_digit_str(chan, tmp_user->mailbox, AST_DIGIT_ANY, chan->language);
+				}
+
+				//loop until we get a response
+				wait_for_key = 0;
+				while(!wait_for_key && res >= 0){
+				//did they hit yes (1)?
+					switch(res){
+						case '1': //this is the one they want!
+							match = 1;
+							wait_for_key = 1;
+							break;
+						case '*': //they want to see the next available person
+							wait_for_key = 1;
+							break;
+						default: //ask them if they want this user
+							res = play_and_wait(chan, "dir-instr");
+							if(!res) //timeout and repeat in 5 seconds
+								res = ast_waitfordigit(chan, 5000);
+							break;
+					}
+				}
+
+
+			}
+
+			if(last_name){ //purge last_name since convert mallocs
+				free(last_name);
+				last_name = NULL;
+			}
+			if(!match){ //don't lose our place if we found the right person
+				tmp_user = tmp_user->next;
+			}
+		}
+
+	}
+
+	if(!match){
+		//there wasn't a match made
+		if(found_at_least_one) //play no more since we did find some but they didn't want them
+			res = play_and_wait(chan, "dir-nomore");
+		else
+			res = play_and_wait(chan, "dir-nomatch");
+	}
+	return tmp_user; //this will be NULL on failures due to the loop and prep
+}
+
+
+
+/* This is the main top level prompt for forwarding messages to group */
+static int ast_vm_forwardgroup_main(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *curdir, int curmsg, char *fmt){
+	struct ast_vm_group_member *fwd_members_head, *fwd_member;
+	struct ast_vm_group *fwd_group, *pointer_group, *result_group;
+	struct ast_vm_user *fwd_user;
+	int cmd,more,res,fwd_at_least_one,cnt,is_forward;
+	char *ecodes;
+	char groupId[80];
+	char buff[256];
+	char tmp[80];
+	char groupgreeting[256];
+
+	fwd_members_head = NULL;
+	fwd_member = NULL;
+	fwd_group = NULL;
+	fwd_user = NULL;
+	result_group = NULL;
+	pointer_group = NULL;
+	ecodes = NULL;
+	cmd = 0;
+	more = 1;
+	res = 0;
+	cnt = 0;
+	is_forward = 0;
+	fwd_at_least_one = 0; //just a simple int to tell us if we have at least one member
+
+	//are we forwarding a message?
+	if(curmsg >= 0){
+		//yes we are
+		is_forward = 1;
+	}
+
+	//init our group here
+	if(!fwd_group){
+		fwd_group = malloc(sizeof(struct ast_vm_group));
+		if(!fwd_group){
+			//uh oh we failed to malloc, so let's bail
+			ast_log(LOG_WARNING, "Failed to malloc fwd_group\n");
+			return -3;
+		}
+		//erase this group
+		memset(fwd_group,0,sizeof(struct ast_vm_group));
+	}
+
+	if(!chan){
+		//this is bad our channel doesn't exist, puke time
+		ast_log(LOG_WARNING, "chan doesn't exist, returning with failure\n");
+		return -1;
+	}
+	if(!vmu){
+		//this is bad our user doesn't exist
+		ast_log(LOG_WARNING, "vmu user doesn't exist, returning with failure\n");
+		return -2;
+	}
+
+	//"Please record you message, when you are finished press the # key"
+	if(curmsg == -1){
+		//we need to create a voicemail message into our own mailbox so we can easily forward to others
+		if(fmt == NULL){
+			fmt = "wav";
+		}
+		//they say extesion for arg[1], but I think they really mean mailbox
+		res = play_and_wait(chan,"vm-fwd-recordmessage");
+		res = leave_voicemail(chan, vmu->mailbox, 1, 0, 0, &curmsg);
+	}
+
+
+
+	//here is the meat let's allow them to select which groups/members they want
+	while(more){
+		//do we have at least one member/group if so, give them the choice to send
+		if(fwd_at_least_one){
+			cmd = play_and_wait_multi(chan,"vm-fwd-tosendthismessage,vm-press,digits/1");
+		}
+		// NOTE: I could string this all together with multi, but splitting it up makes it easier to read
+		if(!cmd) //To Add another group
+			cmd = play_and_wait_multi(chan,"vm-fwd-toattachagroup,vm-press,digits/2");
+		if(!cmd) //To add another member
+			cmd = play_and_wait_multi(chan,"vm-fwd-toattachauser,vm-press,digits/3");
+		if(is_forward){
+			if(!cmd) //For more forwarding options
+				cmd = play_and_wait_multi(chan,"vm-fwd-fwdoptionspress,digits/4");
+		}
+		if(!cmd) //allow them to exit via * or hangup
+			cmd = play_and_wait_multi(chan,"vm-fwd-tocancel,vm-press,digits/star");
+		if(!cmd) //wait for 6 seconds
+			cmd = ast_waitfordigit(chan, 6000);
+
+		switch(cmd){
+			//what key did the user press?
+			case '1': //they want to forward the message to all members in their list
+				cmd = 0;
+				//do we actually have any members?
+				if(!fwd_group || !fwd_group->member){
+					//no we don't so let's tell them and allow them to continue
+					if(!cmd)
+						cmd = play_and_wait(chan,"vm-fwd-nomembers");
+					more = 1;
+					cmd = 0;
+				} else {
+					fwd_member = fwd_group->member;
+					while(fwd_member){
+						//send out a copy to each user
+						ast_verbose( VERBOSE_PREFIX_3 "forwarding message to %s@%s from %s\n", fwd_member->mailbox, fwd_member->context, vmu->mailbox);
+
+						fwd_user = NULL;
+
+						fwd_user = find_user(NULL,fwd_member->context, fwd_member->mailbox);
+						if(fwd_user){ //if this is a valid user, lets push
+							forward_message(chan, fwd_member->context, curdir, curmsg, vmu, vms, vmfmts, fwd_user);
+							//it looks scary to not free the user, but forward_message actually handles it for us
+							//free_user(fwd_user); // we don't need this anymore
+							fwd_user = NULL;
+						} else {
+							ast_log(LOG_WARNING, "Failed to find fwd_user\n");
+						}
+						fwd_member = fwd_member->next;
+					}
+					more = 0;
+
+					//now that it is sent, let's tell the user
+					if(!vms){
+						ast_log(LOG_WARNING, "vms wasn't defined, not giving any options to delete message\n");
+					} else if (curmsg < 0 || curmsg >= MAXMSG){
+						ast_log(LOG_WARNING, "message is out-of-bounds\n");
+					} else {
+						if(!cmd)
+							cmd = play_and_wait(chan,"vm-fwd-yourmsgsent");
+						if(!cmd)
+							cmd = play_and_wait_multi(chan,"vm-fwd-tosavemsg,vm-press,digits/1");
+						if(!cmd)
+							cmd = play_and_wait_multi(chan,"vm-fwd-todiscardmsg,vm-press,digits/2");
+						if(!cmd) //wait for 6 seconds
+							cmd = ast_waitfordigit(chan, 6000);
+						switch(cmd){
+							case '2':
+								//this is the only case we really care about, let's remove this entry
+								vms->deleted[curmsg] = !vms->deleted[curmsg];
+								vms->heard[curmsg] = !vms->heard[curmsg];
+								if(vms->lastmsg < curmsg){ //in order for us to actually delete, we need to move lastmsg forward
+									vms->lastmsg = curmsg;
+								}
+								if (vms->deleted[curmsg])
+									cmd = play_and_wait(chan, "vm-deleted");
+								else
+									cmd = play_and_wait(chan, "vm-undeleted");
+								break;
+							default:
+								cmd = play_and_wait(chan,"vm-msgsaved");
+								break;
+						}
+					}
+				}
+				break;
+			case '2': //they want to add more groups/members
+				//we need to loop through any groups that we don't already have
+				cmd = 0;
+				if(groups){ //our global list of groups, set via voicemail.conf
+					pointer_group = groups;
+					while(pointer_group){
+						ecodes = "#0123456789"; //if they hit any key, we want to pop out
+						//Did we already include this group?
+						if(1){ //TODO: implement
+							//ok we didn't so let's prompt for them
+							cmd = play_and_wait(chan,"vm-fwd-forgroup");
+							ast_verbose( VERBOSE_PREFIX_3 "group announce '%s'\n",pointer_group->announce);
+							if(strlen(pointer_group->announce)){
+								snprintf(groupgreeting, sizeof(groupgreeting), "sounds/%s", pointer_group->announce);
+								if(ast_fileexists(groupgreeting,NULL,NULL)){
+									if(!cmd) //if we have a name prompt play it
+										cmd = play_and_wait(chan,pointer_group->announce);
+								}
+							} else {
+								if(!cmd) //group id
+									cmd = ast_say_digit_str(chan, pointer_group->id, ecodes, chan->language);
+							}
+							if(!cmd) //press
+									cmd = play_and_wait(chan,"vm-press");
+							if(!cmd) //unique id
+								cmd = ast_say_digit_str(chan, pointer_group->id, ecodes, chan->language);
+							/*if(!cmd) //followed by the pound key
+								play_and_wait(chan,"vm-followedbythepoundkey");*/
+						}
+						if(cmd){
+							break;
+						}
+
+
+						pointer_group = pointer_group->next; //position for the next one
+					}
+
+					if(ast_readstring(chan, groupId, sizeof(groupId) - 1, 3000, 3000, "#") < 0){
+						//failure to read a string
+						ast_log(LOG_WARNING, "failed to ast_readstring\n");
+					} else {
+						//if we received a key on while prompting for groups we need to cat that key and the received keys here
+						if(cmd > 0){
+							//a character was pressed during the prompts
+							buff[0] = cmd;
+							buff[1] = '\0';
+							strncat(buff,groupId,sizeof(buff));
+						} else {
+							strncpy(buff,groupId,sizeof(buff));
+						}
+
+						ast_verbose( VERBOSE_PREFIX_3 "groupId: '%s'\n",buff);
+
+						//lets see if this is a valid group
+						result_group = ast_vm_find_group(buff);
+						if(!result_group){
+							//NEED_PROMPT: "I'm sorry that is not a valid group"
+							cmd = play_and_wait(chan,"vm-fwd-invalidgroup");
+							ast_log(LOG_WARNING, "user entered an invalid groupId: '%s'\n",groupId);
+							cmd = 0; //jump back to main forwarding thing
+							continue;
+						} else {
+							//ok that group was found, let's attach all of the members to our list
+							/*
+							** Make sure the list group exists
+							**  then loop through the resultant group
+							**   adding each member into the list group via append
+							*/
+
+
+
+							//loop through the result group
+							fwd_member = result_group->member;
+							while(fwd_member){
+								//we already get a saved copy, so don't forward this onto ourselves
+								if (!((!strcasecmp(vmu->context, fwd_member->context)) &&
+										(!strcasecmp(vmu->mailbox, fwd_member->mailbox)))){
+									ast_verbose( VERBOSE_PREFIX_3 "Attaching member: %s@%s to fwd_group\n",fwd_member->mailbox,fwd_member->context);
+									append_vm_group_member(fwd_group,fwd_member->mailbox,fwd_member->context);
+									fwd_at_least_one = 1;
+								} else {
+									ast_verbose( VERBOSE_PREFIX_3 "Not attaching member: %s@%s to fwd_group since sender is member\n",fwd_member->mailbox,fwd_member->context);
+								}
+								fwd_member = fwd_member->next;
+							}
+
+							//ok we're all done with this group
+
+						}
+
+
+					}
+
+				}
+				cmd = 0; //reset the command so that the user gets a new list
+				more = 1; //allow them to continue to insert
+				break;
+			case '3': //they want to add a user one at a time
+				/* give them 3 prompts
+				**  1) dial-by-name
+				**  2) dial-by-extension
+				**  *) cancel
+				*/
+				cmd = 0;
+				while(cmd == 0){ //while we're waiting, loop
+					cmd = play_and_wait_multi(chan,"vm-fwd-toaddbylastname,vm-press,digits/1");
+					if(!cmd)
+						cmd = play_and_wait_multi(chan,"vm-fwd-toaddbyextension,vm-press,digits/2");
+					if(!cmd)
+						cmd = play_and_wait_multi(chan,"vm-fwd-tocancelstar");
+					if(!cmd) //wait for 6 seconds
+						cmd = ast_waitfordigit(chan, 6000);
+				}
+				switch(cmd){
+					//what did they push?
+					case '1':
+						fwd_user = ast_vm_directory_main(chan,curdir,NULL,0); //last name
+						if(fwd_user){ //they chose a user, so let's add it to the list
+							append_vm_group_member(fwd_group,fwd_user->mailbox,fwd_user->context);
+							fwd_at_least_one = 1;
+						}
+						break;
+					case '2':
+						//they want to just submit the extension
+						cmd = 0;
+						cnt = 0;
+						fwd_user = NULL;
+						while(cmd >= 0 && cnt < 2){ //bomb out if we get a good find or tries exceeds 2
+							cnt++;
+							buff[0] = '\0';
+							cmd = play_and_wait_multi(chan,"vm-extension");
+							if(cmd > 0){ //did we already get a digit during extension?
+								buff[0] = cmd;
+								buff[1] = '\0';
+							}
+							cmd = ast_readstring(chan, tmp, sizeof(tmp) - 1, 2000, 10000, "#");
+							ast_log(LOG_WARNING, "I read '%s'\n",tmp);
+							if(cmd >= 0){
+								strncat(buff,tmp,sizeof(buff));
+							}
+							//can we find an extension now (only works from calling context)?
+							fwd_user = find_user(NULL,vmu->context,buff);
+							if(fwd_user){
+								append_vm_group_member(fwd_group,fwd_user->mailbox,fwd_user->context);
+								fwd_at_least_one = 1;
+								cnt = 2;
+							} else {
+								cmd = play_and_wait_multi(chan,"invalid");
+								cmd = 0;
+							}
+						}
+						cmd = 2; //jump back to add user
+						break;
+					case '*':
+					default:
+						break;
+				}
+				if(cmd >= 0)
+					cmd = 0; //they are finished with their current selection
+
+				break;
+			case '4': //they want extra forwarding options
+				if(is_forward){ //only give them prepending options if they are forwarding
+					cmd = vm_forwardoptions(chan, vmu, curdir, curmsg, fmt, vmu->context);
+				}
+				cmd = 0;
+				break;
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+			case '#':
+				cmd = play_and_wait(chan, "vm-sorry");
+				cmd = 0;
+				break;
+			case 0: //just loop right back, nothing happened
+				break;
+			case '*': //they want to cancel their request
+			case -1: //hungup
+				more = 0;
+				break;
+			default:
+				//they did something we didn't expect
+				more = 0; //let's hop back
+				break;
+		}
+	}
+
+	//do we have a group still alloc'ed?
+	if(fwd_group)
+		free_vm_group(fwd_group);
+
+	return 0; //success let's exit
+}
+
+//static int advanced_options(struct ast_channel *chan, char *fn, struct ast_vm_user *vmu, struct vm_state *vms, int option)
+//{
+
+static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option)
+{
+	int res = 0, msgnum = 0;
+	char filename[256],*origtime, *cid, *context, *name, *num;
+	struct ast_config *msg_cfg;
+	int retries = 0;
+
+	vms->starting = 0;
+	make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
+
+	/* Retrieve info from VM attribute file */
+
+        make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
+        snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
+        msg_cfg = ast_load(filename);
+        if (!msg_cfg) {
+                ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
+                return 0;
+        }
+
+        if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
+                return 0;
+                                                                                                                                 
+        cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
+
+        context = ast_variable_retrieve(msg_cfg, "message", "context");
+	if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
+		context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
+
+	if (option == 3) {
+
+		if (!res)
+			res = play_message_datetime(chan, vmu, origtime, filename);
+		if (!res)
+			res = play_message_callerid(chan, vms, cid, context);
+	} else if (option == 2) { /* Call back */
+
+		if (strlen(cid)) {
+			ast_callerid_parse(cid, &name, &num);
+			while ((res > -1) && (res != 't')) {
+				switch(res) {
+					case '1':
+						if (num) {
+							/* Dial the CID number */
+							res = dialout(chan, vmu, num, vmu->callback);
+							if (res)
+								return 9;
+						} else {
+							res = '2';
+						}
+						break;
+
+					case '2':
+						/* Want to enter a different number, can only do this if there's a dialout context for this user */
+						if (strlen(vmu->dialout)) {
+							res = dialout(chan, vmu, NULL, vmu->dialout);
+							if (res)
+								return 9;
+						} else {
+							ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
+							res = play_and_wait(chan, "vm-sorry");
+						}
+						return res;
+					case '*':
+						res = 't';
+						break;
+					case '3':
+					case '4':
+					case '5':
+					case '6':
+					case '7':
+					case '8':
+					case '9':
+					case '0':
+
+						res = play_and_wait(chan, "vm-sorry");
+						retries++;
+						break;
+					default:
+						if (num) {
+							ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
+							res = play_and_wait(chan, "vm-num-i-have");
+							if (!res)
+								// res = say_calling_party(chan, vmu->context, num);
+								res = play_message_callerid(chan, vms, num, vmu->context);
+							if (!res)
+								res = play_and_wait(chan, "vm-press");
+							if (!res)
+								res = play_and_wait(chan, "digits/1");
+							if (!res)
+								res = play_and_wait(chan, "vm-call-this-num");
+							/* Only prompt for a caller-specified number if there is a dialout context specified */
+							if (strlen(vmu->dialout)) {
+								if (!res)
+									res = play_and_wait(chan, "vm-Press");
+								if (!res)
+									res = play_and_wait(chan, "digits/2");
+								if (!res)
+									res = play_and_wait(chan, "vm-call-diff-num");
+							}
+						} else  {
+							res = play_and_wait(chan, "vm-nonumber");
+							if (strlen(vmu->dialout)) {
+								if (!res)
+									res = play_and_wait(chan, "vm-press");
+								if (!res)
+									res = play_and_wait(chan, "digits/1");
+								if (!res)
+									res = play_and_wait(chan, "vm-toenternumber");
+							}
+						}
+						if (!res)
+							res = play_and_wait(chan, "vm-star-cancel");
+						if (!res)
+							res = ast_waitfordigit(chan, 6000);
+						if (!res)
+							retries++;
+						if (retries > 3)
+							res = 't';
+						break;
+
+						}
+					if (res == 't')
+						res = 0;
+					else
+						res = -1;
+				}
+			}
+
+	}
+	else if (option == 1) { /* Reply */
+              		/* Send reply directly to sender */
+		if (strlen(cid)) {
+			ast_callerid_parse(cid, &name, &num);
+			if (!num) {
+				ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
+				if (!res)
+					res = play_and_wait(chan, "vm-nonumber");
+				return res;
+			} else {
+				if (find_user(NULL, vmu->context, num)) {
+					ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
+					leave_voicemail(chan, num, 1, 0, 1,&msgnum);
+					res = 't';
+					return res;
+				}
+              				else {
+					ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
+                      				/* Sender has no mailbox, can't reply */
+                      				play_and_wait(chan, "vm-nobox");
+                      				res = 't';
+					return res;
+				}
+			} 
+			res = 0;
+		}
+	} else if (option == 5) { /* Forward to group*/
+		ast_verbose( VERBOSE_PREFIX_3 "User pressed 5, let's allow them to create a msg\n");
+		res = ast_vm_forwardgroup_main(chan,vmu,vms,vms->curdir,msg,NULL); //we don't have a message/format
+	}
+
+	ast_destroy(msg_cfg);
+
+	if (!res) {
+		make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
+		vms->heard[msg] = 1;
+		res = wait_file(chan, vms, vms->fn);
+	}
+	return res;
+}
+
+static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller)
+{
+	/* Record message & let caller review or re-record it, or set options if applicable */
+ 	int res = 0;
+ 	int cmd = 0;
+ 	int max_attempts = 3;
+ 	int attempts = 0;
+ 	int recorded = 0;
+ 	time_t start;
+ 	time_t end;
+ 	int duration = 0;
+ 	int message_exists = 0;
+ 	/* Note that urgent and private are for flagging messages as such in the future */
+ 
+ 	cmd = '3';	 /* Want to start by recording */
+
+        	while((cmd >= 0) && (cmd != 't')) {
+        		switch (cmd) {
+ 	       	case '1':
+ 			if (!message_exists) {
+ 				/* In this case, 1 is to record a message */
+ 				cmd = '3';
+ 				break;
+ 			} else {
+ 				/* Otherwise 1 is to save the existing message */
+ 				ast_verbose(VERBOSE_PREFIX_3 "Saving message as is");
+ 				ast_streamfile(chan, "vm-msgsaved", chan->language);
+ 				ast_waitstream(chan, "");
+ 				cmd = 't';
+ 				return res;
+ 			}
+ 		case '2':
+ 			/* Review */
+ 			ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message");
+ 			ast_streamfile(chan, recordfile, chan->language);
+ 			cmd = ast_waitstream(chan, AST_DIGIT_ANY);
+ 			break;
+ 		case '3':
+ 			message_exists = 0;
+ 			/* Record */
+ 			ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
+ 			if (recorded && outsidecaller) {
+ 				cmd = play_and_wait(chan, INTRO);
+ 				cmd = play_and_wait(chan, "beep");
+ 			}
+ 			recorded = 1;
+ 			/* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
+ 			time(&start);
+ 			cmd = play_and_record(chan, playfile, recordfile, maxtime, fmt);
+ 			time(&end);
+ 			if (cmd == -1)
+ 			/* User has hung up, no options to give */
+ 				return res;
+ 			if (cmd == '0' || cmd == '7') {
+ 				/* Erase the message if 0 pushed during playback */
+ 				play_and_wait(chan, "vm-deleted");
+ 				ast_filedelete(recordfile, NULL);
+ 			} else if (cmd == '*') {
+ 				break;
+ 			}
+ 			else if (end - start < 3) {
+ 				/* Message is too short */
+ 				ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
+ 				cmd = ast_filedelete(recordfile, NULL);
+ 				cmd = play_and_wait(chan, "vm-tooshort");
+ 				break;
+ 			}
+ 			else if (cmd == 2 && duration < (maxsilence + 3)) {
+ 				/* Message is all silence */
+ 				ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded");
+ 				// cmd = ast_filedelete(recordfile, NULL);
+ 				cmd = play_and_wait(chan, "vm-nothingrecorded");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-speakup");
+ 				break;
+ 			}
+ 			else {
+ 				/* If all is well, a message exists */
+ 				message_exists = 1;
+ 			}
+ 			break;
+ 		case '4':
+ 		case '5':
+ 		case '6':
+ 		case '7':
+ 		case '8':
+ 		case '9':
+ 			cmd = play_and_wait(chan, "vm-sorry");
+ 			break;
+ // XXX Commented out for the moment because of the dangers of deleting
+ // a message while recording (can put the message numbers out of sync)
+ //		case '*':
+ 			/* Cancel recording, delete message, offer to take another message*/
+ //			cmd = play_and_wait(chan, "vm-deleted");
+ //			cmd = ast_filedelete(recordfile, NULL);
+ //			if (outsidecaller) {
+ //				res = vm_exec(chan, NULL);
+ //				return res;
+ //			}
+ //			else
+ //				return 1;
+ 		case '0':
+ 			if (outsidecaller) {
+ 				if (message_exists)
+ 					play_and_wait(chan, "vm-msgsaved");
+ 				return cmd;
+ 			} else
+ 				cmd = play_and_wait(chan, "vm-sorry");
+ 			break;
+ 		default:
+ 			if (message_exists) {
+ 					cmd = play_and_wait(chan, "vm-Press");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "digits/1");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-accept");
+ 				if (!cmd)
+ 					cmd = ast_waitfordigit(chan, 600);
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-press");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "digits/2");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-listen");
+ 				if (!cmd)
+ 					cmd = ast_waitfordigit(chan, 600);
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-Press");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "digits/3");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-rerecord");
+ 				if (!cmd)
+ 					cmd = ast_waitfordigit(chan, 1000);
+ 			}
+ 			else {
+ 				cmd = play_and_wait(chan, "vm-press");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "digits/1");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-rerecord");
+ 				if (!cmd)
+ 					cmd = ast_waitfordigit(chan, 600);
+ 			}
+ 			
+ 			if (!cmd && outsidecaller) {
+ 				cmd = play_and_wait(chan, "vm-press");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "digits/0");
+ 				if (!cmd)
+ 					cmd = play_and_wait(chan, "vm-reachoperator");
+ 				if (!cmd)
+ 					cmd = ast_waitfordigit(chan, 600);
+ 			}
+ 			/*if (!cmd)
+ 				cmd = play_and_wait(chan, "vm-press");
+ 			if (!cmd)
+ 				cmd = play_and_wait(chan, "digits/star");
+ 			if (!cmd)
+ 				cmd = play_and_wait(chan, "vm-cancelmessage");
+ 				*/
+ 			if (!cmd)
+ 				cmd = ast_waitfordigit(chan, 6000);
+ 			if (!cmd) {
+ 				attempts++;
+ 			}
+ 			if (attempts > max_attempts) {
+ 				cmd = 't';
+ 			}
+ 		}
+ 	}
+ 	if (outsidecaller)  
+ 		play_and_wait(chan, "vm-goodbye");
+ 	if (cmd == 't')
+ 		cmd = 0;
+ 	return cmd;
+ }
+
+
+
 int usecount(void)
 {
 	int res;
@@ -3456,3 +5008,9 @@
 {
 	return ASTERISK_GPL_KEY;
 }
+
+
+
+	
+
+
