diff --git a/apps/app_directory.c b/apps/app_directory.c
index 551f61faef..bea790293f 100755
--- a/apps/app_directory.c
+++ b/apps/app_directory.c
@@ -30,11 +30,12 @@ static char *app = "Directory";
 
 static char *synopsis = "Provide directory of voicemail extensions";
 static char *descrip =
-"  Directory(context): Presents the user with a directory of extensions from which\n"
-"  they may select by name.  The list of names and extensions is discovered from\n"
-"  voicemail.conf.  The context argument is required, and specifies the context\n"
-"  in which to interpret the extensions\n.  Returns 0 unless the user hangs up.  It\n"
-"  also sets up the channel on exit to enter the extension the user selected.\n";
+"  Directory(context): Presents the user with a directory of extensions from\n"
+"which they  may  select  by name. The  list  of  names  and  extensions  is\n"
+"discovered from  voicemail.conf. The  context  argument  is  required,  and\n"
+"specifies  the  context  in  which to interpret the extensions\n. Returns 0\n"
+"unless the user hangs up. It  also sets up the channel on exit to enter the\n"
+"extension the user selected.\n";
 
 /* For simplicity, I'm keeping the format compatible with the voicemail config,
    but i'm open to suggestions for isolating it */
@@ -129,7 +130,8 @@ static int do_directory(struct ast_channel *chan, struct ast_config *cfg, char *
 	char fn[256];
 	memset(ext, 0, sizeof(ext));
 	ext[0] = digit;
-	res = ast_readstring(chan, ext + 1, NUMDIGITS, 3000, 3000, "#");
+	res = 0;
+	if (ast_readstring(chan, ext + 1, NUMDIGITS, 3000, 3000, "#") < 0) res = -1;
 	if (!res) {
 		/* Search for all names which start with those digits */
 		v = ast_variable_browse(cfg, context);
diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c
index 356e1015d3..5321466406 100755
--- a/apps/app_voicemail.c
+++ b/apps/app_voicemail.c
@@ -37,10 +37,6 @@
 #define VOICEMAIL_CONFIG "voicemail.conf"
 #define ASTERISK_USERNAME "asterisk"
 
-/*
-#define HOSTNAME_OVERRIDE "linux-support.net"
-*/
-
 #define SENDMAIL "/usr/sbin/sendmail -t"
 
 #define INTRO "vm-intro"
@@ -58,10 +54,10 @@ static char *synopsis_vm =
 "Leave a voicemail message";
 
 static char *descrip_vm =
-"  VoiceMail([s]extension): Leaves voicemail for a given extension (must be configured in\n"
-"  voicemail.conf).  If the extension is preceeded by an 's' then instructions for leaving\n"
-"  the message will be skipped.  Returns -1 on error or mailbox not found, or if the user\n"
-"  hangs up.  Otherwise, it returns 0. \n";
+"  VoiceMail([s]extension): Leaves voicemail for a given  extension (must be\n"
+"configured in voicemail.conf). If the extension is preceeded by an 's' then\n"
+"instructions for leaving the message will be skipped. Returns  -1 on  error\n"
+"or mailbox not found, or if the user hangs up. Otherwise, it returns 0. \n";
 
 static char *synopsis_vmain =
 "Enter voicemail system";
@@ -80,19 +76,18 @@ STANDARD_LOCAL_USER;
 
 LOCAL_USER_DECL;
 
-static char *get_dir(char *ext, char *mailbox)
+static int make_dir(char *dest, int len, char *ext, char *mailbox)
 {
-	char *tmp = malloc(strlen(ext) + strlen(VM_SPOOL_DIR) + 3 + strlen(mailbox));
-	sprintf(tmp, "%s/%s/%s", VM_SPOOL_DIR, ext, mailbox);
-	return tmp;
+	return snprintf(dest, len, "%s/%s/%s", VM_SPOOL_DIR, ext, mailbox);
 }
-static char *get_fn(char *dir, int num)
+
+static int make_file(char *dest, int len, char *dir, int num)
 {
-	char *tmp = malloc(strlen(dir) + 10);
-	sprintf(tmp, "%s/msg%04d", dir, num);
-	return tmp;
+	return snprintf(dest, len, "%s/msg%04d", dir, num);
 }
 
+#if 0
+
 static int announce_message(struct ast_channel *chan, char *dir, int msgcnt)
 {
 	char *fn;
@@ -115,34 +110,37 @@ static int announce_message(struct ast_channel *chan, char *dir, int msgcnt)
 		ast_log(LOG_WARNING, "Unable to announce message\n");
 	return res;
 }
-static int sendmail(char *email, char *name, int msgnum, char *mailbox)
+#endif
+
+static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid)
 {
 	FILE *p;
 	char date[256];
 	char host[256];
+	char who[256];
 	time_t t;
 	struct tm *tm;
 	p = popen(SENDMAIL, "w");
 	if (p) {
-		gethostname(host, sizeof(host));
+		if (strchr(srcemail, '@'))
+			strncpy(who, srcemail, sizeof(who));
+		else {
+			gethostname(host, sizeof(host));
+			snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+		}
 		time(&t);
 		tm = localtime(&t);
 		strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", tm);
 		fprintf(p, "Date: %s\n", date);
 		fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
-		fprintf(p, "From: Asterisk PBX <%s@%s>\n", ASTERISK_USERNAME,
-#ifdef HOSTNAME_OVERRIDE
-				HOSTNAME_OVERRIDE
-#else
-				host
-#endif
-				);
+		fprintf(p, "From: Asterisk PBX <%s>\n", who);
 		fprintf(p, "To: %s <%s>\n", name, email);
 		fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n\n", msgnum, mailbox);
 		strftime(date, sizeof(date), "%A, %B %d, %Y at %r", tm);
 		fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a message (number %d)\n"
-		           "in mailbox %s, on %s so you might\n"
-				   "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n", name, msgnum, mailbox, date);
+		           "in mailbox %s from %s, on %s so you might\n"
+				   "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n", name, 
+			msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
 		fprintf(p, ".\n");
 		pclose(p);
 	} else {
@@ -164,7 +162,7 @@ static int get_date(char *s, int len)
 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 {
 	struct ast_config *cfg;
-	char *copy, *name, *passwd, *email, *dir, *fmt, *fmts, *fn=NULL;
+	char *copy, *name, *passwd, *email, *fmt, *fmts;
 	char comment[256];
 	struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
 	char *sfmt[MAX_OTHER_FORMATS];
@@ -175,23 +173,28 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 	int outmsg=0;
 	struct ast_frame *f;
 	char date[256];
+	char dir[256];
+	char fn[256];
+	char *astemail;
 	
 	cfg = ast_load(VOICEMAIL_CONFIG);
 	if (!cfg) {
 		ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG);
 		return -1;
 	}
+	if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
+		astemail = ASTERISK_USERNAME;
 	if ((copy = ast_variable_retrieve(cfg, NULL, ext))) {
 		/* Make sure they have an entry in the config */
 		copy = strdup(copy);
 		passwd = strtok(copy, ",");
 		name = strtok(NULL, ",");
 		email = strtok(NULL, ",");
-		dir = get_dir(ext, "");
+		make_dir(dir, sizeof(dir), ext, "");
 		/* It's easier just to try to make it than to check for its existence */
 		if (mkdir(dir, 0700) && (errno != EEXIST))
 			ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
-		dir = get_dir(ext, "INBOX");
+		make_dir(dir, sizeof(dir), ext, "INBOX");
 		if (mkdir(dir, 0700) && (errno != EEXIST))
 			ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
 		/* Stream an info message */
@@ -204,9 +207,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 					fmt = strtok(fmts, "|");
 					msgnum = 0;
 					do {
-						if (fn)
-							free(fn);
-						fn = get_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"), 
 											name, ext, chan->name);
@@ -328,7 +329,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 							}
 							/* Send e-mail if applicable */
 							if (email) 
-								sendmail(email, name, msgnum, ext);
+								sendmail(astemail, email, name, msgnum, ext, chan->callerid);
 						}
 					} else {
 						if (msgnum < MAXMSG)
@@ -336,8 +337,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 						else
 							ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext);
 					}
-					if (fn)
-						free(fn);
 					free(fmts);
 				} else 
 					ast_log(LOG_WARNING, "No format to save messages in \n");
@@ -345,7 +344,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 		} else
 			ast_log(LOG_WARNING, "Unable to playback instructions\n");
 			
-		free(dir);
 		free(copy);
 	} else
 		ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
@@ -354,25 +352,276 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
 	return res;
 }
 
+static char *mbox(int id)
+{
+	switch(id) {
+	case 0:
+		return "INBOX";
+	case 1:
+		return "Old";
+	case 2:
+		return "Work";
+	case 3:
+		return "Family";
+	case 4:
+		return "Friends";
+	case 5:
+		return "Cust1";
+	case 6:
+		return "Cust2";
+	case 7:
+		return "Cust3";
+	case 8:
+		return "Cust4";
+	case 9:
+		return "Cust5";
+	default:
+		return "Unknown";
+	}
+}
+
+static int count_messages(char *dir)
+{
+	int x;
+	char fn[256];
+	for (x=0;x<MAXMSG;x++) {
+		make_file(fn, sizeof(fn), dir, x);
+		if (ast_fileexists(fn, NULL, NULL) < 1)
+			break;
+	}
+	return x;
+}
+
+static int play_and_wait(struct ast_channel *chan, char *fn)
+{
+	int d;
+	d = ast_streamfile(chan, fn, chan->language);
+	if (d)
+		return d;
+	d = ast_waitstream(chan, AST_DIGIT_ANY);
+	return d;
+}
+
+static int say_and_wait(struct ast_channel *chan, int num)
+{
+	int d;
+	d = ast_say_number(chan, num, chan->language);
+	return d;
+}
+
+static int copy(char *infile, char *outfile)
+{
+	int ifd;
+	int ofd;
+	int res;
+	int len;
+	char buf[4096];
+	if ((ifd = open(infile, O_RDONLY)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
+		return -1;
+	}
+	if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
+		close(ifd);
+		return -1;
+	}
+	do {
+		len = read(ifd, buf, sizeof(buf));
+		if (len < 0) {
+			ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+			close(ifd);
+			close(ofd);
+			unlink(outfile);
+		}
+		if (len) {
+			res = write(ofd, buf, len);
+			if (res != len) {
+				ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+				close(ifd);
+				close(ofd);
+				unlink(outfile);
+			}
+		}
+	} while(len);
+	close(ifd);
+	close(ofd);
+	return 0;
+}
+
+static int save_to_folder(char *dir, int msg, char *username, int box)
+{
+	char sfn[256];
+	char dfn[256];
+	char ddir[256];
+	char txt[256];
+	char ntxt[256];
+	char *dbox = mbox(box);
+	int x;
+	make_file(sfn, sizeof(sfn), dir, msg);
+	make_dir(ddir, sizeof(ddir), username, dbox);
+	mkdir(ddir, 0700);
+	for (x=0;x<MAXMSG;x++) {
+		make_file(dfn, sizeof(dfn), ddir, x);
+		if (ast_fileexists(dfn, NULL, NULL) < 0)
+			break;
+	}
+	if (x >= MAXMSG)
+		return -1;
+	ast_filecopy(sfn, dfn, NULL);
+	if (strcmp(sfn, dfn)) {
+		snprintf(txt, sizeof(txt), "%s.txt", sfn);
+		snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
+		copy(txt, ntxt);
+	}
+	return 0;
+}
+
+static int get_folder(struct ast_channel *chan, int start)
+{
+	int x;
+	int d;
+	char fn[256];
+	d = play_and_wait(chan, "vm-press");
+	if (d)
+		return d;
+	for (x = start; x< 5; x++) {
+		if ((d = ast_say_number(chan, x, chan->language)))
+			return d;
+		d = play_and_wait(chan, "vm-for");
+		if (d)
+			return d;
+		snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
+		d = play_and_wait(chan, fn);
+		if (d)
+			return d;
+		d = play_and_wait(chan, "vm-messages");
+		if (d)
+			return d;
+		d = ast_waitfordigit(chan, 500);
+		if (d)
+			return d;
+	}
+	d = play_and_wait(chan, "vm-tocancel");
+	if (d)
+		return d;
+	d = ast_waitfordigit(chan, 4000);
+	return d;
+}
+
+#define WAITCMD(a) do { \
+	d = (a); \
+	if (d < 0) \
+		goto out; \
+	if (d) \
+		goto cmd; \
+} while(0)
+
+#define WAITFILE2(file) do { \
+	if (ast_streamfile(chan, file, chan->language)) \
+		ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
+	d = ast_waitstream(chan, AST_DIGIT_ANY); \
+	if (d < 0) { \
+		goto out; \
+	}\
+} while(0)
+
+#define WAITFILE(file) do { \
+	if (ast_streamfile(chan, file, chan->language)) \
+		ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
+	d = ast_waitstream(chan, AST_DIGIT_ANY); \
+	if (!d) { \
+		repeats = 0; \
+		goto instructions; \
+	} else if (d < 0) { \
+		goto out; \
+	} else goto cmd;\
+} while(0)
+
+#define PLAYMSG(a) do { \
+	starting = 0; \
+	if (!a) \
+		WAITFILE2("vm-first"); \
+	else if (a == lastmsg) \
+		WAITFILE2("vm-last"); \
+	WAITFILE2("vm-message"); \
+	if (a && (a != lastmsg)) { \
+		d = ast_say_number(chan, a + 1, chan->language); \
+		if (d < 0) goto out; \
+		if (d) goto cmd; \
+	} \
+	make_file(fn, sizeof(fn), curdir, a); \
+	heard[a] = 1; \
+	WAITFILE(fn); \
+} while(0)
+
+#define CLOSE_MAILBOX do { \
+	if (lastmsg > -1) { \
+		/* Get the deleted messages fixed */ \
+		curmsg = -1; \
+		for (x=0;x<=lastmsg;x++) { \
+			if (!deleted[x] && (strcasecmp(curbox, "INBOX") || !heard[x])) { \
+				/* Save this message.  It's not in INBOX or hasn't been heard */ \
+				curmsg++; \
+				make_file(fn, sizeof(fn), curdir, x); \
+				make_file(fn2, sizeof(fn2), curdir, curmsg); \
+				if (strcmp(fn, fn2)) { \
+					snprintf(txt, sizeof(txt), "%s.txt", fn); \
+					snprintf(ntxt, sizeof(ntxt), "%s.txt", fn2); \
+					ast_filerename(fn, fn2, NULL); \
+					rename(txt, ntxt); \
+				} \
+			} else if (!strcasecmp(curbox, "INBOX") && heard[x] && !deleted[x]) { \
+				/* Move to old folder before deleting */ \
+				save_to_folder(curdir, x, username, 1); \
+			} \
+		} \
+		for (x = curmsg + 1; x<=lastmsg; x++) { \
+			make_file(fn, sizeof(fn), curdir, x); \
+			snprintf(txt, sizeof(txt), "%s.txt", fn); \
+			ast_filedelete(fn, NULL); \
+			unlink(txt); \
+		} \
+	} \
+	memset(deleted, 0, sizeof(deleted)); \
+	memset(heard, 0, sizeof(heard)); \
+} while(0)
+
+#define OPEN_MAILBOX(a) do { \
+	strcpy(curbox, mbox(a)); \
+	make_dir(curdir, sizeof(curdir), username, curbox); \
+	lastmsg = count_messages(curdir) - 1; \
+	snprintf(vmbox, sizeof(vmbox), "vm-%s", curbox); \
+} while (0)
+
 static int vm_execmain(struct ast_channel *chan, void *data)
 {
-	/* XXX This is, admittedly, some pretty horrendus code XXX */
+	/* XXX This is, admittedly, some pretty horrendus code.  For some
+	   reason it just seemed a lot easier to do with GOTO's.  I feel
+	   like I'm back in my GWBASIC days. XXX */
 	int res=-1;
 	int valid = 0;
-	int curmsg = 0;
-	int maxmsg = 0;
-	int x;
-	char *fn, *nfn;
 	char d;
 	struct localuser *u;
 	char username[80];
 	char password[80], *copy;
-	int deleted[MAXMSG];
+	char curbox[80];
+	char curdir[256];
+	char vmbox[256];
+	char fn[256];
+	char fn2[256];
+	int x;
 	char ntxt[256];
 	char txt[256];
+	int deleted[MAXMSG] = { 0, };
+	int heard[MAXMSG] = { 0, };
+	int newmessages;
+	int oldmessages;
+	int repeats = 0;
+	int curmsg = 0;
+	int lastmsg = 0;
+	int starting = 1;
+	int box;
 	struct ast_config *cfg;
-	int state;
-	char *dir=NULL;
 	
 	LOCAL_USER_ADD(u);
 	cfg = ast_load(VOICEMAIL_CONFIG);
@@ -386,9 +635,12 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 		ast_log(LOG_WARNING, "Couldn't stream login file\n");
 		goto out;
 	}
+	
+	/* Authenticate them and get their mailbox/password */
+	
 	do {
 		/* Prompt for, and read in the username */
-		if (ast_readstring(chan, username, sizeof(username), 2000, 10000, "#")) {
+		if (ast_readstring(chan, username, sizeof(username), 2000, 10000, "#") < 0) {
 			ast_log(LOG_WARNING, "Couldn't read username\n");
 			goto out;
 		}			
@@ -402,7 +654,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 			ast_log(LOG_WARNING, "Unable to stream password file\n");
 			goto out;
 		}
-		if (ast_readstring(chan, password, sizeof(password), 2000, 10000, "#")) {
+		if (ast_readstring(chan, password, sizeof(password), 2000, 10000, "#") < 0) {
 			ast_log(LOG_WARNING, "Unable to read password\n");
 			goto out;
 		}
@@ -424,208 +676,175 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 				break;
 		}
 	} while (!valid);
-	if (valid) {
-		dir = get_dir(username, "INBOX");
-		if (!dir) 
-			goto out;
 
-		deleted[0] = 0;
-		/* Find out how many messages are there, mark all as
-		   not deleted. */
-		do {
-			fn = get_fn(dir, maxmsg);
-			if ((res = ast_fileexists(fn, NULL, chan->language))>0) {
-				maxmsg++;
-				deleted[maxmsg] = 0;
+	if (valid) {
+		OPEN_MAILBOX(1);
+		oldmessages = lastmsg + 1;
+		/* Start in INBOX */
+		OPEN_MAILBOX(0);
+		newmessages = lastmsg + 1;
+		
+		WAITCMD(play_and_wait(chan, "vm-youhave"));
+		if (newmessages) {
+			WAITCMD(say_and_wait(chan, newmessages));
+			WAITCMD(play_and_wait(chan, "vm-INBOX"));
+			if (newmessages == 1)
+				WAITCMD(play_and_wait(chan, "vm-message"));
+			else
+				WAITCMD(play_and_wait(chan, "vm-messages"));
+				
+			if (oldmessages)
+				WAITCMD(play_and_wait(chan, "vm-and"));
+		}
+		if (oldmessages) {
+			WAITCMD(say_and_wait(chan, oldmessages));
+			WAITCMD(play_and_wait(chan, "vm-Old"));
+			if (oldmessages == 1)
+				WAITCMD(play_and_wait(chan, "vm-message"));
+			else
+				WAITCMD(play_and_wait(chan, "vm-messages"));
+		}
+		if (!oldmessages && !newmessages) {
+			WAITCMD(play_and_wait(chan, "vm-no"));
+			WAITCMD(play_and_wait(chan, "vm-messages"));
+		}
+		if (!newmessages && oldmessages) {
+			/* If we only have old messages start here */
+			OPEN_MAILBOX(1);
+		}
+		repeats = 0;
+		starting = 1;
+instructions:
+		if (starting) {
+			if (lastmsg > -1) {
+				WAITCMD(play_and_wait(chan, "vm-onefor"));
+				WAITCMD(play_and_wait(chan, vmbox));
+				WAITCMD(play_and_wait(chan, "vm-messages"));
 			}
-			free(fn);
-		} while(res > 0);
-		if (ast_streamfile(chan, "vm-youhave", chan->language))
-			goto out;
-		if ((d=ast_waitstream(chan, AST_DIGIT_ANY)) < 0)
+			WAITCMD(play_and_wait(chan, "vm-opts"));
+		} else {
+			if (curmsg)
+				WAITCMD(play_and_wait(chan, "vm-prev"));
+			WAITCMD(play_and_wait(chan, "vm-repeat"));
+			if (curmsg != lastmsg)
+				WAITCMD(play_and_wait(chan, "vm-next"));
+			if (!deleted[curmsg])
+				WAITCMD(play_and_wait(chan, "vm-delete"));
+			else
+				WAITCMD(play_and_wait(chan, "vm-undelete"));
+			WAITCMD(play_and_wait(chan, "vm-toforward"));
+			WAITCMD(play_and_wait(chan, "vm-savemessage"));
+		}
+		WAITCMD(play_and_wait(chan, "vm-helpexit"));
+		d = ast_waitfordigit(chan, 6000);
+		if (d < 0)
 			goto out;
-		ast_stopstream(chan);
 		if (!d) {
-			/* If they haven't interrupted us, play the message count */
-			if (maxmsg > 0) {
-				if ((d = ast_say_number(chan, maxmsg, chan->language)) < 0)
+			repeats++;
+			if (repeats > 2) {
+				play_and_wait(chan, "vm-goodbye");
+				goto out;
+			}
+			goto instructions;
+		}
+cmd:
+		switch(d) {
+		case '2':
+			box = play_and_wait(chan, "vm-changeto");
+			if (box < 0)
+				goto out;
+			while((box < '0') || (box > '9')) {
+				box = get_folder(chan, 0);
+				if (box < 0)
 					goto out;
+				if (box == '#')
+					goto instructions;
+			} 
+			box = box - '0';
+			CLOSE_MAILBOX;
+			OPEN_MAILBOX(box);
+			WAITCMD(play_and_wait(chan, vmbox));
+			WAITCMD(play_and_wait(chan, "vm-messages"));
+			starting = 1;
+			goto instructions;
+		case '4':
+			if (curmsg) {
+				curmsg--;
+				PLAYMSG(curmsg);
 			} else {
-				if (ast_streamfile(chan, "vm-no", chan->language))
-					goto out;
-				if ((d=ast_waitstream(chan, AST_DIGIT_ANY)) < 0)
-					goto out;
-				ast_stopstream(chan);
+				WAITCMD(play_and_wait(chan, "vm-nomore"));
+				goto instructions;
 			}
-			if (!d) {
-				/* And if they still haven't, give them the last word */
-				if (ast_streamfile(chan, ((maxmsg == 1) ? "vm-message" : "vm-messages"), chan->language))
-					goto out;
-				if (ast_waitstream(chan, AST_DIGIT_ANY) < 0)
-					goto out;
-				ast_stopstream(chan);
+		case '1':
+				curmsg = 0;
+				/* Fall through */
+		case '5':
+			if (lastmsg > -1) {
+				PLAYMSG(curmsg);
+			} else {
+				WAITCMD(play_and_wait(chan, "vm-youhave"));
+				WAITCMD(play_and_wait(chan, "vm-no"));
+				snprintf(fn, sizeof(fn), "vm-%s", curbox);
+				WAITCMD(play_and_wait(chan, fn));
+				WAITCMD(play_and_wait(chan, "vm-messages"));
+				goto instructions;
 			}
-		}
-		res = -1;
-				
-#define STATE_STARTING 1
-#define STATE_MESSAGE 2
-#define STATE_MESSAGE_PLAYING 3
-		state = STATE_STARTING;
-		ast_log(LOG_EVENT, "User '%s' logged in on channel '%s' with %d message(s).\n", username, chan->name, maxmsg);
-		if (option_verbose > 2)
-			ast_verbose( VERBOSE_PREFIX_3 "User '%s' logged in on channel %s with %d messages\n", username, chan->name, maxmsg);
-		if (!ast_streamfile(chan, "vm-instructions", chan->language)) {
-			for(;;) {
-				if (chan->stream) {
-					d = ast_waitstream(chan, AST_DIGIT_ANY);
-					ast_stopstream(chan);
-					if (!d && (state == STATE_MESSAGE_PLAYING)) {
-						state  = STATE_MESSAGE;
-						/* If it runs out playing a message, then give directions */
-						if (!(d = ast_streamfile(chan, "vm-msginstruct", chan->language)))
-							d = ast_waitstream(chan, AST_DIGIT_ANY);
-						ast_stopstream(chan);
-					}
-					if (!d)
-						d = ast_waitfordigit(chan, COMMAND_TIMEOUT);
-				} else
-					d = ast_waitfordigit(chan, COMMAND_TIMEOUT);
-				if (d < 0)
+		case '6':
+			if (curmsg < lastmsg) {
+				curmsg++;
+				PLAYMSG(curmsg);
+			} else {
+				WAITCMD(play_and_wait(chan, "vm-nomore"));
+				goto instructions;
+			}
+		case '7':
+			deleted[curmsg] = !deleted[curmsg];
+			if (deleted[curmsg]) 
+				WAITCMD(play_and_wait(chan, "vm-deleted"));
+			else
+				WAITCMD(play_and_wait(chan, "vm-undeleted"));
+			goto instructions;
+		case '9':
+			box = play_and_wait(chan, "vm-savefolder");
+			if (box < 0)
+				goto out;
+			while((box < '1') || (box > '9')) {
+				box = get_folder(chan, 1);
+				if (box < 0)
 					goto out;
-restart:
-				if (!d || (d == '*')) {
-					/* If they don't say anything, play back a message.  We'll decide which one is
-					   best based up on where they are.  Ditto if they press the '*' key. */
-					switch(state) {
-					case STATE_STARTING:
-						if (ast_streamfile(chan, "vm-instructions", chan->language))
-							goto out;
-						break;
-					case STATE_MESSAGE:
-					case STATE_MESSAGE_PLAYING:
-						if (ast_streamfile(chan, "vm-msginstruct", chan->language))
-							goto out;
-						break;
-					default:
-						ast_log(LOG_WARNING, "What do I do when they timeout/* in state %d?\n", state);
-					}
-				} else {
-					/* XXX Should we be command-compatible with Meridian mail?  Their system seems
-					       very confusing, but also widely used XXX */
-					/* They've entered (or started to enter) a command */
-					switch(d) {
-					case '0':
-						if (curmsg < maxmsg) {
-							deleted[curmsg] = !deleted[curmsg];
-							if (deleted[curmsg]) {
-								if (ast_streamfile(chan, "vm-deleted", chan->language))
-									goto out;
-							} else {
-								if (ast_streamfile(chan, "vm-undeleted", chan->language))
-									goto out;
-							}
-						} else {
-							if (ast_streamfile(chan, "vm-nomore", chan->language))
-								goto out;
-						}
-						break;
-					case '1':
-						curmsg = 0;
-						if (maxmsg > 0) {
-							/* Yuck */
-							if ((d = announce_message(chan, dir, curmsg)) > 0)
-								goto restart;
-							else if (d < 0)
-								goto out;
-						} else {
-							if (ast_streamfile(chan, "vm-nomore", chan->language))
-								goto out;
-						}
-						state = STATE_MESSAGE_PLAYING;
-						break;
-					case '4':
-						if (curmsg > 0)
-							curmsg--;
-						/* Yuck */
-						if ((d = announce_message(chan, dir, curmsg)) > 0)
-							goto restart;
-						else if (d < 0)
-							goto out;
-						state = STATE_MESSAGE_PLAYING;
-						break;
-					case '5':
-						if ((d = announce_message(chan, dir, curmsg)) > 0)
-							goto restart;
-						else if (d < 0)
-							goto out;
-						state = STATE_MESSAGE_PLAYING;
-						break;
-					case '6':
-						if (curmsg < maxmsg - 1) {
-							curmsg++;
-							if ((d = announce_message(chan, dir, curmsg)) > 0)
-								goto restart;
-							else if (d < 0)
-								goto out;
-						} else {
-							if (ast_streamfile(chan, "vm-nomore", chan->language))
-								goto out;
-						}
-						state = STATE_MESSAGE_PLAYING;
-						break;
-					/* XXX Message compose? It's easy!  Just read their # and, assuming it's in the config, 
-					       call the routine as if it were called from the PBX proper XXX */
-					case '#':
-						if (ast_streamfile(chan, "vm-goodbye", chan->language))
-							goto out;
-						if (ast_waitstream(chan, ""))
-							goto out;
-						res = 0;
-						goto out;
-						break;
-					default:
-						/* Double yuck */
-						d = '*';
-						goto restart;
-					}
-				}
+				if (box == '#')
+					goto instructions;
+			} 
+			box = box - '0';
+			ast_log(LOG_DEBUG, "Save to folder: %s (%d)\n", mbox(box), box);
+			if (save_to_folder(curdir, curmsg, username, box))
+				goto out;
+			deleted[curmsg]=1;
+			WAITCMD(play_and_wait(chan, "vm-message"));
+			WAITCMD(say_and_wait(chan, curmsg + 1) );
+			WAITCMD(play_and_wait(chan, "vm-savedto"));
+			snprintf(fn, sizeof(fn), "vm-%s", mbox(box));
+			WAITCMD(play_and_wait(chan, fn));
+			WAITCMD(play_and_wait(chan, "vm-messages"));
+			goto instructions;
+		case '*':
+			if (!starting) {
+				WAITCMD(play_and_wait(chan, "vm-onefor"));
+				WAITCMD(play_and_wait(chan, vmbox));
+				WAITCMD(play_and_wait(chan, "vm-messages"));
+				WAITCMD(play_and_wait(chan, "vm-opts"));
 			}
+			goto instructions;
+		case '#':
+			play_and_wait(chan, "vm-goodbye");
+			goto out;
+		default:
+			goto instructions;
 		}
 	}
-	
 out:
+	CLOSE_MAILBOX;
 	ast_stopstream(chan);
-	if (maxmsg) {
-		/* Get the deleted messages fixed */
-		curmsg = -1;
-		for (x=0;x<maxmsg;x++) {
-			if (!deleted[x]) {
-				curmsg++;
-				fn = get_fn(dir, x);
-				nfn = get_fn(dir, curmsg);
-				if (strcmp(fn, nfn)) {
-					snprintf(txt, sizeof(txt), "%s.txt", fn);
-					snprintf(ntxt, sizeof(ntxt), "%s.txt", nfn);
-					ast_filerename(fn, nfn, NULL);
-					rename(txt, ntxt);
-				}
-				free(fn);
-				free(nfn);
-			}
-		}
-		for (x = curmsg + 1; x<maxmsg; x++) {
-			fn = get_fn(dir, x);
-			if (fn) {
-				snprintf(txt, sizeof(txt), "%s.txt", fn);
-				ast_filedelete(fn, NULL);
-				unlink(txt);
-				free(fn);
-			}
-		}
-	}
-	if (dir)
-		free(dir);
 	if (cfg)
 		ast_destroy(cfg);
 	LOCAL_USER_REMOVE(u);
@@ -647,6 +866,8 @@ static int vm_exec(struct ast_channel *chan, void *data)
 		silent++;
 		ext++;
 	}
+	if (chan->state != AST_STATE_UP)
+		ast_answer(chan);
 	res = leave_voicemail(chan, ext, silent);
 	LOCAL_USER_REMOVE(u);
 	return res;
diff --git a/include/asterisk/app.h b/include/asterisk/app.h
new file mode 100755
index 0000000000..ecb1ab1018
--- /dev/null
+++ b/include/asterisk/app.h
@@ -0,0 +1,27 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Application convenience functions, designed to give consistent
+ * look and feel to asterisk apps.
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Mark Spencer <markster@linux-support.net>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#ifndef _ASTERISK_APP_H
+#define _ASTERISK_APP_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+extern int ast_app_getdata(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif