Merge Vonage's MySQL Voicemail stuff
[asterisk/asterisk.git] / apps / app_voicemail2.c
index dcc504b..50cbd02 100755 (executable)
@@ -33,6 +33,9 @@
 #include <sys/time.h>
 #include <sys/stat.h>
 #include <time.h>
+#ifdef USEMYSQLVM
+#include <mysql/mysql.h>
+#endif
 
 #include <pthread.h>
 #include "../asterisk.h"
@@ -73,6 +76,17 @@ struct baseio {
        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 *adapp = "CoMa";
@@ -87,7 +101,7 @@ static char *synopsis_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"
@@ -101,10 +115,11 @@ static char *synopsis_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"
-"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 */
@@ -113,25 +128,201 @@ static char *app = "VoiceMail2";
 /* 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;
 
-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 int vm_change_password(char *username, char *password, char *newpassword)
+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 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. */
-
+               /*  This could also be done with a database function */
+       
         FILE *configin;
         FILE *configout;
                char inbuf[256];
@@ -182,12 +373,12 @@ static int vm_change_password(char *username, char *password, char *newpassword)
                                        }
                                } 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) {
-                                               fprintf(configout, "%s => %s,%s\n", username,newpassword,rest);
+                                               fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
                                        } else {
-                                               fprintf(configout, "%s => %s\n", username,newpassword);
+                                               fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
                                        }
                                } else {
                                        /* Put it back like it was */
@@ -200,7 +391,19 @@ static int vm_change_password(char *username, char *password, char *newpassword)
 
         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
@@ -336,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 *astattach;
-       struct ast_config *cfg;
+       if (!strcmp(format, "wav49"))
+               format = "WAV";
        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, '@'))
@@ -359,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");
-               if (ast_true(astattach)) {
+               if (attach_voicemail) {
                        // Something unique.
                        snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
 
@@ -367,14 +567,14 @@ static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *m
 
                        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");
@@ -402,9 +602,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char
        char dur[256];
        time_t t;
        struct tm tm;
-       struct ast_config *cfg;
        p = popen(SENDMAIL, "w");
-       cfg = ast_load(VOICEMAIL_CONFIG);
 
        if (p) {
                gethostname(host, sizeof(host));
@@ -441,11 +639,11 @@ static int get_date(char *s, int len)
        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];
-       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)
@@ -494,7 +692,11 @@ static int play_and_record(struct ast_channel *chan, char *playfile, char *recor
        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);
@@ -528,12 +730,29 @@ static int play_and_record(struct ast_channel *chan, char *playfile, char *recor
                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]);
+               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 */
@@ -562,6 +781,24 @@ static int play_and_record(struct ast_channel *chan, char *playfile, char *recor
                                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");
@@ -603,8 +840,18 @@ static int play_and_record(struct ast_channel *chan, char *playfile, char *recor
        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 */
@@ -617,81 +864,56 @@ static int play_and_record(struct ast_channel *chan, char *playfile, char *recor
        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)
 {
-       struct ast_config *cfg;
-       char *copy, *name=NULL, *passwd=NULL, *email=NULL, *pager=NULL, *fmt;
        char comment[256];
        char txtfile[256];
        FILE *txt;
-       int res = 0, x;
+       int res = 0;
        int msgnum;
-       int maxmessage=0;
        char date[256];
        char dir[256];
        char fn[256];
        char prefile[256]="";
-       char *astemail;
+       char fmt[80];
+       char *context;
        char *ecodes = "#";
-       char *s;
+       char *stringp;
        time_t start;
        time_t end;
-       int silence = 0;                /* amount of silence to allow */
-#if 0
-       /* XXX Need to be moved to play_and_record */
-       struct ast_dsp *sildet;         /* silence detector dsp */
-       int totalsilence = 0;
-       int dspsilence = 0;
-       int gotsilence = 0;             /* did we timeout for silence? */
-#endif 
-       char *silencestr;
-       char *thresholdstr;
-       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)
-                       snprintf(prefile, sizeof(prefile), "vm/%s/busy", ext);
+                       snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
                else if (unavail)
-                       snprintf(prefile, sizeof(prefile), "vm/%s/unavail", ext);
-               /* Make sure they have an entry in the config */
-               copy = strdupa(copy);
-               stringp=copy;
-               passwd = strsep(&stringp, ",");
-               if (passwd) 
-                       name = strsep(&stringp, ",");
-               if (name)
-                       email = strsep(&stringp, ",");
-               if (email)
-                       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));
-               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))
@@ -703,10 +925,11 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
                                    res = ast_waitstream(chan, "#0");
                        } else {
                                ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
-                               res = invent_message(chan, ext, busy, ecodes);
+                               res = invent_message(chan, vmu->context, ext, busy, ecodes);
                        }
                        if (res < 0) {
                                ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
+                               free_user(vmu);
                                return -1;
                        }
                }
@@ -730,25 +953,28 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
                        if (strlen(chan->macrocontext))
                                strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
                        chan->priority = 0;
+                       free_user(vmu);
                        return 0;
                }
-               if (!res && (silent < 2)) {
+               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)
+               if (res < 0) {
+                       free_user(vmu);
                        return -1;
+               }
                /* The meat of recording the message...  All the announcements and beeps have been played*/
-               fmt = ast_variable_retrieve(cfg, "general", "format");
-               if (fmt) {
+               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"), 
-                                                                       name, ext, chan->name);
+                                                                       vmu->fullname, ext, chan->name);
                                if (ast_fileexists(fn, NULL, chan->language) <= 0) 
                                        break;
                                msgnum++;
@@ -783,7 +1009,9 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
                                        fclose(txt);
                                } else
                                        ast_log(LOG_WARNING, "Error opening text file for output\n");
-                               res = play_and_record(chan, NULL, fn, maxmessage, fmt);
+                               res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
+                               if (res > 0)
+                                       res = 0;
                                txt = fopen(txtfile, "a");
                                if (txt) {
                                        time(&end);
@@ -793,34 +1021,17 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int
                                stringp = fmt;
                                strsep(&stringp, "|");
                                /* Send e-mail if applicable */
-                               if (email)
-                                       sendmail(astemail, email, name, msgnum, ext, chan->callerid, fn, fmt, end - start);
-                               if (pager)
-                                       sendpage(astemail, pager, msgnum, ext, chan->callerid, end - start);
+                               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
-                       ast_log(LOG_WARNING, "No format for saving voicemail?\n");
-       
-#if 0
-                                               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);
-                                               }
-#endif                                         
+                       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);
-       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;
@@ -912,7 +1123,7 @@ static int copy(char *infile, char *outfile)
        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];
@@ -922,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);
-       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);
@@ -1437,14 +1648,14 @@ 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 != '#') && (res >= 0)) {
                res = get_folder(chan, 0);
        }
        return res;
 }
 
 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];
@@ -1453,12 +1664,12 @@ forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int
        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];
        int res = 0;
+       struct ast_vm_user *receiver, srec;
+       char tmp[256];
+       char *stringp, *s;
        
        while(!res) {
                res = ast_streamfile(chan, "vm-extension", chan->language);
@@ -1466,78 +1677,41 @@ forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int
                        break;
                if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
                        break;
-               if (ast_variable_retrieve(cfg, NULL, username)) {
+               if ((receiver = find_user(&srec, context, username))) {
                        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);
-                       puts(sys);
+                       ast_log(LOG_DEBUG, sys);
                        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))) {
 
-                         /* 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 */
-              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"));
                        
-                         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 */
                        }
 
@@ -1545,6 +1719,7 @@ forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int
                        res = play_and_wait(chan, "vm-message");
                        if (!res)
                                res = play_and_wait(chan, "vm-saved");
+                       free_user(receiver);
                        break;
                } else {
                        res = play_and_wait(chan, "pbx-invalid");
@@ -1560,110 +1735,16 @@ struct vm_state {
        char vmbox[256];
        char fn[256];
        char fn2[256];
-       char password[80];
        int deleted[MAXMSG];
        int heard[MAXMSG];
        int curmsg;
-       int skipms;
        int lastmsg;
        int newmessages;
        int oldmessages;
        int starting;
        int repeats;
-       int maxgreet;
 };
 
-#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 wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
 {
@@ -1681,14 +1762,14 @@ static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
        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, "#", "*",vms->skipms);
+               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; \
+       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)
@@ -1711,15 +1792,15 @@ static int play_message(struct ast_channel *chan, struct vm_state *vms, int msg)
        return res;
 }
 
-static void open_mailbox(struct vm_state *vms, int box)
+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), vms->username, vms->curbox);
+       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)
+static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
 {
        int x;
        char ntxt[256] = "";
@@ -1741,7 +1822,7 @@ static void close_mailbox(struct vm_state *vms)
                                } 
                        } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
                                /* Move to old folder before deleting */ 
-                               save_to_folder(vms->curdir, x, vms->username, 1); 
+                               save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
                        } 
                } 
                for (x = vms->curmsg + 1; x<=vms->lastmsg; x++) { 
@@ -1846,7 +1927,7 @@ static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
        return res;
 }
 
-static int vm_options(struct ast_channel *chan, struct vm_state *vms, char *fmtc)
+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;
@@ -1858,25 +1939,16 @@ static int vm_options(struct ast_channel *chan, struct vm_state *vms, char *fmtc
                        retries = 0;
                switch (cmd) {
                case '1':
-                       snprintf(prefile,sizeof(prefile),"vm/%s/unavail",vms->username);
-                       cmd = play_and_record(chan,"vm-rec-unv",prefile, vms->maxgreet, fmtc);
-                       /* Ignore DTMF */
-                       if (cmd >0)
-                               cmd = 0;
+                       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),"vm/%s/busy",vms->username);
-                       cmd = play_and_record(chan,"vm-rec-busy",prefile, vms->maxgreet, fmtc);
-                       /* Ignore DTMF */
-                       if (cmd >0)
-                               cmd = 0;
+                       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),"vm/%s/greet",vms->username);
-                       cmd = play_and_record(chan,"vm-rec-name",prefile, vms->maxgreet, fmtc);
-                       /* Ignore DTMF */
-                       if (cmd >0)
-                               cmd = 0;
+                       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';
@@ -1899,11 +1971,8 @@ static int vm_options(struct ast_channel *chan, struct vm_state *vms, char *fmtc
                                cmd = play_and_wait(chan, "vm-mismatch");
                                break;
                        }
-                       if (vm_change_password(vms->username,vms->password,newpassword) < 0)
-                       {
-                               ast_log(LOG_DEBUG,"Failed to set new password of user %s\n",vms->username);
-                       } else
-                ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
+                       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 '*': 
@@ -1936,46 +2005,24 @@ static int vm_execmain(struct ast_channel *chan, void *data)
        struct localuser *u;
        char prefixstr[80] ="";
        char empty[80] = "";
-       char *copy;
-       int x;
        int box;
        int useadsi = 0;
        int skipuser = 0;
-       char *s;
        char tmp[256], *ext;
        char fmtc[256] = "";
-       struct ast_config *cfg;
+       char password[80];
        struct vm_state vms;
+       int logretries = 0;
+       struct ast_vm_user *vmu = NULL, vmus;
+       char *context=NULL;
 
        LOCAL_USER_ADD(u);
        memset(&vms, 0, sizeof(vms));
-       vms.skipms = 3000;
-       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) {
-                       vms.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) {
-                       vms.skipms = x;
-               } else {
-                       ast_log(LOG_WARNING, "Invalid skipms value\n");
-               }
-       }
-       if ((s = ast_variable_retrieve(cfg, "general", "format"))) {
-               strncpy(fmtc, s, sizeof(fmtc) - 1);
-       }
+       strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
        if (chan->_state != AST_STATE_UP)
                ast_answer(chan);
 
-       if (strlen(data)) {
+       if (data && strlen(data)) {
                strncpy(tmp, data, sizeof(tmp) - 1);
                ext = tmp;
 
@@ -1992,13 +2039,17 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                break;
                }
 
+               context = strchr(ext, '@');
+               if (context) {
+                       *context = '\0';
+                       context++;
+               }
 
                if (prefix)
                        strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
                else
                        strncpy(vms.username, ext, sizeof(vms.username) - 1);
-               /* make sure username passed as an option is valid */
-               if (ast_variable_retrieve(cfg, NULL, vms.username)) 
+               if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
                        skipuser++;
                else
                        valid = 0;
@@ -2016,7 +2067,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
        
        /* Authenticate them and get their mailbox/password */
        
-       while (!valid) {
+       while (!valid && (logretries < maxlogins)) {
                /* Prompt for, and read in the username */
                if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
                        ast_log(LOG_WARNING, "Couldn't read username\n");
@@ -2034,7 +2085,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                        ast_log(LOG_WARNING, "Unable to stream password file\n");
                        goto out;
                }
-               if (ast_readstring(chan, vms.password, sizeof(vms.password) - 1, 2000, 10000, "#") < 0) {
+               if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
                        ast_log(LOG_WARNING, "Unable to read password\n");
                        goto out;
                }
@@ -2044,53 +2095,48 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                        strncat(fullusername, vms.username, sizeof(fullusername) - 1);
                        strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
                }
-               copy = ast_variable_retrieve(cfg, NULL, vms.username);
-               if (copy) {
-                       char *stringp=NULL;
-                       copy = strdup(copy);
-                       stringp=copy;
-                       strsep(&stringp, ",");
-                       if (!strcmp(vms.password,copy))
-                               valid++;
-                       else {
-                               if (option_verbose > 2)
-                                       ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s'\n", vms.password, vms.username);
-                               if (prefix)
-                                       strncpy(vms.username, empty, sizeof(vms.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)
-                               ast_verbose( VERBOSE_PREFIX_3 "No such user '%s' in config file\n", vms.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 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) {
-               snprintf(vms.curdir, sizeof(vms.curdir), "%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,"vm", vms.username);
+               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, 1);
+               open_mailbox(&vms, vmu, 1);
                vms.oldmessages = vms.lastmsg + 1;
                /* Start in INBOX */
-               open_mailbox(&vms, 0);
+               open_mailbox(&vms, vmu, 0);
                vms.newmessages = vms.lastmsg + 1;
                
 
                /* Select proper mailbox FIRST!! */
                if (!vms.newmessages && vms.oldmessages) {
                        /* If we only have old messages start here */
-                       open_mailbox(&vms, 1);
+                       open_mailbox(&vms, vmu, 1);
                }
 
                if (useadsi)
@@ -2123,12 +2169,13 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                        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);
-                                       open_mailbox(&vms, cmd);
+                                       close_mailbox(&vms, vmu);
+                                       open_mailbox(&vms, vmu, cmd);
                                        cmd = 0;
                                }
                                if (useadsi)
@@ -2166,7 +2213,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                break;
                        case '8':
                                if(vms.lastmsg > -1)
-                                       cmd = forward_message(chan, cfg, vms.curdir, vms.curmsg, vms.username);
+                                       cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
                                break;
                        case '9':
                                if (useadsi)
@@ -2178,7 +2225,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                        break;
                                } else if (cmd > 0) {
                                        box = cmd = cmd - '0';
-                                       cmd = save_to_folder(vms.curdir, vms.curmsg, vms.username, cmd);
+                                       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);
@@ -2210,21 +2257,14 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                        cmd = 0;
                                break;
                        case '0':
-                               cmd = vm_options(chan, &vms, fmtc);
-                               break;
-                       case '#':
-                               ast_stopstream(chan);
-                               adsi_goodbye(chan);
-                               cmd = play_and_wait(chan, "vm-goodbye");
-                               if (cmd > 0)
-                                       cmd = '#';
+                               cmd = vm_options(chan, vmu, &vms, vmfmts);
                                break;
                        default:        /* Nothing */
                                cmd = vm_instructions(chan, &vms);
                                break;
                        }
                }
-               if (cmd == 't') {
+               if ((cmd == 't') || (cmd == '#')) {
                        /* Timeout */
                        res = 0;
                } else {
@@ -2233,15 +2273,19 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                }
        }
 out:
-       if ((res > -1) && (cmd != '#')) {
+       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);
        }
-       close_mailbox(&vms);
-       if (cfg)
-               ast_destroy(cfg);
+       if (vmu)
+               close_mailbox(&vms, vmu);
+       if (vmu)
+               free_user(vmu);
        if (valid) {
                manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
        }
@@ -2287,18 +2331,190 @@ static int vm_exec(struct ast_channel *chan, void *data)
        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);
+#ifdef USEMYSQLVM
+       mysql_logout();
+#endif
        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);