Merge Vonage's MySQL Voicemail stuff
[asterisk/asterisk.git] / apps / app_voicemail2.c
index 0b83484..50cbd02 100755 (executable)
@@ -33,6 +33,9 @@
 #include <sys/time.h>
 #include <sys/stat.h>
 #include <time.h>
 #include <sys/time.h>
 #include <sys/stat.h>
 #include <time.h>
+#ifdef USEMYSQLVM
+#include <mysql/mysql.h>
+#endif
 
 #include <pthread.h>
 #include "../asterisk.h"
 
 #include <pthread.h>
 #include "../asterisk.h"
 #define BASELINELEN 72
 #define eol "\r\n"
 
 #define BASELINELEN 72
 #define eol "\r\n"
 
-static int iocp;
-static int iolen;
-static int linelength;
-static int ateof;
-static unsigned char iobuf[BASEMAXINLINE];
+struct baseio {
+       int iocp;
+       int iolen;
+       int linelength;
+       int ateof;
+       unsigned char iobuf[BASEMAXINLINE];
+};
+
+struct ast_vm_user {
+       char context[80];
+       char mailbox[80];
+       char password[80];
+       char fullname[80];
+       char email[80];
+       char pager[80];
+       int alloced;
+       struct ast_vm_user *next;
+};
 
 static char *tdesc = "Comedian Mail (Voicemail System)";
 
 
 static char *tdesc = "Comedian Mail (Voicemail System)";
 
@@ -85,7 +101,7 @@ static char *synopsis_vm =
 "Leave a voicemail message";
 
 static char *descrip_vm =
 "Leave a voicemail message";
 
 static char *descrip_vm =
-"  VoiceMail([s|u|b]extension): Leaves voicemail for a given  extension (must\n"
+"  VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given  extension (must\n"
 "be configured in voicemail.conf). If the extension is preceeded by an 's'"
 "then instructions for leaving the message will be skipped.  If the extension\n"
 "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
 "be configured in voicemail.conf). If the extension is preceeded by an 's'"
 "then instructions for leaving the message will be skipped.  If the extension\n"
 "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
@@ -99,10 +115,11 @@ static char *synopsis_vmain =
 "Enter voicemail system";
 
 static char *descrip_vmain =
 "Enter voicemail system";
 
 static char *descrip_vmain =
-"  VoiceMailMain(): Enters the main voicemail system for the checking of\n"
+"  VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system for the checking of\n"
 "voicemail.  The mailbox can be passed as the option, which will stop the\n"
 "voicemail system from prompting the user for the mailbox.  If the mailbox\n"
 "voicemail.  The mailbox can be passed as the option, which will stop the\n"
 "voicemail system from prompting the user for the mailbox.  If the mailbox\n"
-"is preceded by 's' then the password check will be skipped.  Returns -1 if\n"
+"is preceded by 's' then the password check will be skipped.  If a context is\n"
+"specified, logins are considered in that context only. Returns -1 if\n"
 "the user hangs up or 0 otherwise.\n";
 
 /* Leave a message */
 "the user hangs up or 0 otherwise.\n";
 
 /* Leave a message */
@@ -111,25 +128,201 @@ static char *app = "VoiceMail2";
 /* Check mail, control, etc */
 static char *app2 = "VoiceMailMain2";
 
 /* Check mail, control, etc */
 static char *app2 = "VoiceMailMain2";
 
+static pthread_mutex_t vmlock = AST_MUTEX_INITIALIZER;
+struct ast_vm_user *users;
+struct ast_vm_user *usersl;
+static int attach_voicemail;
+static int maxsilence;
+static int silencethreshold;
+static char serveremail[80];
+static char vmfmts[80];
+static int vmmaxmessage;
+static int maxgreet;
+static int skipms;
+static int maxlogins;
+
 STANDARD_LOCAL_USER;
 
 LOCAL_USER_DECL;
 
 STANDARD_LOCAL_USER;
 
 LOCAL_USER_DECL;
 
-static int make_dir(char *dest, int len, char *ext, char *mailbox)
+#ifdef USEMYSQLVM
+MYSQL *dbhandler=NULL;
+pthread_mutex_t mysqllock;
+char dbuser[80];
+char dbpass[80];
+char dbname[80];
+
+static int mysql_login(void)
 {
 {
-       return snprintf(dest, len, "%s/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,"vm", ext, mailbox);
+       ast_verbose( VERBOSE_PREFIX_3 "Logging into database with user %s, password %s, and database %s\n", dbuser, dbpass, dbname);
+
+       dbhandler=mysql_init(NULL);
+       if (!mysql_real_connect(dbhandler, NULL, dbuser, dbpass, dbname, 0, NULL, 0)) {
+               ast_log(LOG_WARNING, "Error Logging into database\n");
+               return(-1);
+       }
+       pthread_mutex_init(&mysqllock, NULL);
+       return(0);
 }
 
 }
 
-static int make_file(char *dest, int len, char *dir, int num)
+static void mysql_logout(void)
 {
 {
-       return snprintf(dest, len, "%s/msg%04d", dir, num);
+       mysql_close(dbhandler);
+}
+
+static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
+{
+       MYSQL_RES *result;
+       MYSQL_ROW rowval;
+       MYSQL_FIELD *fields;
+       int numFields, i;
+       char query[240];
+       struct ast_vm_user *retval;
+
+       retval=malloc(sizeof(struct ast_vm_user));
+
+       *retval->mailbox='\0';
+       *retval->context='\0';
+       *retval->password='\0';
+       *retval->fullname='\0';
+       *retval->email='\0';
+       *retval->pager='\0';
+       retval->alloced=1;
+       retval->next=NULL;
+       if (mailbox) {
+               strcpy(retval->mailbox, mailbox);
+       }
+       if (context) {
+               strcpy(retval->context, context);
+       }
+
+       if (*retval->context) {
+               sprintf(query, "SELECT password,fullname,email,pager FROM users WHERE context='%s' AND mailbox='%s'", context, mailbox);
+       } else {
+               sprintf(query, "SELECT password,fullname,email,pager FROM users WHERE mailbox='%s'", mailbox);
+       }
+       pthread_mutex_lock(&mysqllock);
+       mysql_query(dbhandler, query);
+       if ((result=mysql_store_result(dbhandler))!=NULL) {
+               if ((rowval=mysql_fetch_row(result))!=NULL) {
+                       numFields=mysql_num_fields(result);
+                       fields=mysql_fetch_fields(result);
+                       for (i=0; i<numFields; i++) {
+                               if (rowval[i]) {
+                                       if (!strcmp(fields[i].name, "password")) {
+                                               strcpy(retval->password, rowval[i]);
+                                       } else if (!strcmp(fields[i].name, "fullname")) {
+                                               strcpy(retval->fullname, rowval[i]);
+                                       } else if (!strcmp(fields[i].name, "email")) {
+                                               strcpy(retval->email, rowval[i]);
+                                       } else if (!strcmp(fields[i].name, "pager")) {
+                                               strcpy(retval->pager, rowval[i]);
+                                       }
+                               }
+                       }
+                       mysql_free_result(result);
+                       pthread_mutex_unlock(&mysqllock);
+                       return(retval);
+               } else {
+                       mysql_free_result(result);
+                       pthread_mutex_unlock(&mysqllock);
+                       free(retval);
+                       return(NULL);
+               }
+       }
+       pthread_mutex_unlock(&mysqllock);
+       free(retval);
+       return(NULL);
+}
+
+static void vm_change_password(struct ast_vm_user *vmu, char *password)
+{
+       char query[400];
+
+       if (*vmu->context) {
+               sprintf(query, "UPDATE users SET password='%s' WHERE context='%s' AND mailbox='%s' AND password='%s'", password, vmu->context, vmu->mailbox, vmu->password);
+       } else {
+               sprintf(query, "UPDATE users SET password='%s' WHERE mailbox='%s' AND password='%s'", password, vmu->mailbox, vmu->password);
+       }
+       pthread_mutex_lock(&mysqllock);
+       mysql_query(dbhandler, query);
+       strcpy(vmu->password, password);
+       pthread_mutex_unlock(&mysqllock);
+}
+
+static void reset_user_pw(char *context, char *mailbox, char *password)
+{
+       char query[320];
+
+       if (context) {
+               sprintf(query, "UPDATE users SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
+       } else {
+               sprintf(query, "UPDATE users SET password='%s' WHERE mailbox='%s'", password, mailbox);
+       }
+       pthread_mutex_lock(&mysqllock);
+       mysql_query(dbhandler, query);
+       pthread_mutex_unlock(&mysqllock);
+}
+#else
+
+static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
+{
+       /* This function could be made to generate one from a database, too */
+       struct ast_vm_user *vmu=NULL, *cur;
+       ast_pthread_mutex_lock(&vmlock);
+       cur = users;
+       while(cur) {
+               if ((!context || !strcasecmp(context, cur->context)) &&
+                       (!strcasecmp(mailbox, cur->mailbox)))
+                               break;
+               cur=cur->next;
+       }
+       if (cur) {
+               if (ivm)
+                       vmu = ivm;
+               else
+                       /* Make a copy, so that on a reload, we have no race */
+                       vmu = malloc(sizeof(struct ast_vm_user));
+               if (vmu) {
+                       memcpy(vmu, cur, sizeof(struct ast_vm_user));
+                       if (ivm)
+                               vmu->alloced = 0;
+                       else
+                               vmu->alloced = 1;
+                       vmu->next = NULL;
+               }
+       }
+       ast_pthread_mutex_unlock(&vmlock);
+       return vmu;
+}
+
+static int reset_user_pw(char *context, char *mailbox, char *newpass)
+{
+       /* This function could be made to generate one from a database, too */
+       struct ast_vm_user *cur;
+       int res = -1;
+       ast_pthread_mutex_lock(&vmlock);
+       cur = users;
+       while(cur) {
+               if ((!context || !strcasecmp(context, cur->context)) &&
+                       (!strcasecmp(mailbox, cur->mailbox)))
+                               break;
+               cur=cur->next;
+       }
+       if (cur) {
+               strncpy(cur->password, newpass, sizeof(cur->password) - 1);
+               res = 0;
+       }
+       ast_pthread_mutex_unlock(&vmlock);
+       return res;
 }
 
 }
 
-static int vm_change_password(char *username, char *password, char *newpassword)
+static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
 {
         /*  There's probably a better way of doing this. */
         /*  That's why I've put the password change in a separate function. */
 {
         /*  There's probably a better way of doing this. */
         /*  That's why I've put the password change in a separate function. */
-
+               /*  This could also be done with a database function */
+       
         FILE *configin;
         FILE *configout;
                char inbuf[256];
         FILE *configin;
         FILE *configout;
                char inbuf[256];
@@ -180,12 +373,12 @@ static int vm_change_password(char *username, char *password, char *newpassword)
                                        }
                                } else
                                        rest = NULL;
                                        }
                                } else
                                        rest = NULL;
-                               if (user && pass && *user && *pass && !strcmp(user, username) && !strcmp(pass, password)) {
+                               if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
                                        /* This is the line */
                                        if (rest) {
                                        /* This is the line */
                                        if (rest) {
-                                               fprintf(configout, "%s => %s,%s\n", username,newpassword,rest);
+                                               fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
                                        } else {
                                        } else {
-                                               fprintf(configout, "%s => %s\n", username,newpassword);
+                                               fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
                                        }
                                } else {
                                        /* Put it back like it was */
                                        }
                                } else {
                                        /* Put it back like it was */
@@ -198,55 +391,67 @@ static int vm_change_password(char *username, char *password, char *newpassword)
 
         unlink((char *)tmpin);
         rename((char *)tmpout,(char *)tmpin);
 
         unlink((char *)tmpin);
         rename((char *)tmpout,(char *)tmpin);
-       return(1);
+       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
+       strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
+}
+#endif
+
+static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
+{
+       return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
+}
+
+static int make_file(char *dest, int len, char *dir, int num)
+{
+       return snprintf(dest, len, "%s/msg%04d", dir, num);
 }
 
 static int
 }
 
 static int
-inbuf(FILE *fi)
+inbuf(struct baseio *bio, FILE *fi)
 {
        int l;
 
 {
        int l;
 
-       if(ateof)
+       if(bio->ateof)
                return 0;
 
                return 0;
 
-       if ( (l = fread(iobuf,1,BASEMAXINLINE,fi)) <= 0) {
+       if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
                if(ferror(fi))
                        return -1;
 
                if(ferror(fi))
                        return -1;
 
-               ateof = 1;
+               bio->ateof = 1;
                return 0;
        }
 
                return 0;
        }
 
-       iolen= l;
-       iocp= 0;
+       bio->iolen= l;
+       bio->iocp= 0;
 
        return 1;
 }
 
 static int 
 
        return 1;
 }
 
 static int 
-inchar(FILE *fi)
+inchar(struct baseio *bio, FILE *fi)
 {
 {
-       if(iocp>=iolen)
-               if(!inbuf(fi))
+       if(bio->iocp>=bio->iolen)
+               if(!inbuf(bio, fi))
                        return EOF;
 
                        return EOF;
 
-       return iobuf[iocp++];
+       return bio->iobuf[bio->iocp++];
 }
 
 static int
 }
 
 static int
-ochar(int c, FILE *so)
+ochar(struct baseio *bio, int c, FILE *so)
 {
 {
-       if(linelength>=BASELINELEN) {
+       if(bio->linelength>=BASELINELEN) {
                if(fputs(eol,so)==EOF)
                        return -1;
 
                if(fputs(eol,so)==EOF)
                        return -1;
 
-               linelength= 0;
+               bio->linelength= 0;
        }
 
        if(putc(((unsigned char)c),so)==EOF)
                return -1;
 
        }
 
        if(putc(((unsigned char)c),so)==EOF)
                return -1;
 
-       linelength++;
+       bio->linelength++;
 
        return 1;
 }
 
        return 1;
 }
@@ -256,11 +461,10 @@ static int base_encode(char *filename, FILE *so)
        unsigned char dtable[BASEMAXINLINE];
        int i,hiteof= 0;
        FILE *fi;
        unsigned char dtable[BASEMAXINLINE];
        int i,hiteof= 0;
        FILE *fi;
+       struct baseio bio;
 
 
-       linelength = 0;
-       iocp = BASEMAXINLINE;
-       iolen = 0;
-       ateof = 0;
+       memset(&bio, 0, sizeof(bio));
+       bio.iocp = BASEMAXINLINE;
 
        if ( !(fi = fopen(filename, "rb"))) {
                ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
 
        if ( !(fi = fopen(filename, "rb"))) {
                ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
@@ -290,7 +494,7 @@ static int base_encode(char *filename, FILE *so)
                igroup[0]= igroup[1]= igroup[2]= 0;
 
                for(n= 0;n<3;n++){
                igroup[0]= igroup[1]= igroup[2]= 0;
 
                for(n= 0;n<3;n++){
-                       if ( (c = inchar(fi)) == EOF) {
+                       if ( (c = inchar(&bio, fi)) == EOF) {
                                hiteof= 1;
                                break;
                        }
                                hiteof= 1;
                                break;
                        }
@@ -312,7 +516,7 @@ static int base_encode(char *filename, FILE *so)
                        }
 
                        for(i= 0;i<4;i++)
                        }
 
                        for(i= 0;i<4;i++)
-                               ochar(ogroup[i], so);
+                               ochar(&bio, ogroup[i], so);
                }
        }
 
                }
        }
 
@@ -335,12 +539,9 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m
        char dur[256];
        time_t t;
        struct tm tm;
        char dur[256];
        time_t t;
        struct tm tm;
-       char *astattach;
-       struct ast_config *cfg;
+       if (!strcmp(format, "wav49"))
+               format = "WAV";
        p = popen(SENDMAIL, "w");
        p = popen(SENDMAIL, "w");
-       cfg = ast_load(VOICEMAIL_CONFIG);
-       if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
-               astattach = "yes";
        if (p) {
                gethostname(host, sizeof(host));
                if (strchr(srcemail, '@'))
        if (p) {
                gethostname(host, sizeof(host));
                if (strchr(srcemail, '@'))
@@ -358,7 +559,7 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m
                fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox);
                fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
                fprintf(p, "MIME-Version: 1.0\n");
                fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox);
                fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
                fprintf(p, "MIME-Version: 1.0\n");
-               if (ast_true(astattach)) {
+               if (attach_voicemail) {
                        // Something unique.
                        snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
 
                        // Something unique.
                        snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
 
@@ -366,14 +567,14 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m
 
                        fprintf(p, "--%s\n", bound);
                }
 
                        fprintf(p, "--%s\n", bound);
                }
-                       fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
-                       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 %s long message (number %d)\n"
-
-                          "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\n", name, 
-                               dur, msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
-               if (ast_true(astattach)) {
+               fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
+               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 %s long message (number %d)\n"
+
+                       "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\n", name, 
+                       dur, msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
+               if (attach_voicemail) {
                        fprintf(p, "--%s\n", bound);
                        fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum, format);
                        fprintf(p, "Content-Transfer-Encoding: BASE64\n");
                        fprintf(p, "--%s\n", bound);
                        fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum, format);
                        fprintf(p, "Content-Transfer-Encoding: BASE64\n");
@@ -401,9 +602,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char
        char dur[256];
        time_t t;
        struct tm tm;
        char dur[256];
        time_t t;
        struct tm tm;
-       struct ast_config *cfg;
        p = popen(SENDMAIL, "w");
        p = popen(SENDMAIL, "w");
-       cfg = ast_load(VOICEMAIL_CONFIG);
 
        if (p) {
                gethostname(host, sizeof(host));
 
        if (p) {
                gethostname(host, sizeof(host));
@@ -440,11 +639,11 @@ static int get_date(char *s, int len)
        return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
 }
 
        return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
 }
 
-static int invent_message(struct ast_channel *chan, char *ext, int busy, char *ecodes)
+static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
 {
        int res;
        char fn[256];
 {
        int res;
        char fn[256];
-       snprintf(fn, sizeof(fn), "vm/%s/greet", ext);
+       snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
        if (ast_fileexists(fn, NULL, NULL) > 0) {
                res = ast_streamfile(chan, fn, chan->language);
                if (res)
        if (ast_fileexists(fn, NULL, NULL) > 0) {
                res = ast_streamfile(chan, fn, chan->language);
                if (res)
@@ -473,81 +672,248 @@ static int invent_message(struct ast_channel *chan, char *ext, int busy, char *e
        return res;
 }
 
        return res;
 }
 
-static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
+static int play_and_wait(struct ast_channel *chan, char *fn)
 {
 {
-       struct ast_config *cfg;
-       char *copy, *name, *passwd, *email, *pager, *fmt, *fmts;
+       int d;
+       d = ast_streamfile(chan, fn, chan->language);
+       if (d)
+               return d;
+       d = ast_waitstream(chan, AST_DIGIT_ANY);
+       return d;
+}
+
+static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
+{
+       char d, *fmts;
        char comment[256];
        char comment[256];
-       struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
+       int x, fmtcnt=1, res=-1,outmsg=0;
+       struct ast_frame *f;
+       struct ast_filestream *others[MAX_OTHER_FORMATS];
        char *sfmt[MAX_OTHER_FORMATS];
        char *sfmt[MAX_OTHER_FORMATS];
+       char *stringp=NULL;
+       time_t start, end;
+       struct ast_dsp *sildet;         /* silence detector dsp */
+       int totalsilence = 0;
+       int dspsilence = 0;
+       int gotsilence = 0;             /* did we timeout for silence? */
+       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) { 
+               d = play_and_wait(chan, playfile);
+               if (!d)
+                       d = ast_streamfile(chan, "beep",chan->language);
+               if (!d)
+                       d = ast_waitstream(chan,"");
+               if (d < 0)
+                       return -1;
+       }
+       
+       fmts = strdupa(fmt);
+       
+       stringp=fmts;
+       strsep(&stringp, "|");
+       ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
+       sfmt[0] = 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");
+                       break;
+               }
+               sfmt[fmtcnt++] = strdupa(fmt);
+       }
+
+       if (maxtime)
+               time(&start);
+       for (x=0;x<fmtcnt;x++) {
+               others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
+               ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
+                       
+               if (!others[x]) {
+                       break;
+               }
+       }
+       
+       sildet = ast_dsp_new(); //Create the silence detector
+       if (!sildet) {
+               ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
+               return -1;
+       }
+       ast_dsp_set_threshold(sildet, 50);
+       
+       if (maxsilence > 0) {
+               rfmt = chan->readformat;
+               res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+               if (res < 0) {
+                       ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
+                       return -1;
+               }
+       }
+                                               
+       if (x == fmtcnt) {
+       /* Loop forever, writing the packets we read to the writer(s), until
+          we read a # or get a hangup */
+               f = NULL;
+               for(;;) {
+                       res = ast_waitfor(chan, 2000);
+                       if (!res) {
+                               ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
+                               /* Try one more time in case of masq */
+                               res = ast_waitfor(chan, 2000);
+                               if (!res) {
+                                       ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
+                                       res = -1;
+                               }
+                       }
+                       
+                       if (res < 0) {
+                               f = NULL;
+                               break;
+                       }
+                       f = ast_read(chan);
+                       if (!f)
+                               break;
+                       if (f->frametype == AST_FRAME_VOICE) {
+                               /* write each format */
+                               for (x=0;x<fmtcnt;x++) {
+                                       res = ast_writestream(others[x], f);
+                               }
+                               
+                               /* Silence Detection */
+                               if (maxsilence > 0) {
+                                       dspsilence = 0;
+                                       ast_dsp_silence(sildet, f, &dspsilence);
+                                       if (dspsilence)
+                                               totalsilence = dspsilence;
+                                       else
+                                               totalsilence = 0;
+                                       
+                                       if (totalsilence > maxsilence) {
+                                       /* Ended happily with silence */
+                                       ast_frfree(f);
+                                       gotsilence = 1;
+                                       outmsg=2;
+                                       break;
+                                       }
+                               }
+                               /* Exit on any error */
+                               if (res) {
+                                       ast_log(LOG_WARNING, "Error writing frame\n");
+                                       ast_frfree(f);
+                                       break;
+                               }
+                       } else if (f->frametype == AST_FRAME_DTMF) {
+                               if (f->subclass == '#') {
+                                       if (option_verbose > 2) 
+                                               ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
+                                       res = '#';
+                                       outmsg = 2;
+                                       ast_frfree(f);
+                                       break;
+                               }
+                       }
+                       if (maxtime) {
+                               time(&end);
+                               if (maxtime < (end - start)) {
+                                       if (option_verbose > 2)
+                                               ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
+                                       res = 't';
+                                       ast_frfree(f);
+                                       break;
+                               }
+                       }
+                       ast_frfree(f);
+               }
+               if (!f) {
+                       if (option_verbose > 2) 
+                               ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
+                       res = -1;
+                       outmsg=1;
+               }
+       } else {
+               ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); 
+       }
+
+       for (x=0;x<fmtcnt;x++) {
+               if (!others[x])
+                       break;
+               if (totalsilence)
+                       ast_stream_rewind(others[x], totalsilence-200);
+               else
+                       ast_stream_rewind(others[x], 1000);
+               ast_truncstream(others[x]);
+               ast_closestream(others[x]);
+       }
+       if (rfmt) {
+               if (ast_set_read_format(chan, rfmt)) {
+                       ast_log(LOG_WARNING, "Unable to restore format %d to channel '%s'\n", rfmt, chan->name);
+               }
+       }
+       if (outmsg) {
+               if (outmsg > 1) {
+               /* Let them know it worked */
+                       ast_streamfile(chan, "vm-msgsaved", chan->language);
+                       ast_waitstream(chan, "");
+               }
+       }       
+
+       
+       return res;
+}
+
+static void free_user(struct ast_vm_user *vmu)
+{
+       if (vmu->alloced)
+               free(vmu);
+}
+
+static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
+{
+       char comment[256];
        char txtfile[256];
        FILE *txt;
        char txtfile[256];
        FILE *txt;
-       int res = -1, fmtcnt=0, x;
+       int res = 0;
        int msgnum;
        int msgnum;
-       int outmsg=0;
-       int wavother=0;
-       int maxmessage=0;
-       struct ast_frame *f;
        char date[256];
        char dir[256];
        char fn[256];
        char prefile[256]="";
        char date[256];
        char dir[256];
        char fn[256];
        char prefile[256]="";
-       char *astemail;
+       char fmt[80];
+       char *context;
        char *ecodes = "#";
        char *ecodes = "#";
-       char *s;
+       char *stringp;
        time_t start;
        time_t end;
        time_t start;
        time_t end;
-       struct ast_dsp *sildet;         /* silence detector dsp */
-       int totalsilence = 0;
-       int dspsilence = 0;
-       int silence = 0;                /* amount of silence to allow */
-       int gotsilence = 0;             /* did we timeout for silence? */
-       char *silencestr;
-       char *thresholdstr;
-       int rfmt;
-       int threshold = 128;
-
-       cfg = ast_load(VOICEMAIL_CONFIG);
-       if (!cfg) {
-               ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG);
-               return -1;
-       }
-       if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
-               silence = atoi(silencestr);
-               if (silence > 0)
-                       silence *= 1000;
-       }
-       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
-               threshold = atoi(thresholdstr);
-               
-       if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
-               astemail = ASTERISK_USERNAME;
-       if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
-               if (sscanf(s, "%d", &x) == 1) {
-                       maxmessage = x;
-               } else {
-                       ast_log(LOG_WARNING, "Invalid max message time length\n");
-               }
+       char tmp[256] = "";
+       struct ast_vm_user *vmu;
+       struct ast_vm_user svm;
+       
+       strncpy(tmp, ext, sizeof(tmp) - 1);
+       ext = tmp;
+       context = strchr(tmp, '@');
+       if (context) {
+               *context = '\0';
+               context++;
        }
        }
-       if ((copy = ast_variable_retrieve(cfg, NULL, ext))) {
-               char *stringp=NULL;
+
+       if ((vmu = find_user(&svm, context, ext))) {
                /* Setup pre-file if appropriate */
                if (busy)
                /* Setup pre-file if appropriate */
                if (busy)
-                       snprintf(prefile, sizeof(prefile), "vm/%s/busy", ext);
+                       snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
                else if (unavail)
                else if (unavail)
-                       snprintf(prefile, sizeof(prefile), "vm/%s/unavail", ext);
-               /* Make sure they have an entry in the config */
-               copy = strdup(copy);
-               stringp=copy;
-               passwd = strsep(&stringp, ",");
-               name = strsep(&stringp, ",");
-               email = strsep(&stringp, ",");
-               pager = strsep(&stringp, ",");
-               make_dir(dir, sizeof(dir), ext, "");
+                       snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
+               make_dir(dir, sizeof(dir), vmu->context, "", "");
+               /* 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));
+               make_dir(dir, sizeof(dir), vmu->context, 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));
                /* 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));
-               make_dir(dir, sizeof(dir), ext, "INBOX");
+               make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
                if (mkdir(dir, 0700) && (errno != EEXIST))
                        ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
                if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
                if (mkdir(dir, 0700) && (errno != EEXIST))
                        ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
                if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
@@ -556,75 +922,71 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
                if (strlen(prefile)) {
                        if (ast_fileexists(prefile, NULL, NULL) > 0) {
                                if (ast_streamfile(chan, prefile, chan->language) > -1) 
                if (strlen(prefile)) {
                        if (ast_fileexists(prefile, NULL, NULL) > 0) {
                                if (ast_streamfile(chan, prefile, chan->language) > -1) 
-                                   silent = ast_waitstream(chan, "#0");
+                                   res = ast_waitstream(chan, "#0");
                        } else {
                                ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
                        } else {
                                ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
-                               silent = invent_message(chan, ext, busy, ecodes);
+                               res = invent_message(chan, vmu->context, ext, busy, ecodes);
                        }
                        }
-                       if (silent < 0) {
+                       if (res < 0) {
                                ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
                                ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
-                               free(copy);
+                               free_user(vmu);
                                return -1;
                        }
                }
                                return -1;
                        }
                }
-               /* If they hit "#" we should still play the beep sound */
-               if (silent == '#') {
-                       if (!ast_streamfile(chan, "beep", chan->language) < 0)
+               if (res == '#') {
+                       /* On a '#' we skip the instructions */
+                       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;
                                silent = 1;
-                       if (ast_waitstream(chan, "") <0) {
-                               ast_log(LOG_DEBUG, "Hangup during beep\n");
-                               free(copy);
-                               return -1;
+                               res = 0;
                        }
                        }
-               } else if (silent == '0') {
+               }
+               /* Check for a '0' here */
+               if (res == '0') {
                        strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
                        if (strlen(chan->macrocontext))
                                strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
                        chan->priority = 0;
                        strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
                        if (strlen(chan->macrocontext))
                                strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
                        chan->priority = 0;
-                       free(copy);
+                       free_user(vmu);
                        return 0;
                }
                        return 0;
                }
-               /* Stream an info message */
-               if (silent || !ast_streamfile(chan, INTRO, chan->language)) {
-                       /* Wait for the message to finish */
-                       if (silent || !ast_waitstream(chan, "")) {
-                               if (!ast_streamfile(chan, "beep", chan->language) < 0)
-                                       silent = 1;
-                               if (ast_waitstream(chan, "") <0) {
-                                       ast_log(LOG_DEBUG, "Hangup during beep\n");
-                                       free(copy);
-                                       return -1;
-                               }
-                               fmt = ast_variable_retrieve(cfg, "general", "format");
-                               if (fmt) {
-                                       char *stringp=NULL;
-                                       fmts = strdup(fmt);
-                                       stringp=fmts;
-                                       fmt = strsep(&stringp, "|");
-                                       msgnum = 0;
-                                       do {
-                                               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);
-                                               if (ast_fileexists(fn, NULL, chan->language) > 0) {
-                                                       msgnum++;
-                                                       continue;
-                                               }
-                                               writer = ast_writefile(fn, fmt, comment, O_EXCL, 1 /* check for other formats */, 0700);
-                                               if (!writer)
-                                                       break;
-                                               msgnum++;
-                                       } while(!writer && (msgnum < MAXMSG));
-                                       if (writer) {
-                                               char *stringp=NULL;
-                                               /* Store information */
-                                               snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
-                                               txt = fopen(txtfile, "w+");
-                                               if (txt) {
-                                                       get_date(date, sizeof(date));
-                                                       time(&start);
-                                                       fprintf(txt, 
+               if (res >= 0) {
+                       /* Unless we're *really* silent, try to send the beep */
+                       res = ast_streamfile(chan, "beep", chan->language);
+                       if (!res)
+                               res = ast_waitstream(chan, "");
+               }
+               if (res < 0) {
+                       free_user(vmu);
+                       return -1;
+               }
+               /* 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;
+                       do {
+                               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) {
+                               /* Store information */
+                               snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
+                               txt = fopen(txtfile, "w+");
+                               if (txt) {
+                                       get_date(date, sizeof(date));
+                                       time(&start);
+                                       fprintf(txt, 
 ";\n"
 "; Message Information file\n"
 ";\n"
 ";\n"
 "; Message Information file\n"
 ";\n"
@@ -644,197 +1006,32 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
        chan->name,
        chan->callerid ? chan->callerid : "Unknown",
        date, time(NULL));
        chan->name,
        chan->callerid ? chan->callerid : "Unknown",
        date, time(NULL));
-                                                       fclose(txt);
-                                               } else
-                                                       ast_log(LOG_WARNING, "Error opening text file for output\n");
-       
-                                               /* We need to reset these values */
-                                               free(fmts);
-                                               fmt = ast_variable_retrieve(cfg, "general", "format");
-                                               fmts = strdup(fmt);
-                                               stringp=fmts;
-                                               strsep(&stringp, "|");
-                                               while((fmt = strsep(&stringp, "|"))) {
-                                                       if (fmtcnt > MAX_OTHER_FORMATS - 1) {
-                                                               ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
-                                                               break;
-                                                       }
-                                                       sfmt[fmtcnt++] = strdup(fmt);
-                                               }
-                                               for (x=0;x<fmtcnt;x++) {
-                                                       others[x] = ast_writefile(fn, sfmt[x], comment, 0, 0, 0700);
-                                                       if (!others[x]) {
-                                                               /* Ick, the other format didn't work, but be sure not
-                                                                  to leak memory here */
-                                                               int y;
-                                                               for(y=x+1;y < fmtcnt;y++)
-                                                                       free(sfmt[y]);
-                                                               break;
-                                                       }
-                                                       if(!strcasecmp(sfmt[x], "wav"))
-                                                               wavother++;
-                                                       free(sfmt[x]);
-                                               }
-                                               
-                                               sildet = ast_dsp_new(); //Create the silence detector
-                                               if (silence > 0) {
-                                                       rfmt = chan->readformat;
-                                                       res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
-                                                       if (res < 0) {
-                                                               ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
-                                                               return -1;
-                                                       }
-                                                       if (!sildet) {
-                                                               ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
-                                                               return -1;
-                                                       }
-                                                       ast_dsp_set_threshold(sildet, 50);
-                                               }
-                                               
-                                               if (x == fmtcnt) {
-                                                       /* Loop forever, writing the packets we read to the writer(s), until
-                                                          we read a # or get a hangup */
-                                                       if (option_verbose > 2) 
-                                                               ast_verbose( VERBOSE_PREFIX_3 "Recording to %s\n", fn);
-                                                       f = NULL;
-                                                       for(;;) {
-                                                               res = ast_waitfor(chan, 2000);
-                                                               if (!res) {
-                                                                       ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
-                                                                       /* Try one more time in case of masq */
-                                                                       res = ast_waitfor(chan, 2000);
-                                                                       if (!res) {
-                                                                               ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
-                                                                               res = -1;
-                                                                       }
-                                                               }
-                                                               
-                                                               if (res < 0) {
-                                                                       f = NULL;
-                                                                       break;
-                                                               }
-
-                                                               f = ast_read(chan);
-                                                               if (!f)
-                                                                       break;
-                                                               if (f->frametype == AST_FRAME_VOICE) {
-                                                                       /* Write the primary format */
-                                                                       res = ast_writestream(writer, f);
-                                                                       if (res) {
-                                                                               ast_log(LOG_WARNING, "Error writing primary frame\n");
-                                                                               break;
-                                                                       }
-                                                                       /* And each of the others */
-                                                                       for (x=0;x<fmtcnt;x++) {
-                                                                               res |= ast_writestream(others[x], f);
-                                                                       }
-                                                                       /* Exit on any error */
-                                                                       if (res) {
-                                                                               ast_log(LOG_WARNING, "Error writing frame\n");
-                                                                               ast_frfree(f);
-                                                                               break;
-                                                                       }
-                                                                       /* Silence Detection */
-                                                                       if (silence > 0) {
-                                                                               dspsilence = 0;
-                                                                               ast_dsp_silence(sildet, f, &dspsilence);
-                                                                               if (dspsilence) {
-                                                                                       totalsilence = dspsilence;
-                                                                               } else {
-                                                                                       totalsilence = 0;
-                                                                               }
-                                                                               if (totalsilence > silence) {
-                                                                               /* Ended happily with silence */
-                                                                               outmsg=2;
-                                                                               ast_frfree(f);
-                                                                               gotsilence = 1;
-                                                                               res = 0;
-                                                                               break;
-                                                                               }
-                                                                       }
-                                                                       
-                                                               } else if (f->frametype == AST_FRAME_DTMF) {
-                                                                       if (f->subclass == '#') {
-                                                                               if (option_verbose > 2) 
-                                                                                       ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
-                                                                               outmsg=2;
-                                                                               ast_frfree(f);
-                                                                               res = 0;
-                                                                               break;
-                                                                       }
-                                                               }
-                                                               ast_frfree(f);
-                                                               time(&end);
-                                                               if (maxmessage && (end - start > maxmessage)) {
-                                                                       if (option_verbose > 2) 
-                                                                               ast_verbose( VERBOSE_PREFIX_3 "Message is too long, ending it now...\n");
-                                                                       outmsg = 2;
-                                                                       res = 0;
-                                                                       break;
-                                                               }
-                                                       }
-                                                       if (!f) {
-                                                               if (option_verbose > 2) 
-                                                                       ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
-                                                               res = -1;
-                                                               outmsg=1;
-                                                       }
-                                                       if (gotsilence) {
-                                                               ast_stream_rewind(writer, silence-1000);
-                                                               ast_truncstream(writer);
-                                                       
-                                                               /* And each of the others */
-                                                               for (x=0;x<fmtcnt;x++) {
-                                                                       ast_stream_rewind(others[x], silence-1000);
-                                                                       ast_truncstream(others[x]);
-                                                               }
-                                                       }
-                                               } else {
-                                                       ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", fn, sfmt[x]); 
-                                                       free(sfmt[x]);
-                                               }
-
-                                               ast_closestream(writer);
-                                               for (x=0;x<fmtcnt;x++) {
-                                                       if (!others[x])
-                                                               break;
-                                                       ast_closestream(others[x]);
-                                               }
-                                               if (outmsg) {
-                                                       if (outmsg > 1) {
-                                                               /* Let them know it worked */
-                                                               ast_streamfile(chan, "vm-msgsaved", chan->language);
-                                                               ast_waitstream(chan, "");
-                                                       }
-                                                       txt = fopen(txtfile, "a");
-                                                       if (txt) {
-                                                               time(&end);
-                                                               fprintf(txt, "duration=%ld\n", end-start);
-                                                               fclose(txt);
-                                                       }
-                                                       /* Send e-mail if applicable */
-                                                       if (email)
-                                                               sendmail(astemail, email, name, msgnum, ext, chan->callerid, fn, wavother ? "wav" : fmts, end - start);
-                                                       if (pager)
-                                                               sendpage(astemail, pager, msgnum, ext, chan->callerid, end - start);
-                                               }
-                                       } else {
-                                               if (msgnum < MAXMSG)
-                                                       ast_log(LOG_WARNING, "Error writing to mailbox %s\n", ext);
-                                               else
-                                                       ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext);
-                                       }
-                                       free(fmts);
-                               } else 
-                                       ast_log(LOG_WARNING, "No format to save messages in \n");
-                       }
+                                       fclose(txt);
+                               } else
+                                       ast_log(LOG_WARNING, "Error opening text file for output\n");
+                               res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
+                               if (res > 0)
+                                       res = 0;
+                               txt = fopen(txtfile, "a");
+                               if (txt) {
+                                       time(&end);
+                                       fprintf(txt, "duration=%ld\n", end-start);
+                                       fclose(txt);
+                               }
+                               stringp = fmt;
+                               strsep(&stringp, "|");
+                               /* Send e-mail if applicable */
+                               if (strlen(vmu->email))
+                                       sendmail(serveremail, vmu->email, vmu->fullname, msgnum, ext, chan->callerid, fn, fmt, end - start);
+                               if (strlen(vmu->pager))
+                                       sendpage(serveremail, vmu->pager, msgnum, ext, chan->callerid, end - start);
+                       } else
+                               ast_log(LOG_WARNING, "No more messages possible\n");
                } else
                } else
-                       ast_log(LOG_WARNING, "Unable to playback instructions\n");
-                       
-               free(copy);
+                       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);
        } else
                ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
-       ast_destroy(cfg);
        /* Leave voicemail for someone */
        manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
        return res;
        /* Leave voicemail for someone */
        manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
        return res;
@@ -880,16 +1077,6 @@ static int count_messages(char *dir)
        return x;
 }
 
        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;
 static int say_and_wait(struct ast_channel *chan, int num)
 {
        int d;
@@ -936,7 +1123,7 @@ static int copy(char *infile, char *outfile)
        return 0;
 }
 
        return 0;
 }
 
-static int save_to_folder(char *dir, int msg, char *username, int box)
+static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
 {
        char sfn[256];
        char dfn[256];
 {
        char sfn[256];
        char dfn[256];
@@ -946,7 +1133,7 @@ static int save_to_folder(char *dir, int msg, char *username, int box)
        char *dbox = mbox(box);
        int x;
        make_file(sfn, sizeof(sfn), dir, msg);
        char *dbox = mbox(box);
        int x;
        make_file(sfn, sizeof(sfn), dir, msg);
-       make_dir(ddir, sizeof(ddir), username, dbox);
+       make_dir(ddir, sizeof(ddir), context, username, dbox);
        mkdir(ddir, 0700);
        for (x=0;x<MAXMSG;x++) {
                make_file(dfn, sizeof(dfn), ddir, x);
        mkdir(ddir, 0700);
        for (x=0;x<MAXMSG;x++) {
                make_file(dfn, sizeof(dfn), ddir, x);
@@ -1456,8 +1643,19 @@ static int get_folder(struct ast_channel *chan, int start)
        return d;
 }
 
        return d;
 }
 
+static int get_folder2(struct ast_channel *chan, char *fn, int start)
+{
+       int res = 0;
+       res = play_and_wait(chan, fn);
+       while (((res < '0') || (res > '9')) &&
+                       (res != '#') && (res >= 0)) {
+               res = get_folder(chan, 0);
+       }
+       return res;
+}
+
 static int
 static int
-forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int curmsg, char* myusername)
+forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
 {
        char username[70];
        char sys[256];
 {
        char username[70];
        char sys[256];
@@ -1466,341 +1664,334 @@ forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int
        long duration;
        struct ast_config *mif;
        char miffile[256];
        long duration;
        struct ast_config *mif;
        char miffile[256];
-       char *copy, *name, *passwd, *email, *pager;
-       char *mycopy, *myname, *mypasswd, *myemail, *mypager;
-       char *astemail;
        char fn[256];
        char callerid[512];
        char fn[256];
        char callerid[512];
+       int res = 0;
+       struct ast_vm_user *receiver, srec;
+       char tmp[256];
+       char *stringp, *s;
        
        
-       while(1) {
-               ast_streamfile(chan, "vm-extension", chan->language);
-
-               if (ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0)
-                       return 0;
-               if (ast_variable_retrieve(cfg, NULL, username)) {
+       while(!res) {
+               res = ast_streamfile(chan, "vm-extension", chan->language);
+               if (res)
+                       break;
+               if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
+                       break;
+               if ((receiver = find_user(&srec, context, username))) {
                        printf("Got %d\n", atoi(username));
                        /* if (play_and_wait(chan, "vm-savedto"))
                                break;
                        */
 
                        printf("Got %d\n", atoi(username));
                        /* if (play_and_wait(chan, "vm-savedto"))
                                break;
                        */
 
-                       snprintf(todir, sizeof(todir), "%s/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR,"vm", username);
+                       snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
                        snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
                        snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
-                       puts(sys);
+                       ast_log(LOG_DEBUG, sys);
                        system(sys);
 
                        todircount = count_messages(todir);
                        system(sys);
 
                        todircount = count_messages(todir);
-
-                       snprintf(sys, sizeof(sys), "cp %s/msg%04d.gsm %s/msg%04d.gsm\n", dir, curmsg, todir, todircount);
-                       puts(sys);
-                       system(sys);
-
-                       /* TODO: use config to determine what other formats to copy the message in */
-                       snprintf(sys, sizeof(sys), "cp %s/msg%04d.wav %s/msg%04d.wav\n", dir, curmsg, todir, todircount);
-                       puts(sys);
-                       system(sys);
-
-                       /* copy the message information file too */
-                       snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
-                       puts(sys);
-                       system(sys);
-                       
+                       strncpy(tmp, fmt, sizeof(tmp));
+                       stringp = tmp;
+                       while((s = strsep(&stringp, "|"))) {
+                               snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
+                               ast_log(LOG_DEBUG, sys);
+                               system(sys);
+                       }
                        snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
 
                        /* load the information on the source message so we can send an e-mail like a new message */
                        snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
                        if ((mif=ast_load(miffile))) {
 
                        snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
 
                        /* load the information on the source message so we can send an e-mail like a new message */
                        snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
                        if ((mif=ast_load(miffile))) {
 
-                         /* send an e-mail like it was a new message if appropriate */
-                         if ((copy = ast_variable_retrieve(cfg, NULL, username))) {                      
-                           char *stringp=NULL;
-                           /* Make sure they have an entry in the config */
-                           copy = strdup(copy);
-                           stringp=copy;
-                           passwd = strsep(&stringp, ",");
-                           name = strsep(&stringp, ",");
-                           email = strsep(&stringp, ",");
-                           pager = strsep(&stringp, ",");
-                         }
-                         
-                         if ((mycopy = ast_variable_retrieve(cfg, NULL, myusername))) {                          
-                           char *mystringp=NULL;
-                           /* Make sure they have an entry in the config */
-                           mycopy = strdup(mycopy);
-                           mystringp=mycopy;
-                           mypasswd = strsep(&mystringp, ",");
-                           myname = strsep(&mystringp, ",");
-                           myemail = strsep(&mystringp, ",");
-                           mypager = strsep(&mystringp, ",");
-                         }
-
-              /* set the outbound email from address */
-              if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
-                       astemail = ASTERISK_USERNAME;
-
               /* set callerid and duration variables */
               /* set callerid and duration variables */
-              snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", myname, ast_variable_retrieve(mif, NULL, "callerid"));
+              snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
               duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
                        
               duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
                        
-                         if (email)
-                           sendmail(astemail, email, name, todircount, username, callerid, fn, "wav", atol(ast_variable_retrieve(mif, NULL, "duration")));
+                         if (strlen(receiver->email))
+                           sendmail(serveremail, receiver->email, receiver->fullname, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")));
                                     
                                     
-                         if (pager)
-                               sendpage(astemail, pager, todircount, username, callerid, duration);
+                         if (strlen(receiver->pager))
+                               sendpage(serveremail, receiver->pager, todircount, username, callerid, duration);
                          
                          
-                         free(copy); /* no leaks here */
-                         free(mycopy); /* or here */
                          ast_destroy(mif); /* or here */
                        }
 
                        /* give confirmatopm that the message was saved */
                          ast_destroy(mif); /* or here */
                        }
 
                        /* give confirmatopm that the message was saved */
-                       if (play_and_wait(chan, "vm-message")) break;
-                       if (play_and_wait(chan, "vm-saved")) break;
-
+                       res = play_and_wait(chan, "vm-message");
+                       if (!res)
+                               res = play_and_wait(chan, "vm-saved");
+                       free_user(receiver);
                        break;
                } else {
                        break;
                } else {
-                       if ( play_and_wait(chan, "pbx-invalid"))
-                               break;
+                       res = play_and_wait(chan, "pbx-invalid");
                }
        }
                }
        }
-       return 0;
+       return res;
 }
 
 }
 
-#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); \
-       if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) { \
-               if (sscanf(s, "%d", &x) == 1) \
-                       ms = x; \
-       } \
-       d = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",ms); \
-       if (!d) { \
-               repeats = 0; \
-               goto instructions; \
-       } else if (d < 0) { \
-               goto out; \
-       } else goto cmd;\
-} while(0)
-
-#define PLAYMSG(a) do { \
-       starting = 0; \
-       make_file(fn, sizeof(fn), curdir, a); \
-       adsi_message(chan, curbox, a, lastmsg, deleted[a], fn); \
-       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, AST_DIGIT_ANY, 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 play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime)
+struct vm_state {
+       char curbox[80];
+       char username[80];
+       char curdir[256];
+       char vmbox[256];
+       char fn[256];
+       char fn2[256];
+       int deleted[MAXMSG];
+       int heard[MAXMSG];
+       int curmsg;
+       int lastmsg;
+       int newmessages;
+       int oldmessages;
+       int starting;
+       int repeats;
+};
+
+
+static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
 {
 {
-       char d, *fmt, *fmts;
-       char comment[256];
-       int x, fmtcnt=1, res=-1,outmsg=0, wavother=0;
-       struct ast_frame *f;
-       struct ast_config *cfg;
-       struct ast_filestream *others[MAX_OTHER_FORMATS];
-       char *sfmt[MAX_OTHER_FORMATS];
-       char *stringp=NULL;
-       time_t start, end;
-       
-       
-       ast_log(LOG_DEBUG,"play_and_record: %s, %s\n", playfile, recordfile);
-       snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile, recordfile, chan->name);
-       
-       d = play_and_wait(chan, playfile);
-       if (d < 0)
-               return -1;
-       ast_streamfile(chan, "beep",chan->language);
-       ast_waitstream(chan,"");
-       cfg = ast_load(VOICEMAIL_CONFIG);
-       
-       fmt = ast_variable_retrieve(cfg, "general", "format");
-       ast_log(LOG_DEBUG,"Recording Formats: fmt=%s\n", fmt);  
-       
-       fmts = strdup(fmt);
-       
-       ast_destroy(cfg);
+       int res;
+       if ((res = ast_streamfile(chan, file, chan->language))) 
+               ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
+       if (!res)
+               res = ast_waitstream(chan, AST_DIGIT_ANY);
+       return res;
+}
 
 
-       stringp=fmts;
-       strsep(&stringp, "|");
-       ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
-       sfmt[0] = strdup(fmts);
+static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
+{
+       int res;
+       if ((res = ast_streamfile(chan, file, chan->language)))
+               ast_log(LOG_WARNING, "Unable to play message %s\n", file);
+       if (!res)
+               res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
+       return res;
+}
+
+static int play_message(struct ast_channel *chan, struct vm_state *vms, int msg)
+{
+       int res = 0;
+       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)
+               res = wait_file2(chan, vms, "vm-first");
+       else if (msg == vms->lastmsg)
+               res = wait_file2(chan, vms, "vm-last");
+       if (!res) {
+               res = wait_file2(chan, vms, "vm-message");
+               if (msg && (msg != vms->lastmsg)) {
+                       if (!res)
+                               res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
+               }
+       }
        
        
-       while((fmt = strsep(&stringp, "|"))) {
-               if (fmtcnt > MAX_OTHER_FORMATS - 1) {
-                       ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
-                       break;
+       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 void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
+{
+       strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
+       make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
+       vms->lastmsg = count_messages(vms->curdir) - 1;
+       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
+}
+
+static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
+{
+       int x;
+       char ntxt[256] = "";
+       char txt[256] = "";
+       if (vms->lastmsg > -1) { 
+               /* Get the deleted messages fixed */ 
+               vms->curmsg = -1; 
+               for (x=0;x<=vms->lastmsg;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 */ 
+                               vms->curmsg++; 
+                               make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
+                               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<=vms->lastmsg; x++) { 
+                       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
+                       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)
+{
+       /* Introduce messages they have */
+       int res;
+       res = play_and_wait(chan, "vm-youhave");
+       if (!res) {
+               if (vms->newmessages) {
+                       res = say_and_wait(chan, vms->newmessages);
+                       if (!res)
+                               res = play_and_wait(chan, "vm-INBOX");
+                       if (vms->oldmessages && !res)
+                               res = play_and_wait(chan, "vm-and");
+                       else if (!res) {
+                               if ((vms->newmessages == 1))
+                                       res = play_and_wait(chan, "vm-message");
+                               else
+                                       res = play_and_wait(chan, "vm-messages");
                        }
                        }
-               sfmt[fmtcnt++] = strdup(fmt);
+                               
                }
                }
-
-       if (maxtime)
-               time(&start);
-       for (x=0;x<fmtcnt;x++) {
-               others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
-               ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s\n", x, recordfile, sfmt[x]);
-                       
-               if (!others[x]) {
-               /* Ick, the other format didn't work, but be sure not
-                  to leak memory here */
-                       int y;
-                       for(y=x+1;y < fmtcnt;y++)
-                       free(sfmt[y]);
-                       break;
+               if (!res && vms->oldmessages) {
+                       res = say_and_wait(chan, vms->oldmessages);
+                       if (!res)
+                               res = play_and_wait(chan, "vm-Old");
+                       if (!res) {
+                               if (vms->oldmessages == 1)
+                                       res = play_and_wait(chan, "vm-message");
+                               else
+                                       res = play_and_wait(chan, "vm-messages");
                        }
                        }
-               if(!strcasecmp(sfmt[x], "wav"))
-                       wavother++;
-                       free(sfmt[x]);
+               }
+               if (!res) {
+                       if (!vms->oldmessages && !vms->newmessages) {
+                               res = play_and_wait(chan, "vm-no");
+                               if (!res)
+                                       res = play_and_wait(chan, "vm-messages");
                        }
                        }
-               if (x == fmtcnt) {
-               /* Loop forever, writing the packets we read to the writer(s), until
-                  we read a # or get a hangup */
-                       f = NULL;
-                       for(;;) {
-                               res = ast_waitfor(chan, 2000);
-                               if (!res) {
-                                       ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
-                                       /* Try one more time in case of masq */
-                                       res = ast_waitfor(chan, 2000);
-                                       if (!res) {
-                                               ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
-                                               res = -1;
-                                       }
-                               }
-                               
-                               if (res < 0) {
-                                       f = NULL;
-                                       break;
-                               }
+               }
+       }
+       return res;
+}
 
 
-                               f = ast_read(chan);
-                               if (!f)
-                                       break;
-                               if (f->frametype == AST_FRAME_VOICE) {
-                                       /* write each format */
-                                       for (x=0;x<fmtcnt;x++) {
-                                               res = ast_writestream(others[x], f);
-                                               }
-                                       /* Exit on any error */
-                                       if (res) {
-                                               ast_log(LOG_WARNING, "Error writing frame\n");
-                                               ast_frfree(f);
-                                               break;
-                                       }
-                               } else if (f->frametype == AST_FRAME_DTMF) {
-                                       if (f->subclass == '#') {
-                                               if (option_verbose > 2) 
-                                                       ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
-                                               outmsg=2;
-                                               break;
-                                       }
-                               }
-                               if (maxtime) {
-                                       time(&end);
-                                       if (maxtime < (end - start)) {
-                                               if (option_verbose > 2)
-                                                       ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
-                                               outmsg=2;
-                                               break;
-                                       }
-                               }
-                               ast_frfree(f);
+static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
+{
+       int res = 0;
+       /* Play instructions and wait for new command */
+       while(!res) {
+               if (vms->starting) {
+                       if (vms->lastmsg > -1) {
+                               res = play_and_wait(chan, "vm-onefor");
+                               if (!res)
+                                       res = play_and_wait(chan, vms->vmbox);
+                               if (!res)
+                                       res = play_and_wait(chan, "vm-messages");
                        }
                        }
-                       if (!f) {
-                               if (option_verbose > 2) 
-                                       ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
-                               res = -1;
-                               outmsg=1;
-                               }
-                       } else {
-                               ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); 
-                               free(sfmt[x]);
-                               }
-
-                       for (x=0;x<fmtcnt;x++) {
-                               if (!others[x])
-                                       break;
-                               ast_closestream(others[x]);
-                               }
-                       if (outmsg) {
-                               if (outmsg > 1) {
-                               /* Let them know it worked */
-                                       ast_streamfile(chan, "vm-msgsaved", chan->language);
-                                       ast_waitstream(chan, "");
-                               }
-               }       
-
-       
-       return 0;
+                       if (!res)
+                               res = play_and_wait(chan, "vm-opts");
+               } else {
+                       if (vms->curmsg)
+                               res = play_and_wait(chan, "vm-prev");
+                       if (!res)
+                               res = play_and_wait(chan, "vm-repeat");
+                       if (!res && (vms->curmsg != vms->lastmsg))
+                               res = play_and_wait(chan, "vm-next");
+                       if (!res) {
+                               if (!vms->deleted[vms->curmsg])
+                                       res = play_and_wait(chan, "vm-delete");
+                               else
+                                       res = play_and_wait(chan, "vm-undelete");
+                               if (!res)
+                                       res = play_and_wait(chan, "vm-toforward");
+                               if (!res)
+                                       res = play_and_wait(chan, "vm-savemessage");
+                       }
+               }
+               if (!res)
+                       res = play_and_wait(chan, "vm-helpexit");
+               if (!res)
+                       res = ast_waitfordigit(chan, 6000);
+               if (!res) {
+                       vms->repeats++;
+                       if (vms->repeats > 2) {
+                               res = play_and_wait(chan, "vm-goodbye");
+                               if (!res)
+                                       res = 't';
+                       }
+               }
+       }
+       return res;
 }
 
 }
 
+static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
+{
+       int cmd = 0;
+       int retries = 0;
+       char newpassword[80] = "";
+       char newpassword2[80] = "";
+       char prefile[256]="";
+       while((cmd >= 0) && (cmd != 't')) {
+               if (cmd)
+                       retries = 0;
+               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);
+                       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);
+                       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);
+                       break;
+               case '4':
+                       newpassword[1] = '\0';
+                       newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
+                       if (cmd < 0)
+                               break;
+                       if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
+                               break;
+            }
+                       newpassword2[1] = '\0';
+                       newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
+                       if (cmd < 0)
+                               break;
 
 
-
+                       if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
+                               break;
+            }
+                       if (strcmp(newpassword, newpassword2)) {
+                               ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
+                               cmd = play_and_wait(chan, "vm-mismatch");
+                               break;
+                       }
+                       vm_change_password(vmu,newpassword);
+                       ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
+                       cmd = play_and_wait(chan,"vm-passchanged");
+                       break;
+               case '*': 
+                       cmd = 't';
+                       break;
+               default: 
+                       cmd = play_and_wait(chan,"vm-options");
+                       if (!cmd)
+                               cmd = ast_waitfordigit(chan,6000);
+                       if (!cmd)
+                               retries++;
+                       if (retries > 3)
+                               cmd = 't';
+                }
+       }
+       if (cmd == 't')
+               cmd = 0;
+       return cmd;
+}
 
 static int vm_execmain(struct ast_channel *chan, void *data)
 {
 
 static int vm_execmain(struct ast_channel *chan, void *data)
 {
@@ -1810,57 +2001,28 @@ static int vm_execmain(struct ast_channel *chan, void *data)
        int res=-1;
        int valid = 0;
        int prefix = 0;
        int res=-1;
        int valid = 0;
        int prefix = 0;
-       char d;
+       int cmd=0;
        struct localuser *u;
        struct localuser *u;
-       char username[80] ="";
        char prefixstr[80] ="";
        char empty[80] = "";
        char prefixstr[80] ="";
        char empty[80] = "";
-       char password[80] = "", *copy;
-       char newpassword[80] = "";
-       char newpassword2[80] = "";
-       char curbox[80] = "";
-       char curdir[256] = "";
-       char vmbox[256] = "";
-       char fn[256] = "";
-       char fn2[256] = "";
-       char prefile[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;
        int useadsi = 0;
        int skipuser = 0;
        int box;
        int useadsi = 0;
        int skipuser = 0;
-       char *s;
-       int ms = 3000;
-       int maxgreet = 0;
        char tmp[256], *ext;
        char tmp[256], *ext;
-       struct ast_config *cfg;
+       char fmtc[256] = "";
+       char password[80];
+       struct vm_state vms;
+       int logretries = 0;
+       struct ast_vm_user *vmu = NULL, vmus;
+       char *context=NULL;
 
        LOCAL_USER_ADD(u);
 
        LOCAL_USER_ADD(u);
-       cfg = ast_load(VOICEMAIL_CONFIG);
-       if (!cfg) {
-               ast_log(LOG_WARNING, "No voicemail configuration\n");
-               goto out;
-       }
-       if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
-               if (sscanf(s, "%d", &x) == 1) {
-                       maxgreet = x;
-               } else {
-                       ast_log(LOG_WARNING, "Invalid max message greeting length\n");
-               }
-       }
+       memset(&vms, 0, sizeof(vms));
+       strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
        if (chan->_state != AST_STATE_UP)
                ast_answer(chan);
 
        if (chan->_state != AST_STATE_UP)
                ast_answer(chan);
 
-       if (strlen(data)) {
+       if (data && strlen(data)) {
                strncpy(tmp, data, sizeof(tmp) - 1);
                ext = tmp;
 
                strncpy(tmp, data, sizeof(tmp) - 1);
                ext = tmp;
 
@@ -1877,13 +2039,17 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                break;
                }
 
                                break;
                }
 
+               context = strchr(ext, '@');
+               if (context) {
+                       *context = '\0';
+                       context++;
+               }
 
                if (prefix)
                        strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
                else
 
                if (prefix)
                        strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
                else
-                       strncpy(username, ext, sizeof(username) - 1);
-               /* make sure username passed as an option is valid */
-               if (ast_variable_retrieve(cfg, NULL, username)) 
+                       strncpy(vms.username, ext, sizeof(vms.username) - 1);
+               if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
                        skipuser++;
                else
                        valid = 0;
                        skipuser++;
                else
                        valid = 0;
@@ -1901,13 +2067,13 @@ static int vm_execmain(struct ast_channel *chan, void *data)
        
        /* Authenticate them and get their mailbox/password */
        
        
        /* Authenticate them and get their mailbox/password */
        
-       while (!valid) {
+       while (!valid && (logretries < maxlogins)) {
                /* Prompt for, and read in the username */
                /* Prompt for, and read in the username */
-               if (!skipuser && ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0) {
+               if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
                        ast_log(LOG_WARNING, "Couldn't read username\n");
                        goto out;
                }
                        ast_log(LOG_WARNING, "Couldn't read username\n");
                        goto out;
                }
-               if (!strlen(username)) {
+               if (!strlen(vms.username)) {
                        if (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
                        res = 0;
                        if (option_verbose > 2)
                                ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
                        res = 0;
@@ -1926,311 +2092,206 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                if (prefix) {
                        char fullusername[80] = "";
                        strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
                if (prefix) {
                        char fullusername[80] = "";
                        strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
-                       strncat(fullusername, username, sizeof(fullusername) - 1);
-                       strncpy(username, fullusername, sizeof(username) - 1);
+                       strncat(fullusername, vms.username, sizeof(fullusername) - 1);
+                       strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
                }
                }
-               copy = ast_variable_retrieve(cfg, NULL, username);
-               if (copy) {
-                       char *stringp=NULL;
-                       copy = strdup(copy);
-                       stringp=copy;
-                       strsep(&stringp, ",");
-                       if (!strcmp(password,copy))
-                               valid++;
-                       else {
-                               if (option_verbose > 2)
-                                       ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s'\n", password, username);
-                               if (prefix)
-                                       strncpy(username, empty, sizeof(username) -1);
-                       }
-                       free(copy);
-               } else {
-                       skipuser = 0;
+               if (!skipuser) 
+                       vmu = find_user(&vmus, context, vms.username);
+               if (vmu && !strcmp(vmu->password, password)) 
+                       valid++;
+               else {
                        if (option_verbose > 2)
                        if (option_verbose > 2)
-                               ast_verbose( VERBOSE_PREFIX_3 "No such user '%s' in config file\n", username);
+                               ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
+                       if (prefix)
+                               strncpy(vms.username, empty, sizeof(vms.username) -1);
                }
                if (!valid) {
                        if (useadsi)
                                adsi_login(chan);
                        if (ast_streamfile(chan, "vm-incorrect", chan->language))
                                break;
                }
                if (!valid) {
                        if (useadsi)
                                adsi_login(chan);
                        if (ast_streamfile(chan, "vm-incorrect", chan->language))
                                break;
-#if 0
-                       if (ast_waitstream(chan, ""))
-                               break;
-#endif
                }
                }
+               logretries++;
+       }
+       if (!valid && (logretries >= maxlogins)) {
+               ast_stopstream(chan);
+               res = play_and_wait(chan, "vm-goodbye");
+               if (res > 0)
+                       res = 0;
        }
 
        if (valid) {
        }
 
        if (valid) {
-               snprintf(curdir, sizeof(curdir), "%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,"vm", username);
-               mkdir(curdir, 0700);
-               OPEN_MAILBOX(1);
-               oldmessages = lastmsg + 1;
+               snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
+               mkdir(vms.curdir, 0700);
+               snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
+               mkdir(vms.curdir, 0700);
+               /* Retrieve old and new message counts */
+               open_mailbox(&vms, vmu, 1);
+               vms.oldmessages = vms.lastmsg + 1;
                /* Start in INBOX */
                /* Start in INBOX */
-               OPEN_MAILBOX(0);
-               newmessages = lastmsg + 1;
+               open_mailbox(&vms, vmu, 0);
+               vms.newmessages = vms.lastmsg + 1;
                
 
                /* Select proper mailbox FIRST!! */
                
 
                /* Select proper mailbox FIRST!! */
-               if (!newmessages && oldmessages) {
+               if (!vms.newmessages && vms.oldmessages) {
                        /* If we only have old messages start here */
                        /* If we only have old messages start here */
-                       OPEN_MAILBOX(1);
+                       open_mailbox(&vms, vmu, 1);
                }
 
                if (useadsi)
                }
 
                if (useadsi)
-                       adsi_status(chan, newmessages, oldmessages, lastmsg);
-
-               WAITCMD(play_and_wait(chan, "vm-youhave"));
-               if (newmessages) {
-                       WAITCMD(say_and_wait(chan, newmessages));
-                       WAITCMD(play_and_wait(chan, "vm-INBOX"));
-
-                       if (oldmessages)
-                               WAITCMD(play_and_wait(chan, "vm-and"));
-                       else {
-                               if (newmessages == 1)
-                                       WAITCMD(play_and_wait(chan, "vm-message"));
+                       adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
+               res = 0;
+               cmd = vm_intro(chan, &vms);
+               vms.repeats = 0;
+               vms.starting = 1;
+               while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
+                       /* Run main menu */
+                       switch(cmd) {
+                       case '1':
+                               vms.curmsg = 0;
+                               /* Fall through */
+                       case '5':
+                               if (vms.lastmsg > -1) {
+                                       cmd = play_message(chan, &vms, vms.curmsg);
+                               } else {
+                                       cmd = play_and_wait(chan, "vm-youhave");
+                                       if (!cmd) 
+                                               cmd = play_and_wait(chan, "vm-no");
+                                       if (!cmd) {
+                                               snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
+                                               cmd = play_and_wait(chan, vms.fn);
+                                       }
+                                       if (!cmd)
+                                               cmd = play_and_wait(chan, "vm-messages");
+                               }
+                               break;
+                       case '2': /* Change folders */
+                               if (useadsi)
+                                       adsi_folders(chan, 0, "Change to folder...");
+                               cmd = get_folder2(chan, "vm-changeto", 0);
+                               if (cmd == '#') {
+                                       cmd = 0;
+                               } else if (cmd > 0) {
+                                       cmd = cmd - '0';
+                                       close_mailbox(&vms, vmu);
+                                       open_mailbox(&vms, vmu, cmd);
+                                       cmd = 0;
+                               }
+                               if (useadsi)
+                                       adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
+                               if (!cmd)
+                                       cmd = play_and_wait(chan, vms.vmbox);
+                               if (!cmd)
+                                       cmd = play_and_wait(chan, "vm-messages");
+                               vms.starting = 1;
+                               break;
+                       case '4':
+                               if (vms.curmsg) {
+                                       vms.curmsg--;
+                                       cmd = play_message(chan, &vms, vms.curmsg);
+                               } else {
+                                       cmd = play_and_wait(chan, "vm-nomore");
+                               }
+                               break;
+                       case '6':
+                               if (vms.curmsg < vms.lastmsg) {
+                                       vms.curmsg++;
+                                       cmd = play_message(chan, &vms, vms.curmsg);
+                               } else {
+                                       cmd = play_and_wait(chan, "vm-nomore");
+                               }
+                               break;
+                       case '7':
+                               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]) 
+                                       cmd = play_and_wait(chan, "vm-deleted");
                                else
                                else
-                                       WAITCMD(play_and_wait(chan, "vm-messages"));
-                       }
-                               
-               }
-               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"));
-               }
-               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"));
-                       }
-                       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;
-               if (!d) {
-                       repeats++;
-                       if (repeats > 2) {
-                               play_and_wait(chan, "vm-goodbye");
-                               goto out;
+                                       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);
+                               break;
+                       case '9':
+                               if (useadsi)
+                                       adsi_folders(chan, 1, "Save to folder...");
+                               cmd = get_folder2(chan, "vm-savefolder", 1);
+                               box = 0;        /* Shut up compiler */
+                               if (cmd == '#') {
+                                       cmd = 0;
+                                       break;
+                               } else if (cmd > 0) {
+                                       box = cmd = cmd - '0';
+                                       cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
+                                       vms.deleted[vms.curmsg]=1;
+                               }
+                               make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
+                               if (useadsi)
+                                       adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
+                               if (!cmd)
+                                       cmd = play_and_wait(chan, "vm-message");
+                               if (!cmd)
+                                       cmd = say_and_wait(chan, vms.curmsg + 1);
+                               if (!cmd)
+                                       cmd = play_and_wait(chan, "vm-savedto");
+                               if (!cmd) {
+                                       snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
+                                       cmd = play_and_wait(chan, vms.fn);
+                               }
+                               if (!cmd)
+                                       cmd = play_and_wait(chan, "vm-messages");
+                               break;
+                       case '*':
+                               if (!vms.starting) {
+                                       cmd = play_and_wait(chan, "vm-onefor");
+                                       if (!cmd)
+                                               cmd = play_and_wait(chan, vms.vmbox);
+                                       if (!cmd)
+                                               cmd = play_and_wait(chan, "vm-messages");
+                                       if (!cmd)
+                                               cmd = play_and_wait(chan, "vm-opts");
+                               } else
+                                       cmd = 0;
+                               break;
+                       case '0':
+                               cmd = vm_options(chan, vmu, &vms, vmfmts);
+                               break;
+                       default:        /* Nothing */
+                               cmd = vm_instructions(chan, &vms);
+                               break;
                        }
                        }
-                       goto instructions;
                }
                }
-cmd:
-               switch(d) {
-               case '2':
-                       if (useadsi)
-                               adsi_folders(chan, 0, "Change to folder...");
-                       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);
-                       if (useadsi)
-                               adsi_status2(chan, curbox, lastmsg + 1);
-                       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 {
-                               WAITCMD(play_and_wait(chan, "vm-nomore"));
-                               goto instructions;
-                       }
-               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;
-                       }
-               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 (useadsi)
-                               adsi_delete(chan, curmsg, lastmsg, deleted[curmsg]);
-                       if (deleted[curmsg]) 
-                               WAITCMD(play_and_wait(chan, "vm-deleted"));
-                       else
-                               WAITCMD(play_and_wait(chan, "vm-undeleted"));
-                       goto instructions;
-               case '8':
-                       if(lastmsg > -1)
-                               if(forward_message(chan, cfg, curdir, curmsg, username) < 0)
-                                       goto out;
-                       goto instructions;
-               case '9':
-                       if (useadsi)
-                               adsi_folders(chan, 1, "Save to folder...");
-                       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;
-                               if (box == '#')
-                                       goto instructions;
-                       } 
-                       box = box - '0';
-                       if (option_debug)
-                               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;
-                       make_file(fn, sizeof(fn), curdir, curmsg);
-                       if (useadsi)
-                               adsi_message(chan, curbox, curmsg, lastmsg, deleted[curmsg], fn);
-                       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 '#':
-                       ast_stopstream(chan);
-                       adsi_goodbye(chan);
-                       play_and_wait(chan, "vm-goodbye");
+               if ((cmd == 't') || (cmd == '#')) {
+                       /* Timeout */
                        res = 0;
                        res = 0;
-                       goto out2;
-
-               case '0':
-                       goto vm_options;
-
-               default:
-                       goto instructions;
+               } else {
+                       /* Hangup */
+                       res = -1;
                }
        }
 out:
                }
        }
 out:
-       adsi_goodbye(chan);
-out2:
-       CLOSE_MAILBOX;
-       ast_stopstream(chan);
-       if (cfg)
-               ast_destroy(cfg);
-       if (useadsi)
-               adsi_unload_session(chan);
+       if (res > -1) {
+               ast_stopstream(chan);
+               adsi_goodbye(chan);
+               res = play_and_wait(chan, "vm-goodbye");
+               if (res > 0)
+                       res = 0;
+               if (useadsi)
+                       adsi_unload_session(chan);
+       }
+       if (vmu)
+               close_mailbox(&vms, vmu);
+       if (vmu)
+               free_user(vmu);
        if (valid) {
        if (valid) {
-               manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
+               manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
        }
        LOCAL_USER_REMOVE(u);
        return res;
 
        }
        LOCAL_USER_REMOVE(u);
        return res;
 
-vm_options:
-       d = play_and_wait(chan,"vm-options");
-       if (!d)
-               d = ast_waitfordigit(chan,6000);
-       if (d < 0)
-               goto out;
-       switch (d) {
-               
-               case '1':
-                       snprintf(prefile,sizeof(prefile),"vm/%s/unavail",username);
-                       play_and_record(chan,"vm-rec-unv",prefile, maxgreet);
-                       break;
-               case '2': 
-                       snprintf(prefile,sizeof(prefile),"vm/%s/busy",username);
-                       play_and_record(chan,"vm-rec-busy",prefile, maxgreet);
-                       break;
-               case '3': 
-                       snprintf(prefile,sizeof(prefile),"vm/%s/greet",username);
-                       play_and_record(chan,"vm-rec-name",prefile, maxgreet);
-                       break;
-               case '4':
-                       newpassword[1] = '\0';
-                       newpassword[0] = play_and_wait(chan,"vm-newpassword");
-                       if (ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#") < 0) {
-                               play_and_wait(chan, "vm-sorry");
-                               ast_log(LOG_NOTICE,"Unable to read new password\n");
-                               goto vm_options;
-            }
-                       newpassword2[1] = '\0';
-                       newpassword2[0] = play_and_wait(chan,"vm-reenterpassword");
-
-                       if (ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#") < 0) {
-                               play_and_wait(chan, "vm-sorry");
-                               ast_log(LOG_NOTICE,"Unable to read re-entered password\n");
-                               goto vm_options;
-            }
-                       if (strcmp(newpassword, newpassword2)) {
-                               ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", username, newpassword, newpassword2);
-                               play_and_wait(chan, "vm-mismatch");
-                               goto vm_options;
-                       }
-                       if (vm_change_password(username,password,newpassword) < 0)
-                       {
-                               ast_log(LOG_DEBUG,"Failed to set new password of user %s\n",username);
-                       } else
-                ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",username,newpassword,strlen(newpassword));
-                       play_and_wait(chan,"vm-passchanged");
-                       break;
-               case '*': 
-                       goto instructions;
-
-               default: 
-                       goto vm_options;
-                }
-       goto vm_options;
 }
 
 static int vm_exec(struct ast_channel *chan, void *data)
 }
 
 static int vm_exec(struct ast_channel *chan, void *data)
@@ -2252,33 +2313,208 @@ static int vm_exec(struct ast_channel *chan, void *data)
                        return 0;
        }
        ext = tmp;
                        return 0;
        }
        ext = tmp;
-       if (*ext == 's') {
-               silent++;
-               ext++;
-       } else if (*ext == 'b') {
-               busy++;
-               ext++;
-       } else if (*ext == 'u') {
-               unavail++;
-               ext++;
+       while(*ext) {
+               if (*ext == 's') {
+                       silent = 2;
+                       ext++;
+               } else if (*ext == 'b') {
+                       busy=1;
+                       ext++;
+               } else if (*ext == 'u') {
+                       unavail=1;
+                       ext++;
+               } else 
+                       break;
        }
        res = leave_voicemail(chan, ext, silent, busy, unavail);
        LOCAL_USER_REMOVE(u);
        return res;
 }
 
        }
        res = leave_voicemail(chan, ext, silent, busy, unavail);
        LOCAL_USER_REMOVE(u);
        return res;
 }
 
+static int append_mailbox(char *context, char *mbox, char *data)
+{
+       /* Assumes lock is already held */
+       char tmp[256] = "";
+       char *stringp;
+       char *s;
+       struct ast_vm_user *vmu;
+       strncpy(tmp, data, sizeof(tmp));
+       vmu = malloc(sizeof(struct ast_vm_user));
+       if (vmu) {
+               memset(vmu, 0, sizeof(struct ast_vm_user));
+               strncpy(vmu->context, context, sizeof(vmu->context));
+               strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
+               stringp = tmp;
+               if ((s = strsep(&stringp, ","))) 
+                       strncpy(vmu->password, s, sizeof(vmu->password));
+               if ((s = strsep(&stringp, ","))) 
+                       strncpy(vmu->fullname, s, sizeof(vmu->fullname));
+               if ((s = strsep(&stringp, ","))) 
+                       strncpy(vmu->email, s, sizeof(vmu->email));
+               if ((s = strsep(&stringp, ","))) 
+                       strncpy(vmu->pager, s, sizeof(vmu->pager));
+               vmu->next = NULL;
+               if (usersl)
+                       usersl->next = vmu;
+               else
+                       users = vmu;
+               usersl = vmu;
+       }
+       return 0;
+}
+
+static int load_config(void)
+{
+       struct ast_vm_user *cur, *l;
+       struct ast_config *cfg;
+       char *cat;
+       struct ast_variable *var;
+       char *astattach;
+       char *silencestr;
+       char *thresholdstr;
+       char *fmt;
+       char *astemail;
+       char *s;
+       int x;
+
+       cfg = ast_load(VOICEMAIL_CONFIG);
+       ast_pthread_mutex_lock(&vmlock);
+       cur = users;
+       while(cur) {
+               l = cur;
+               cur = cur->next;
+               free_user(l);
+       }
+       users = NULL;
+       usersl = NULL;
+       if (cfg) {
+               /* General settings */
+               attach_voicemail = 1;
+               if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
+                       astattach = "yes";
+               attach_voicemail = ast_true(astattach);
+               maxsilence = 0;
+               if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
+                       maxsilence = atoi(silencestr);
+                       if (maxsilence > 0)
+                               maxsilence *= 1000;
+               }
+               
+               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);
+               
+               vmmaxmessage = 0;
+               if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
+                       if (sscanf(s, "%d", &x) == 1) {
+                               vmmaxmessage = x;
+                       } else {
+                               ast_log(LOG_WARNING, "Invalid max message time length\n");
+                       }
+               }
+               fmt = ast_variable_retrieve(cfg, "general", "format");
+               if (!fmt)
+                       fmt = "wav";    
+               strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
+
+               skipms = 3000;
+               if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
+                       if (sscanf(s, "%d", &x) == 1) {
+                               maxgreet = x;
+                       } else {
+                               ast_log(LOG_WARNING, "Invalid max message greeting length\n");
+                       }
+               }
+
+               if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
+                       if (sscanf(s, "%d", &x) == 1) {
+                               skipms = x;
+                       } else {
+                               ast_log(LOG_WARNING, "Invalid skipms value\n");
+                       }
+               }
+
+               maxlogins = 3;
+               if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
+                       if (sscanf(s, "%d", &x) == 1) {
+                               maxlogins = x;
+                       } else {
+                               ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
+                       }
+               }
+
+#ifdef USEMYSQLVM
+               if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
+                       strcpy(dbuser, "test");
+               } else {
+                       strcpy(dbuser, s);
+               }
+               if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
+                       strcpy(dbpass, "test");
+               } else {
+                       strcpy(dbpass, s);
+               }
+               if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
+                       strcpy(dbname, "vmdb");
+               } else {
+                       strcpy(dbname, s);
+               }
+#else
+               cat = ast_category_browse(cfg, NULL);
+               while(cat) {
+                       if (strcasecmp(cat, "general")) {
+                               /* Process mailboxes in this context */
+                               var = ast_variable_browse(cfg, cat);
+                               while(var) {
+                                       append_mailbox(cat, var->name, var->value);
+                                       var = var->next;
+                               }
+                       }
+                       cat = ast_category_browse(cfg, cat);
+               }
+#endif
+               ast_destroy(cfg);
+               ast_pthread_mutex_unlock(&vmlock);
+               return 0;
+       } else {
+               ast_pthread_mutex_unlock(&vmlock);
+               ast_log(LOG_WARNING, "Error reading voicemail config\n");
+               return -1;
+       }
+}
+
+int reload(void)
+{
+       return(load_config());
+}
+
 int unload_module(void)
 {
        int res;
        STANDARD_HANGUP_LOCALUSERS;
        res = ast_unregister_application(app);
        res |= ast_unregister_application(app2);
 int unload_module(void)
 {
        int res;
        STANDARD_HANGUP_LOCALUSERS;
        res = ast_unregister_application(app);
        res |= ast_unregister_application(app2);
+#ifdef USEMYSQLVM
+       mysql_logout();
+#endif
        return res;
 }
 
 int load_module(void)
 {
        int res;
        return res;
 }
 
 int load_module(void)
 {
        int res;
+       if ((res=load_config())) {
+               return(res);
+       }
+#ifdef USEMYSQLVM
+       if ((res=mysql_login())) {
+               return(res);
+       }
+#endif
        res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
        if (!res)
                res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
        res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
        if (!res)
                res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);