Merge in ast_strftime branch, which changes timestamps to be accurate to the microsec...
[asterisk/asterisk.git] / apps / app_voicemail.c
index 42ef554..4c051dc 100644 (file)
@@ -35,7 +35,7 @@ c-client (http://www.washington.edu/imap/
  *
  *
  *
- * \note  This file is now almost impossible to work with, due to all #ifdefs.
+ * \note  This file is now almost impossible to work with, due to all \#ifdefs.
  *        Feels like the database code before realtime. Someone - please come up
  *        with a plan to clean this up.
  */
@@ -82,10 +82,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <ctype.h>
 #include <signal.h>
 #include <pwd.h>
+#ifdef USE_SYSTEM_IMAP
+#include <imap/c-client.h>
+#include <imap/imap4r1.h>
+#include <imap/linkage.h>
+#else
 #include "c-client.h"
 #include "imap4r1.h"
 #include "linkage.h"
 #endif
+#endif
 
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
@@ -116,10 +122,12 @@ static char imapserver[48];
 static char imapport[8];
 static char imapflags[128];
 static char imapfolder[64];
+static char greetingfolder[64];
 static char authuser[32];
 static char authpassword[42];
 
 static int expungeonhangup = 1;
+static int imapgreetings = 0;
 AST_MUTEX_DEFINE_STATIC(delimiter_lock);
 static char delimiter = '\0';
 
@@ -149,7 +157,9 @@ static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int tar
 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
 static void update_messages_by_imapuser(const char *user, unsigned long number);
 
-
+static int imap_remove_file (char *dir, int msgnum);
+static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context);
+static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
 struct vmstate {
        struct vm_state *vms;
        AST_LIST_ENTRY(vmstate) list;
@@ -170,6 +180,16 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 #define VOICEMAIL_CONFIG "voicemail.conf"
 #define ASTERISK_USERNAME "asterisk"
 
+/* Define fast-forward, pause, restart, and reverse keys
+   while listening to a voicemail message - these are
+   strings, not characters */
+#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
+#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
+#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
+#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
+#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
+#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
+
 /* Default mail command to mail voicemail. Change it with the
     mailcmd= command in voicemail.conf */
 #define SENDMAIL "/usr/sbin/sendmail -t"
@@ -207,6 +227,15 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 
 
 enum {
+       NEW_FOLDER,
+       OLD_FOLDER,
+       WORK_FOLDER,
+       FAMILY_FOLDER,
+       FRIENDS_FOLDER,
+       GREETINGS_FOLDER
+} vm_box;
+
+enum {
        OPT_SILENT =           (1 << 0),
        OPT_BUSY_GREETING =    (1 << 1),
        OPT_UNAVAIL_GREETING = (1 << 2),
@@ -341,7 +370,7 @@ struct ast_vm_user {
        char uniqueid[20];               /*!< Unique integer identifier */
        char exit[80];
        char attachfmt[20];              /*!< Attachment format */
-       unsigned int flags;              /*!< VM_ flags */      
+       uint64_t flags;              /*!< VM_ flags */  
        int saydurationm;
        int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
        int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
@@ -394,7 +423,7 @@ struct vm_state {
 #ifdef ODBC_STORAGE
 static char odbc_database[80];
 static char odbc_table[80];
-#define RETRIEVE(a,b) retrieve_file(a,b)
+#define RETRIEVE(a,b,c,d) retrieve_file(a,b)
 #define DISPOSE(a,b) remove_file(a,b)
 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
 #define EXISTS(a,b,c,d) (message_exists(a,b))
@@ -403,8 +432,8 @@ static char odbc_table[80];
 #define DELETE(a,b,c) (delete_file(a,b))
 #else
 #ifdef IMAP_STORAGE
-#define RETRIEVE(a,b)
-#define DISPOSE(a,b)
+#define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
+#define DISPOSE(a,b) (imap_remove_file(a,b))
 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
@@ -412,7 +441,7 @@ static char odbc_table[80];
 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
 #define DELETE(a,b,c) (vm_delete(c))
 #else
-#define RETRIEVE(a,b)
+#define RETRIEVE(a,b,c,d)
 #define DISPOSE(a,b)
 #define STORE(a,b,c,d,e,f,g,h,i)
 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
@@ -466,7 +495,7 @@ static char *descrip_vm =
        "           message. The units are whole-number decibels (dB).\n"
        "    s    - Skip the playback of instructions for leaving a message to the\n"
        "           calling party.\n"
-       "    u    - Play the 'unavailable greeting.\n";
+       "    u    - Play the 'unavailable' greeting.\n";
 
 static char *synopsis_vmain = "Check Voicemail messages";
 
@@ -572,6 +601,13 @@ struct mwi_sub {
 
 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
 
+/* custom audio control prompts for voicemail playback */
+static char listen_control_forward_key[12];
+static char listen_control_reverse_key[12];
+static char listen_control_pause_key[12];
+static char listen_control_restart_key[12];
+static char listen_control_stop_key[12];
+
 /* custom password sounds */
 static char vm_password[80] = "vm-password";
 static char vm_newpassword[80] = "vm-newpassword";
@@ -583,9 +619,9 @@ static struct ast_flags globalflags = {0};
 
 static int saydurationminfo;
 
-static char dialcontext[AST_MAX_CONTEXT];
-static char callcontext[AST_MAX_CONTEXT];
-static char exitcontext[AST_MAX_CONTEXT];
+static char dialcontext[AST_MAX_CONTEXT] = "";
+static char callcontext[AST_MAX_CONTEXT] = "";
+static char exitcontext[AST_MAX_CONTEXT] = "";
 
 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
 
@@ -616,6 +652,7 @@ static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
 static void apply_options(struct ast_vm_user *vmu, const char *options);
+static int is_valid_dtmf(const char *key);
 
 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
@@ -628,12 +665,9 @@ static void populate_defaults(struct ast_vm_user *vmu)
        ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
        if (saydurationminfo)
                vmu->saydurationm = saydurationminfo;
-       if (callcontext)
-               ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
-       if (dialcontext)
-               ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
-       if (exitcontext)
-               ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
+       ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
+       ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
+       ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
        if (vmmaxsecs)
                vmu->maxsecs = vmmaxsecs;
        if (maxmsg)
@@ -777,6 +811,21 @@ static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *
        } 
 }
 
+static int is_valid_dtmf(const char *key)
+{
+       int i;
+       char *local_key = ast_strdupa(key);
+
+       for(i = 0; i < strlen(key); ++i) {
+               if(!strchr(VALID_DTMF, *local_key)) {
+                       ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
+                       return 0;
+               }
+               local_key++;
+       }
+       return 1;
+}
+
 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
 {
        struct ast_variable *var;
@@ -939,8 +988,9 @@ static int make_dir(char *dest, int len, const char *context, const char *ext, c
 #ifdef IMAP_STORAGE
 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
 {
-       if (mkdir(dir, 01777) && (errno != EEXIST)) {
-               ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
+       int res;
+       if ((res = ast_mkdir(dir, 01777))) {
+               ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
                return sprintf(dest, "%s/msg%04d", dir, num);
        }
        /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
@@ -983,27 +1033,12 @@ static int make_file(char *dest, int len, char *dir, int num)
 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
 {
        mode_t  mode = VOICEMAIL_DIR_MODE;
+       int res;
 
-       if (!ast_strlen_zero(context)) {
-               make_dir(dest, len, context, "", "");
-               if (mkdir(dest, mode) && errno != EEXIST) {
-                       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
-                       return -1;
-               }
-       }
-       if (!ast_strlen_zero(ext)) {
-               make_dir(dest, len, context, ext, "");
-               if (mkdir(dest, mode) && errno != EEXIST) {
-                       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
-                       return -1;
-               }
-       }
-       if (!ast_strlen_zero(folder)) {
-               make_dir(dest, len, context, ext, folder);
-               if (mkdir(dest, mode) && errno != EEXIST) {
-                       ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
-                       return -1;
-               }
+       make_dir(dest, len, context, ext, folder);
+       if ((res = ast_mkdir(dest, mode))) {
+               ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
+               return -1;
        }
        return 0;
 }
@@ -1929,10 +1964,10 @@ static char *quote(const char *from, char *to, size_t len)
  * fill in *tm for current time according to the proper timezone, if any.
  * Return tm so it can be used as a function argument.
  */
-static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
+static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
 {
        const struct vm_zone *z = NULL;
-       time_t t = time(NULL);
+       struct timeval t = ast_tvnow();
 
        /* Does this user have a timezone specified? */
        if (!ast_strlen_zero(vmu->zonetag)) {
@@ -1972,9 +2007,11 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
        char fname[256];
        char dur[256];
        char tmpcmd[256];
-       struct tm tm;
+       struct ast_tm tm;
        char *passdata2;
        size_t len_passdata;
+       char *greeting_attachment;
+
 #ifdef IMAP_STORAGE
 #define ENDL "\r\n"
 #else
@@ -1986,12 +2023,17 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
                ast_copy_string(who, srcemail, sizeof(who));
        else 
                snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+       
+       greeting_attachment = strrchr(ast_strdupa(attach), '/');
+       if (greeting_attachment)
+               *greeting_attachment++ = '\0';
+
        snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
-       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+       ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
        fprintf(p, "Date: %s" ENDL, date);
 
        /* Set date format for voicemail mail */
-       strftime(date, sizeof(date), emaildateformat, &tm);
+       ast_strftime(date, sizeof(date), emaildateformat, &tm);
 
        if (!ast_strlen_zero(fromstring)) {
                struct ast_channel *ast;
@@ -2050,8 +2092,9 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
                fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
                fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
                fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
-               if (!ast_strlen_zero(category))
+               if (!ast_strlen_zero(category)) 
                        fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
+               fprintf(p, "X-Asterisk-VM-Message-Type: %s\n", msgnum > -1 ? "Message" : greeting_attachment);
                fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
                fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
        }
@@ -2084,40 +2127,53 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
                        ast_channel_free(ast);
                } else
                        ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
-       } else {
+       } else if (msgnum > -1){
                fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
 
                "in mailbox %s from %s, on %s so you might" ENDL
                "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
                dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
+       } else {
+               fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
+                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
        }
        if (attach_user_voicemail) {
                /* Eww. We want formats to tell us their own MIME type */
                char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
                char tmpdir[256], newtmp[256];
-               int tmpfd;
+               int tmpfd = -1;
        
-               create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
-               snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
-               tmpfd = mkstemp(newtmp);
-               ast_debug(3, "newtmp: %s\n", newtmp);
                if (vmu->volgain < -.001 || vmu->volgain > .001) {
-                       snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
-                       ast_safe_system(tmpcmd);
-                       attach = newtmp;
-                       ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+                       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
+                       snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
+                       tmpfd = mkstemp(newtmp);
+                       ast_debug(3, "newtmp: %s\n", newtmp);
+                       if (tmpfd > -1) {
+                               snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
+                               ast_safe_system(tmpcmd);
+                               attach = newtmp;
+                               ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+                       }
                }
                fprintf(p, "--%s" ENDL, bound);
-               fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
+               if (msgnum > -1)
+                       fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
+               else
+                       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
                fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
                fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
-               fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
+               if (msgnum > -1)
+                       fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
+               else
+                       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
                snprintf(fname, sizeof(fname), "%s.%s", attach, format);
                base_encode(fname, p);
                fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
-               if (tmpfd > -1)
+               if (tmpfd > -1) {
+                       unlink(fname);
                        close(tmpfd);
-               unlink(newtmp);
+                       unlink(newtmp);
+               }
        }
 #undef ENDL
 }
@@ -2134,7 +2190,7 @@ static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *c
        }
        if (!strcmp(format, "wav49"))
                format = "WAV";
-       ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
+       ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %lld\n", attach, format, attach_user_voicemail, (unsigned long long)ast_test_flag((&globalflags), VM_ATTACH));
        /* Make a temporary file instead of piping directly to sendmail, in case the mail
           command hangs */
        if ((p = vm_mkftemp(tmp)) == NULL) {
@@ -2158,7 +2214,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
        char dur[PATH_MAX];
        char tmp[80] = "/tmp/astmail-XXXXXX";
        char tmp2[PATH_MAX];
-       struct tm tm;
+       struct ast_tm tm;
        FILE *p;
 
        if ((p = vm_mkftemp(tmp)) == NULL) {
@@ -2171,7 +2227,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
        else 
                snprintf(who, sizeof(who), "%s@%s", srcemail, host);
        snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
-       strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+       ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
        fprintf(p, "Date: %s\n", date);
 
        if (*pagerfromstring) {
@@ -2210,7 +2266,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
        } else
                fprintf(p, "Subject: New VM\n\n");
 
-       strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
+       ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
        if (pagerbody) {
                struct ast_channel *ast;
                if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
@@ -2237,11 +2293,10 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
 
 static int get_date(char *s, int len)
 {
-       struct tm tm;
-       time_t t;
-       t = time(0);
+       struct ast_tm tm;
+       struct timeval t = ast_tvnow();
        ast_localtime(&t, &tm, NULL);
-       return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
+       return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
 }
 
 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
@@ -2252,12 +2307,12 @@ static int invent_message(struct ast_channel *chan, char *context, char *ext, in
 
        snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
 
-       if ((res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
+       if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
                ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
                return -1;
        }
 
-       RETRIEVE(fn, -1);
+       RETRIEVE(fn, -1, ext, context);
        if (ast_fileexists(fn, NULL, NULL) > 0) {
                res = ast_stream_and_wait(chan, fn, ecodes);
                if (res) {
@@ -2518,8 +2573,11 @@ static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, i
        if (!ast_strlen_zero(vmu->serveremail))
                myserveremail = vmu->serveremail;
 
-       make_file(fn, sizeof(fn), dir, msgnum);
-
+       if (msgnum > -1)
+               make_file(fn, sizeof(fn), dir, msgnum);
+       else
+               ast_copy_string (fn, dir, sizeof(fn));
+       
        if (ast_strlen_zero(vmu->email))
                ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
 
@@ -2532,6 +2590,12 @@ static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, i
        if (!(p = vm_mkftemp(tmp))) {
                ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
                return -1;
+
+       }
+
+       if (msgnum < 0 && imapgreetings) {
+               init_mailstream(vms, GREETINGS_FOLDER);
+               imap_delete_old_greeting(fn, vms);
        }
        
        make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
@@ -2545,8 +2609,8 @@ static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, i
        fread(buf, len, 1, p);
        ((char *)buf)[len] = '\0';
        INIT(&str, mail_string, buf, len);
-       init_mailstream(vms, 0);
-       imap_mailbox_name(mailbox, vms, 0, 1);
+       init_mailstream(vms, NEW_FOLDER);
+       imap_mailbox_name(mailbox, vms, NEW_FOLDER, 1);
        if(!mail_append(vms->mailstream, mailbox, &str))
                ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
        fclose(p);
@@ -2579,7 +2643,7 @@ static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
         ast_debug(3,"Mailbox is set to %s\n",mailbox);
 
        /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
+       if (ast_strlen_zero(mailbox)) 
                return 0;
 
        if (strchr(mailbox, ',')) {
@@ -2626,7 +2690,7 @@ static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
                free_user(vmu);
                return -1;
        }
+
        /* check if someone is accessing this box right now... */
        if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
                ast_debug(3,"Returning before search - user is logged in\n");
@@ -2635,7 +2699,7 @@ static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
                free_user(vmu);
                return 0;
        }
+
        /* add one if not there... */
        if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
                ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
@@ -2649,20 +2713,20 @@ static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
                ast_debug(3,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
                vms_p->updated = 1;
                /* set mailbox to INBOX! */
-               ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
+               ast_copy_string(vms_p->curbox, mbox(NEW_FOLDER), sizeof(vms_p->curbox));
                init_vm_state(vms_p);
                vmstate_insert(vms_p);
        }
 
        /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
-       ret = init_mailstream(vms_p, 0);
+       ret = init_mailstream(vms_p, NEW_FOLDER);
        if (!vms_p->mailstream) {
                ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
                free_user(vmu);
                return -1;
        }
 
-       if (!ret && vms_p->updated == 1) {
+       if (!ret && vms_p->updated > 0) {
                if (newmsgs) {
                        pgm = mail_newsearchpgm();
                        hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
@@ -2693,10 +2757,8 @@ static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
                }
        }
 
-       if (vms_p->updated == 1) {  /* changes, so we did the searches above */
+       if (vms_p->updated > 1) {  /* changes, so we did the searches above */
                vms_p->updated = 0;
-       } else if (vms_p->updated > 1) {  /* decrement delay count */
-               vms_p->updated--;
        } else {  /* no changes, so don't search */
                mail_ping(vms_p->mailstream);
                /* Keep the old data */
@@ -2925,7 +2987,7 @@ static void run_externnotify(char *context, char *extension)
 }
 
 struct leave_vm_options {
-       unsigned int flags;
+       uint64_t flags;
        signed char record_gain;
 };
 
@@ -2952,7 +3014,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
        char priority[16];
        char origtime[16];
        char dir[PATH_MAX], tmpdir[PATH_MAX];
-       char dest[PATH_MAX];
        char fn[PATH_MAX];
        char prefile[PATH_MAX] = "";
        char tempfile[PATH_MAX] = "";
@@ -2989,26 +3050,23 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
        if (strcmp(vmu->context, "default"))
                snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
        else
-               ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
+               ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
        if (ast_test_flag(options, OPT_BUSY_GREETING)) {
-               res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
                snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
        } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
-               res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
                snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
        }
        snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
-       if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
+       if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
                ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
                return -1;
        }
-       RETRIEVE(tempfile, -1);
+       RETRIEVE(tempfile, -1, ext, context);
        if (ast_fileexists(tempfile, NULL, NULL) > 0)
                ast_copy_string(prefile, tempfile, sizeof(prefile));
        DISPOSE(tempfile, -1);
        /* It's easier just to try to make it than to check for its existence */
        create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
-       create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
 
        /* Check current or macro-calling context for special extensions */
        if (ast_test_flag(vmu, VM_OPERATOR)) {
@@ -3039,7 +3097,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 
        /* Play the beginning intro if desired */
        if (!ast_strlen_zero(prefile)) {
-               RETRIEVE(prefile, -1);
+               RETRIEVE(prefile, -1, ext, context);
                if (ast_fileexists(prefile, NULL, NULL) > 0) {
                        if (ast_streamfile(chan, prefile, chan->language) > -1) 
                                res = ast_waitstream(chan, ecodes);
@@ -3116,19 +3174,30 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 #ifdef IMAP_STORAGE
                /* Is ext a mailbox? */
                /* must open stream for this user to get info! */
-               vms = get_vm_state_by_mailbox(ext,0);
-               if (vms) {
-                       ast_debug(3, "Using vm_state, interactive set to %d.\n",vms->interactive);
-                       newmsgs = vms->newmessages++;
-                       oldmsgs = vms->oldmessages;
-               } else {
-                       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
-                       if(res < 0) {
-                               ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
+               res = inboxcount(ext_context, &newmsgs, &oldmsgs);
+               if(res < 0) {
+                       ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
+                       return -1;
+               }
+               if(!(vms = get_vm_state_by_mailbox(ext,0))) {
+               /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
+                * rarely be used*/
+                       if (!(vms = ast_calloc(1, sizeof(*vms)))) {
+                               ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
                                return -1;
                        }
+                       ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
+                       ast_copy_string(vms->username, ext, sizeof(vms->username));
+                       vms->mailstream = NIL;
+                       ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
+                       vms->updated=1;
+                       ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
+                       init_vm_state(vms);
+                       vmstate_insert(vms);
                        vms = get_vm_state_by_mailbox(ext,0);
                }
+               vms->newmessages++;
+               
                /* here is a big difference! We add one to it later */
                msgnum = newmsgs + oldmsgs;
                ast_debug(3, "Messagecount set to %d\n",msgnum);
@@ -3143,7 +3212,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                        return -1;
                }
                /* here is a big difference! We add one to it later */
-               msgnum = newmsgs + oldmsgs;
                ast_debug(3, "Messagecount set to %d\n",msgnum);
 
 #else
@@ -3245,7 +3313,9 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                                ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
                                        }
                                } else {
+#ifndef IMAP_STORAGE
                                        msgnum = last_message_index(vmu, dir) + 1;
+#endif
                                        make_file(fn, sizeof(fn), dir, msgnum);
 
                                        /* assign a variable with the name of the voicemail file */ 
@@ -3259,6 +3329,11 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                        ast_filerename(tmptxtfile, fn, NULL);
                                        rename(tmptxtfile, txtfile);
 
+                                       /* Properly set permissions on voicemail text descriptor file.
+                                          Unfortunately mkstemp() makes this file 0600 on most unix systems. */
+                                       if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
+                                               ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
+
                                        ast_unlock_path(dir);
                                        if (ast_check_realtime("voicemail_data")) {
                                                snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
@@ -4097,6 +4172,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
        char *temp;
        char todir[256];
        int todircount=0;
+       struct vm_state *dstvms;
 #endif
        char username[70]="";
        int res = 0, cmd = 0;
@@ -4240,7 +4316,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                char buf[1024] = "";
                int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
 #endif
-               RETRIEVE(dir, curmsg);
+               RETRIEVE(dir, curmsg, sender->mailbox, context);
                cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, vms);
                if (!cmd) {
                        AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
@@ -4287,7 +4363,19 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                /* should not assume "fmt" here! */
                                save_body(body,vms,"2",fmt);
  
-                               STORE(todir, vmtmp->mailbox, vmtmp->context, vms->curmsg, chan, vmtmp, fmt, duration, vms);
+                               /* get destination mailbox */
+                               dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
+                               if (dstvms) {
+                                       init_mailstream(dstvms, 0);
+                                       if (!dstvms->mailstream) {
+                                               ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
+                                       } else {
+                                               STORE(todir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
+                                               run_externnotify(vmtmp->context, vmtmp->mailbox); 
+                                       }
+                               } else {
+                                       ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
+                               }
 
                                if (!ast_strlen_zero(vmtmp->serveremail))
                                        myserveremail = vmtmp->serveremail;
@@ -4335,7 +4423,7 @@ static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file
 
 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
 {
-       return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms, NULL);
+       return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
 }
 
 static int play_message_category(struct ast_channel *chan, const char *category)
@@ -4383,8 +4471,7 @@ static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *v
        /* Set the DIFF_* variables */
        ast_localtime(&t, &time_now, NULL);
        tv_now = ast_tvnow();
-       tnow = tv_now.tv_sec;
-       ast_localtime(&tnow, &time_then, NULL);
+       ast_localtime(&tv_now, &time_then, NULL);
 
        /* Day difference */
        if (time_now.tm_year == time_then.tm_year)
@@ -4582,6 +4669,7 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc
                return -1;
        }
        
+       
        /* Find the format of the attached file */
 
        strsep(&attachedfilefmt, ".");
@@ -4718,7 +4806,7 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc
        /* Retrieve info from VM attribute file */
        make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
        snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
-       RETRIEVE(vms->curdir, vms->curmsg);
+       RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
        msg_cfg = ast_config_load(filename);
        if (!msg_cfg) {
                ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
@@ -4768,9 +4856,9 @@ static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int use
        char tmp[256], *t = tmp;
        size_t left = sizeof(tmp);
 
-       if (box == 1) {
-               ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
-               sprintf(vms->vmbox, "vm-%s", mbox(1));
+       if (box == OLD_FOLDER) {
+               ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
+               sprintf(vms->vmbox, "vm-%s", mbox(OLD_FOLDER));
        } else {
                ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
                snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
@@ -4790,8 +4878,10 @@ static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int use
        /* End with username */
        ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
 
-       if(box == 0 || box == 1)
+       if(box == NEW_FOLDER || box == OLD_FOLDER)
                sprintf(spec, "%s%s", tmp, use_folder? imapfolder: "INBOX");
+       else if (box == GREETINGS_FOLDER)
+               sprintf(spec, "%s%s", tmp, greetingfolder);
        else
                sprintf(spec, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
 }
@@ -4817,9 +4907,13 @@ static int init_mailstream(struct vm_state *vms, int box)
 
        if (delimiter == '\0') {                /* did not probe the server yet */
                char *cp;
+#ifdef USE_SYSTEM_IMAP
+#include <imap/linkage.c>
+#else
 #include "linkage.c"
+#endif
                /* Connect to INBOX first to get folders delimiter */
-               imap_mailbox_name(tmp, vms, 0, 0);
+               imap_mailbox_name(tmp, vms, NEW_FOLDER, 0);
                stream = mail_open(stream, tmp, debug ? OP_DEBUG : NIL);
                if (stream == NIL) {
                        ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
@@ -4871,11 +4965,11 @@ static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
        pgm->deleted = 0;
        pgm->undeleted = 1;
 
-       /* if box = 0, check for new, if box = 1, check for read */
-       if (box == 0) {
+       /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
+       if (box == NEW_FOLDER) {
                pgm->unseen = 1;
                pgm->seen = 0;
-       } else if (box == 1) {
+       } else if (box == OLD_FOLDER) {
                pgm->seen = 1;
                pgm->unseen = 0;
        }
@@ -4889,6 +4983,139 @@ static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
 
        return 0;
 }
+
+static int imap_remove_file(char *dir, int msgnum)
+{
+       char fn[PATH_MAX];
+       char full_fn[PATH_MAX];
+       char msgnums[80];
+       
+       if (msgnum > -1) {
+               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+               make_file(fn, sizeof(fn), dir, msgnum);
+       } else
+               ast_copy_string(fn, dir, sizeof(fn));
+       
+       if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
+               ast_filedelete(fn, NULL);       
+               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+               unlink(full_fn);
+       }
+       return 0;
+}
+
+static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context)
+{
+       struct ast_vm_user *vmu;
+       struct vm_state *vms_p;
+       char *file, *filename;
+       char *attachment;
+       /*char *mb, *cur;*/
+       int ret = 0, i;
+       BODY *body;
+
+       /* This function is only used for retrieval of IMAP greetings
+        * regular messages are not retrieved this way, nor are greetings
+        * if they are stored locally*/
+       if (msgnum > -1 || !imapgreetings) {
+               return 0;
+       } else {
+               file = strrchr(ast_strdupa(dir), '/');
+               if (file)
+                       *file++ = '\0';
+               else {
+                       ast_debug (1, "Failed to procure file name from directory passed.\n");
+                       return -1;
+               }
+       }
+       /* We have to get the user before we can open the stream! */
+       /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
+       vmu = find_user(NULL, context, mailbox);
+       if (!vmu) {
+               ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
+               return -1;
+       } else {
+               /* No IMAP account available */
+               if (vmu->imapuser[0] == '\0') {
+                       ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
+                       free_user(vmu);
+                       return -1;
+               }
+       }
+
+       /* check if someone is accessing this box right now... */
+       vms_p = get_vm_state_by_mailbox(mailbox,0);
+       if (!vms_p) {
+               ast_log(LOG_ERROR, "Voicemail state not found!\n");
+               return -1;
+       }
+       
+       ret = init_mailstream(vms_p, GREETINGS_FOLDER);
+       if (!vms_p->mailstream) {
+               ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
+               free_user(vmu);
+               return -1;
+       }
+
+       for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
+               mail_fetchstructure(vms_p->mailstream, i + 1, &body);
+               /* We have the body, now we extract the file name of the first attachment. */
+               if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
+                       attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
+               } else {
+                       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
+                       return -1;
+               }
+               filename = strsep(&attachment, ".");
+               if (!strcmp(filename, file)) {
+                       ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
+                       vms_p->msgArray[vms_p->curmsg] = i + 1;
+                       save_body(body, vms_p, "2", attachment);
+                       free_user(vmu);
+                       return 0;
+               }
+       }
+
+       free_user(vmu);
+       return -1;
+}
+
+static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
+{
+       char *file, *filename;
+       char *attachment;
+       char arg[10];
+       int i;
+       BODY* body;
+
+       
+       file = strrchr(ast_strdupa(dir), '/');
+       if (file)
+               *file++ = '\0';
+       else {
+               ast_log (LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
+               return -1;
+       }
+       
+       for (i = 0; i < vms->mailstream->nmsgs; i++) {
+               mail_fetchstructure(vms->mailstream, i + 1, &body);
+               /* We have the body, now we extract the file name of the first attachment. */
+               if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
+                       attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
+               } else {
+                       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
+                       return -1;
+               }
+               filename = strsep(&attachment, ".");
+               if (!strcmp(filename, file)) {
+                       sprintf (arg,"%d", i+1);
+                       mail_setflag (vms->mailstream,arg,"\\DELETED");
+               }
+       }
+       mail_expunge(vms->mailstream);
+       return 0;
+}
+
 #else
 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
 {
@@ -6176,13 +6403,11 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct
 
 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
 {
-       int res;
        int cmd = 0;
        int retries = 0;
        int duration = 0;
        char prefile[PATH_MAX] = "";
        unsigned char buf[256];
-       char dest[PATH_MAX];
        int bytes = 0;
 
        if (ast_adsi_available(chan)) {
@@ -6195,14 +6420,10 @@ static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, st
        }
 
        snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
-       if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
-               ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
-               return -1;
-       }
        while((cmd >= 0) && (cmd != 't')) {
                if (cmd)
                        retries = 0;
-               RETRIEVE(prefile, -1);
+               RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
                if (ast_fileexists(prefile, NULL, NULL) <= 0) {
 #ifndef IMAP_STORAGE
                        play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, NULL);
@@ -6501,7 +6722,6 @@ static int vm_execmain(struct ast_channel *chan, void *data)
        int res=-1;
        int cmd=0;
        int valid = 0;
-       struct ast_module_user *u;
        char prefixstr[80] ="";
        char ext_context[256]="";
        int box;
@@ -6518,7 +6738,6 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 #ifdef IMAP_STORAGE
        int deleted = 0;
 #endif
-       u = ast_module_user_add(chan);
 
        /* Add the vm_state to the active list and keep it active */
        memset(&vms, 0, sizeof(vms));
@@ -6544,16 +6763,13 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                AST_STANDARD_APP_ARGS(args, parse);
 
                if (args.argc == 2) {
-                       if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
-                               ast_module_user_remove(u);
+                       if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
                                return -1;
-                       }
                        if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
                                int gain;
-                               if (opts[OPT_ARG_RECORDGAIN]) {
+                               if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
                                        if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
                                                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
-                                               ast_module_user_remove(u);
                                                return -1;
                                        } else {
                                                record_gain = (signed char) gain;
@@ -6623,7 +6839,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 
 #ifdef IMAP_STORAGE
        vms.interactive = 1;
-       vms.updated = 2;
+       vms.updated = 1;
        vmstate_insert(&vms);
        init_vm_state(&vms);
 #endif
@@ -6645,13 +6861,13 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 #endif
        /* Retrieve old and new message counts */
        ast_debug(1, "Before open_mailbox\n");
-       res = open_mailbox(&vms, vmu, 1);
+       res = open_mailbox(&vms, vmu, OLD_FOLDER);
        if (res == ERROR_LOCK_PATH)
                goto out;
        vms.oldmessages = vms.lastmsg + 1;
        ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
        /* Start in INBOX */
-       res = open_mailbox(&vms, vmu, 0);
+       res = open_mailbox(&vms, vmu, NEW_FOLDER);
        if (res == ERROR_LOCK_PATH)
                goto out;
        vms.newmessages = vms.lastmsg + 1;
@@ -6672,7 +6888,8 @@ static int vm_execmain(struct ast_channel *chan, void *data)
        } else {
                if (!vms.newmessages && vms.oldmessages) {
                        /* If we only have old messages start here */
-                       res = open_mailbox(&vms, vmu, 1);
+                       res = open_mailbox(&vms, vmu, OLD_FOLDER);
+                       play_folder = 1;
                        if (res == ERROR_LOCK_PATH)
                                goto out;
                }
@@ -6736,6 +6953,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                res = open_mailbox(&vms, vmu, cmd);
                                if (res == ERROR_LOCK_PATH)
                                        goto out;
+                               play_folder = cmd;
                                cmd = 0;
                        }
                        if (useadsi)
@@ -6872,10 +7090,20 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
                                if (useadsi)
                                        adsi_delete(chan, &vms);
-                               if (vms.deleted[vms.curmsg]) 
+                               if (vms.deleted[vms.curmsg]) {
+                                       if (play_folder == 0)
+                                               vms.newmessages--;
+                                       else if (play_folder == 1)
+                                               vms.oldmessages--;
                                        cmd = ast_play_and_wait(chan, "vm-deleted");
-                               else
+                               }
+                               else {
+                                       if (play_folder == 0)
+                                               vms.newmessages++;
+                                       else if (play_folder == 1)
+                                               vms.oldmessages++;
                                        cmd = ast_play_and_wait(chan, "vm-undeleted");
+                               }
                                if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
                                        if (vms.curmsg < vms.lastmsg) {
                                                vms.curmsg++;
@@ -6902,6 +7130,11 @@ static int vm_execmain(struct ast_channel *chan, void *data)
                                cmd = ast_play_and_wait(chan, "vm-nomore");
                        break;
                case '9':
+                       if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
+                               /* No message selected */
+                               cmd = 0;
+                               break;
+                       }
                        if (useadsi)
                                adsi_folders(chan, 1, "Save to folder...");
                        cmd = get_folder2(chan, "vm-savefolder", 1);
@@ -7026,7 +7259,6 @@ out:
                free(vms.deleted);
        if (vms.heard)
                free(vms.heard);
-       ast_module_user_remove(u);
 
        return res;
 }
@@ -7034,7 +7266,6 @@ out:
 static int vm_exec(struct ast_channel *chan, void *data)
 {
        int res = 0;
-       struct ast_module_user *u;
        char *tmp;
        struct leave_vm_options leave_options;
        struct ast_flags flags = { 0 };
@@ -7043,8 +7274,6 @@ static int vm_exec(struct ast_channel *chan, void *data)
                AST_APP_ARG(argv0);
                AST_APP_ARG(argv1);
        );
-
-       u = ast_module_user_add(chan);
        
        memset(&leave_options, 0, sizeof(leave_options));
 
@@ -7055,17 +7284,14 @@ static int vm_exec(struct ast_channel *chan, void *data)
                tmp = ast_strdupa(data);
                AST_STANDARD_APP_ARGS(args, tmp);
                if (args.argc == 2) {
-                       if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
-                               ast_module_user_remove(u);
+                       if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
                                return -1;
-                       }
                        ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING);
                        if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
                                int gain;
 
                                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
                                        ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
-                                       ast_module_user_remove(u);
                                        return -1;
                                } else {
                                        leave_options.record_gain = (signed char) gain;
@@ -7075,14 +7301,10 @@ static int vm_exec(struct ast_channel *chan, void *data)
        } else {
                char tmp[256];
                res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
-               if (res < 0) {
-                       ast_module_user_remove(u);
+               if (res < 0)
                        return res;
-               }
-               if (ast_strlen_zero(tmp)) {
-                       ast_module_user_remove(u);
+               if (ast_strlen_zero(tmp))
                        return 0;
-               }
                args.argv0 = ast_strdupa(tmp);
        }
 
@@ -7093,8 +7315,6 @@ static int vm_exec(struct ast_channel *chan, void *data)
                pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
                res = 0;
        }
-       
-       ast_module_user_remove(u);
 
        return res;
 }
@@ -7166,7 +7386,6 @@ static int append_mailbox(char *context, char *mbox, char *data)
 
 static int vm_box_exists(struct ast_channel *chan, void *data) 
 {
-       struct ast_module_user *u;
        struct ast_vm_user svm;
        char *context, *box;
        AST_DECLARE_APP_ARGS(args,
@@ -7180,8 +7399,6 @@ static int vm_box_exists(struct ast_channel *chan, void *data)
                return -1;
        }
 
-       u = ast_module_user_add(chan);
-
        if (!dep_warning) {
                dep_warning = 1;
                ast_log(LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
@@ -7203,7 +7420,7 @@ static int vm_box_exists(struct ast_channel *chan, void *data)
                pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
        } else
                pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
-       ast_module_user_remove(u);
+
        return 0;
 }
 
@@ -7233,14 +7450,11 @@ static struct ast_custom_function mailbox_exists_acf = {
 
 static int vmauthenticate(struct ast_channel *chan, void *data)
 {
-       struct ast_module_user *u;
        char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
        struct ast_vm_user vmus;
        char *options = NULL;
        int silent = 0, skipuser = 0;
        int res = -1;
-
-       u = ast_module_user_add(chan);
        
        if (s) {
                s = ast_strdupa(s);
@@ -7267,7 +7481,6 @@ static int vmauthenticate(struct ast_channel *chan, void *data)
                res = 0;
        }
 
-       ast_module_user_remove(u);
        return res;
 }
 
@@ -7607,6 +7820,11 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
        
        AST_LIST_TRAVERSE(&users, vmu, list) {
                char dirname[256];
+
+#ifdef IMAP_STORAGE
+               int new, old;
+               inboxcount (vmu->mailbox, &new, &old);
+#endif
                
                make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
                astman_append(s,
@@ -7632,6 +7850,7 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
                              "MaxMessageLength: %d\r\n"
                              "NewMessageCount: %d\r\n"
 #ifdef IMAP_STORAGE
+                                 "OldMessageCount: %d\r\n"
                              "IMAPUser: %s\r\n"
 #endif
                              "\r\n",
@@ -7639,9 +7858,11 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
                              vmu->pager, vmu->serveremail, vmu->mailcmd, vmu->language,
                              vmu->zonetag, vmu->callback, vmu->dialout, vmu->uniqueid,
                              vmu->exit, vmu->saydurationm, vmu->attachfmt, vmu->volgain,
-                             vmu->maxmsg, vmu->maxsecs, count_messages(vmu, dirname)
+                             vmu->maxmsg, vmu->maxsecs, 
 #ifdef IMAP_STORAGE
-                             , vmu->imapuser
+                                 new, old, vmu->imapuser
+#else
+                                 count_messages(vmu, dirname)
 #endif
                        );
        }               
@@ -7661,10 +7882,18 @@ static int load_config(void)
        struct ast_variable *var;
        const char *val;
        const char *s;
+       const char *key;
        char *q, *stringp;
        int x;
        int tmpadsi[4];
 
+       /* set audio control prompts */
+       strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
+       strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
+       strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
+       strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
+       strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
+       
        cfg = ast_config_load(VOICEMAIL_CONFIG);
 
        AST_LIST_LOCK(&users);
@@ -7788,6 +8017,17 @@ static int load_config(void)
                } else {
                        ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
                }
+               if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
+                       imapgreetings = ast_true(val);
+               } else {
+                       imapgreetings = 0;
+               }
+               if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
+                       ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
+               } else {
+                       ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
+               }
+
 #endif
                /* External voicemail notify application */
                if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
@@ -8014,6 +8254,17 @@ static int load_config(void)
                        ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
                if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
                        ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
+               /* load configurable audio prompts */
+               if ((key = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(key))
+                       ast_copy_string(listen_control_forward_key, key, sizeof(listen_control_forward_key));
+               if ((key = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(key))
+                       ast_copy_string(listen_control_reverse_key, key, sizeof(listen_control_reverse_key));
+               if ((key = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(key))
+                       ast_copy_string(listen_control_pause_key, key, sizeof(listen_control_pause_key));
+               if ((key = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(key))
+                       ast_copy_string(listen_control_restart_key, key, sizeof(listen_control_restart_key));
+               if ((key = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(key))
+                       ast_copy_string(listen_control_stop_key, key, sizeof(listen_control_stop_key));
 
                if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
                        val = "no";
@@ -8220,8 +8471,6 @@ static int unload_module(void)
        res |= ast_manager_unregister("VoicemailUsersList");
        ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
        ast_uninstall_vm_functions();
-       
-       ast_module_user_hangup_all();
 
        if (poll_thread != AST_PTHREADT_NULL)
                stop_poll_thread();
@@ -8378,7 +8627,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
 
        make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
        snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
-       RETRIEVE(vms->curdir, vms->curmsg);
+       RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
        msg_cfg = ast_config_load(filename);
        DISPOSE(vms->curdir, vms->curmsg);
        if (!msg_cfg) {
@@ -8640,8 +8889,8 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                                /* User has hung up, no options to give */
                                if (!outsidecaller) {
                                        /* user was recording a greeting and they hung up, so let's delete the recording. */
-                                       vm_delete(tempfile);
-                               }
+                                       ast_filedelete(tempfile, NULL);
+                               }               
                                return cmd;
                        }
                        if (cmd == '0') {
@@ -8655,14 +8904,14 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
                                cmd = ast_play_and_wait(chan, "vm-tooshort");
-                               cmd = vm_delete(tempfile);
+                               cmd = ast_filedelete(tempfile, NULL);
                                break;
                        }
                        else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
                                /* Message is all silence */
                                if (option_verbose > 2)
                                        ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
-                               cmd = vm_delete(tempfile);
+                               cmd = ast_filedelete(tempfile, NULL);
                                cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
                                if (!cmd)
                                        cmd = ast_play_and_wait(chan, "vm-speakup");
@@ -8691,7 +8940,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                case '*':
                        /* Cancel recording, delete message, offer to take another message*/
                        cmd = ast_play_and_wait(chan, "vm-deleted");
-                       cmd = vm_delete(tempfile);
+                       cmd = ast_filedelete(tempfile, NULL);
                        if (outsidecaller) {
                                res = vm_exec(chan, NULL);
                                return res;
@@ -9204,10 +9453,10 @@ static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
        if (ast_strlen_zero(mailbox))
                return NULL;
 
-       if (!(start = strstr(mailbox, "user=")))
+       if (!(start = strstr(mailbox, "/user=")))
                return NULL;
 
-       ast_copy_string(buf, start+5, len);
+       ast_copy_string(buf, start+6, len);
 
        if (!(quote = strchr(buf, '\"'))) {
                if (!(eol_pnt = strchr(buf, '/')))
@@ -9236,7 +9485,7 @@ static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
                        continue;
                }
 
-               if (interactive == 2 || vlist->vms->interactive == interactive) {
+               if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
                        AST_LIST_UNLOCK(&vmstates);
                        return vlist->vms;
                }
@@ -9333,7 +9582,7 @@ static void vmstate_delete(struct vm_state *vms)
                ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
                altvms->newmessages = vms->newmessages;
                altvms->oldmessages = vms->oldmessages;
-               altvms->updated = 2;
+               altvms->updated = 1;
        }
        
        ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
@@ -9368,7 +9617,7 @@ static void set_update(MAILSTREAM * stream)
 
        ast_debug(3, "User %s mailbox set for update.\n", user);
        
-       vms->updated = 2; /* Set updated flag since mailbox changed */
+       vms->updated = 1; /* Set updated flag since mailbox changed */
 }
 
 static void init_vm_state(struct vm_state *vms)