Merge "BuildSystem: For consistency, avoid double-checking via if clauses."
[asterisk/asterisk.git] / apps / app_voicemail.c
index 8e2df02..82fa3b3 100644 (file)
  * \addtogroup configuration_file Configuration Files
  */
 
-/*! 
+/*!
  * \page voicemail.conf voicemail.conf
  * \verbinclude voicemail.conf.sample
  */
 
 /*** MODULEINFO
        <defaultenabled>yes</defaultenabled>
-       <conflict>res_mwi_external</conflict>
        <use type="module">res_adsi</use>
        <use type="module">res_smdi</use>
        <support_level>core</support_level>
 #endif
 #endif
 
-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
-
 #include "asterisk/paths.h"    /* use ast_config_AST_SPOOL_DIR */
 #include <sys/time.h>
 #include <sys/stat.h>
@@ -466,6 +463,23 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                <description>
                </description>
        </manager>
+       <manager name="VoicemailUserStatus" language="en_US">
+               <synopsis>
+                       Show the status of given voicemail user's info.
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+                       <parameter name="Context" required="true">
+                               <para>The context you want to check.</para>
+                       </parameter>
+                       <parameter name="Mailbox" required="true">
+                               <para>The mailbox you want to check.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Retrieves the status of the given voicemail user.</para>
+               </description>
+       </manager>
        <manager name="VoicemailRefresh" language="en_US">
                <synopsis>
                        Tell Asterisk to poll mailboxes for a change
@@ -508,6 +522,7 @@ static int imapversion = 1;
 
 static int expungeonhangup = 1;
 static int imapgreetings = 0;
+static int imap_poll_logout = 0;
 static char delimiter = '\0';
 
 /* mail_open cannot be protected on a stream basis */
@@ -545,6 +560,8 @@ static int imap_retrieve_file (const char *dir, const int msgnum, const char *ma
 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
 static void check_quota(struct vm_state *vms, char *mailbox);
 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
+static void imap_logout(const char *mailbox_id);
+
 struct vmstate {
        struct vm_state *vms;
        AST_LIST_ENTRY(vmstate) list;
@@ -581,6 +598,8 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 
 #define INTRO "vm-intro"
 
+#define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
+
 #define MAXMSG 100
 #define MAXMSGLIMIT 9999
 
@@ -617,15 +636,16 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
 #define ERROR_LOCK_PATH  -100
+#define ERROR_MAX_MSGS   -101
 #define OPERATOR_EXIT     300
 
 enum vm_box {
-       NEW_FOLDER,
-       OLD_FOLDER,
-       WORK_FOLDER,
-       FAMILY_FOLDER,
-       FRIENDS_FOLDER,
-       GREETINGS_FOLDER
+       NEW_FOLDER =            0,
+       OLD_FOLDER =            1,
+       WORK_FOLDER =           2,
+       FAMILY_FOLDER =         3,
+       FRIENDS_FOLDER =        4,
+       GREETINGS_FOLDER =      -1
 };
 
 enum vm_option_flags {
@@ -791,7 +811,7 @@ struct baseio {
        unsigned char iobuf[BASEMAXINLINE];
 };
 
-/*! Structure for linked list of users 
+/*! Structure for linked list of users
  * Use ast_vm_user_destroy() to free one of these structures. */
 struct ast_vm_user {
        char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
@@ -803,6 +823,7 @@ struct ast_vm_user {
        char *emailbody;                 /*!< E-mail body */
        char pager[80];                  /*!< E-mail address to pager (no attachment) */
        char serveremail[80];            /*!< From: Mail address */
+       char fromstring[100];            /*!< From: Username */
        char language[MAX_LANGUAGE];     /*!< Config: Language setting */
        char zonetag[80];                /*!< Time zone */
        char locale[20];                 /*!< The locale (for presentation of date/time) */
@@ -811,7 +832,7 @@ struct ast_vm_user {
        char uniqueid[80];               /*!< Unique integer identifier */
        char exit[80];
        char attachfmt[20];              /*!< Attachment format */
-       unsigned int flags;              /*!< VM_ flags */      
+       unsigned int flags;              /*!< VM_ flags */
        int saydurationm;
        int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
        int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
@@ -909,7 +930,7 @@ static char odbc_table[80];
 #define STORE(a,b,c,d,e,f,g,h,i,j,k)
 #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));
-#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
+#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
 #define DELETE(a,b,c,d) (vm_delete(c))
 #define UPDATE_MSG_ID(a, b, c, d, e, f)
 #endif
@@ -963,7 +984,7 @@ static int maxdeletedmsg;
 static int silencethreshold = 128;
 static char serveremail[80];
 static char mailcmd[160];      /* Configurable mail cmd */
-static char externnotify[160]; 
+static char externnotify[160];
 static struct ast_smdi_interface *smdi_iface = NULL;
 static char vmfmts[80];
 static double volgain;
@@ -1034,6 +1055,8 @@ static char listen_control_restart_key[12];
 static char listen_control_stop_key[12];
 
 /* custom password sounds */
+static char vm_login[80] = "vm-login";
+static char vm_newuser[80] = "vm-newuser";
 static char vm_password[80] = "vm-password";
 static char vm_newpassword[80] = "vm-newpassword";
 static char vm_passchanged[80] = "vm-passchanged";
@@ -1086,7 +1109,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
                        char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
-                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id);
+                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
 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, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
@@ -1099,6 +1122,8 @@ static int write_password_to_file(const char *secretfn, const char *password);
 static const char *substitute_escapes(const char *value);
 static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
 static void notify_new_state(struct ast_vm_user *vmu);
+static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
+
 
 /*!
  * Place a message in the indicated folder
@@ -1290,11 +1315,11 @@ static void populate_defaults(struct ast_vm_user *vmu)
 }
 
 /*!
- * \brief Sets a a specific property value.
+ * \brief Sets a specific property value.
  * \param vmu The voicemail user object to work with.
  * \param var The name of the property to be set.
  * \param value The value to be set to the property.
- * 
+ *
  * The property name must be one of the understood properties. See the source for details.
  */
 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
@@ -1306,6 +1331,8 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
                ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
        } else if (!strcasecmp(var, "serveremail")) {
                ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
+       } else if (!strcasecmp(var, "fromstring")) {
+               ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
        } else if (!strcasecmp(var, "emailbody")) {
                ast_free(vmu->emailbody);
                vmu->emailbody = ast_strdup(substitute_escapes(value));
@@ -1342,25 +1369,25 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
                vmu->imapversion = imapversion;
 #endif
        } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
-               ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
+               ast_set2_flag(vmu, ast_true(value), VM_DELETE);
        } else if (!strcasecmp(var, "saycid")){
-               ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
+               ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
        } else if (!strcasecmp(var, "sendvoicemail")){
-               ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
+               ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
        } else if (!strcasecmp(var, "review")){
                ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
        } else if (!strcasecmp(var, "tempgreetwarn")){
-               ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
+               ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
        } else if (!strcasecmp(var, "messagewrap")){
-               ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);    
+               ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
        } else if (!strcasecmp(var, "operator")) {
-               ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
+               ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
        } else if (!strcasecmp(var, "envelope")){
-               ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
+               ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
        } else if (!strcasecmp(var, "moveheard")){
                ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
        } else if (!strcasecmp(var, "sayduration")){
-               ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
+               ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
        } else if (!strcasecmp(var, "saydurationm")){
                if (sscanf(value, "%30d", &x) == 1) {
                        vmu->saydurationm = x;
@@ -1368,9 +1395,9 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
                        ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
                }
        } else if (!strcasecmp(var, "forcename")){
-               ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
+               ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
        } else if (!strcasecmp(var, "forcegreetings")){
-               ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
+               ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
        } else if (!strcasecmp(var, "callback")) {
                ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
        } else if (!strcasecmp(var, "dialout")) {
@@ -1434,7 +1461,7 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
        }
 }
 
-static char *vm_check_password_shell(char *command, char *buf, size_t len) 
+static char *vm_check_password_shell(char *command, char *buf, size_t len)
 {
        int fds[2], pid = 0;
 
@@ -1472,7 +1499,7 @@ static char *vm_check_password_shell(char *command, char *buf, size_t len)
 
                        AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
 
-                       execv(arg.v[0], arg.v); 
+                       execv(arg.v[0], arg.v);
                        printf("FAILURE: %s", strerror(errno));
                        _exit(0);
                }
@@ -1518,11 +1545,11 @@ static int check_password(struct ast_vm_user *vmu, char *password)
        return 0;
 }
 
-/*! 
+/*!
  * \brief Performs a change of the voicemail passowrd in the realtime engine.
  * \param vmu The voicemail user to change the password for.
  * \param password The new value to be set to the password for this user.
- * 
+ *
  * This only works if there is a realtime engine configured.
  * This is called from the (top level) vm_change_password.
  *
@@ -1551,7 +1578,7 @@ static int change_password_realtime(struct ast_vm_user *vmu, const char *passwor
  * \brief Destructively Parse options and apply.
  */
 static void apply_options(struct ast_vm_user *vmu, const char *options)
-{      
+{
        char *stringp;
        char *s;
        char *var, *value;
@@ -1561,12 +1588,12 @@ static void apply_options(struct ast_vm_user *vmu, const char *options)
                if ((var = strsep(&value, "=")) && value) {
                        apply_option(vmu, var, value);
                }
-       }       
+       }
 }
 
 /*!
  * \brief Loads the options specific to a voicemail user.
- * 
+ *
  * This is called when a vm_user structure is being set up, such as from load_options.
  */
 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
@@ -1632,7 +1659,7 @@ static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *
  * \brief Determines if a DTMF key entered is valid.
  * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
  *
- * Tests the character entered against the set of valid DTMF characters. 
+ * Tests the character entered against the set of valid DTMF characters.
  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
  */
 static int is_valid_dtmf(const char *key)
@@ -1684,12 +1711,12 @@ static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const cha
                if (var) {
                        apply_options_full(retval, var);
                        ast_variables_destroy(var);
-               } else { 
-                       if (!ivm) 
+               } else {
+                       if (!ivm)
                                ast_free(retval);
                        retval = NULL;
-               }       
-       } 
+               }
+       }
        return retval;
 }
 
@@ -1698,7 +1725,7 @@ static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const cha
  * \param ivm
  * \param context
  * \param mailbox
- * 
+ *
  * \return The ast_vm_user structure for the user that was found.
  */
 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
@@ -1723,13 +1750,14 @@ static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *contex
        }
        if (cur) {
                /* Make a copy, so that on a reload, we have no race */
-               if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
+               if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
+                       ast_free(vmu->email);
+                       ast_free(vmu->emailbody);
+                       ast_free(vmu->emailsubject);
                        *vmu = *cur;
-                       if (!ivm) {
-                               vmu->email = ast_strdup(cur->email);
-                               vmu->emailbody = ast_strdup(cur->emailbody);
-                               vmu->emailsubject = ast_strdup(cur->emailsubject);
-                       }
+                       vmu->email = ast_strdup(cur->email);
+                       vmu->emailbody = ast_strdup(cur->emailbody);
+                       vmu->emailsubject = ast_strdup(cur->emailsubject);
                        ast_set2_flag(vmu, !ivm, VM_ALLOCED);
                        AST_LIST_NEXT(vmu, list) = NULL;
                }
@@ -1776,7 +1804,7 @@ static inline int valid_config(const struct ast_config *cfg)
        return cfg && cfg != CONFIG_STATUS_FILEINVALID;
 }
 
-/*! 
+/*!
  * \brief The handler for the change password option.
  * \param vmu The voicemail user to work with.
  * \param newpassword The new password (that has been gathered from the appropriate prompting).
@@ -1904,15 +1932,15 @@ static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
        }
 }
 
-/*! 
+/*!
  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
  * \param len The length of the path string that was written out.
  * \param context
- * \param ext 
- * \param folder 
- * 
- * The path is constructed as 
+ * \param ext
+ * \param folder
+ *
+ * The path is constructed as
  *     VM_SPOOL_DIRcontext/ext/folder
  *
  * \return zero on success, -1 on error.
@@ -1922,14 +1950,14 @@ static int make_dir(char *dest, int len, const char *context, const char *ext, c
        return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
 }
 
-/*! 
+/*!
  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
  * \param len The length of the path string that was written out.
- * \param dir 
- * \param num 
- * 
- * The path is constructed as 
+ * \param dir
+ * \param num
+ *
+ * The path is constructed as
  *     VM_SPOOL_DIRcontext/ext/folder
  *
  * \return zero on success, -1 on error.
@@ -1960,7 +1988,7 @@ static FILE *vm_mkftemp(char *template)
  * \param len     Length of dest.
  * \param context String. Ignored if is null or empty string.
  * \param ext     String. Ignored if is null or empty string.
- * \param folder  String. Ignored if is null or empty string. 
+ * \param folder  String. Ignored if is null or empty string.
  * \return -1 on failure, 0 on success.
  */
 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
@@ -2007,17 +2035,18 @@ static int get_folder_by_name(const char *name)
 
 static void free_user(struct ast_vm_user *vmu)
 {
-       if (ast_test_flag(vmu, VM_ALLOCED)) {
-
-               ast_free(vmu->email);
-               vmu->email = NULL;
-
-               ast_free(vmu->emailbody);
-               vmu->emailbody = NULL;
+       if (!vmu) {
+               return;
+       }
 
-               ast_free(vmu->emailsubject);
-               vmu->emailsubject = NULL;
+       ast_free(vmu->email);
+       vmu->email = NULL;
+       ast_free(vmu->emailbody);
+       vmu->emailbody = NULL;
+       ast_free(vmu->emailsubject);
+       vmu->emailsubject = NULL;
 
+       if (ast_test_flag(vmu, VM_ALLOCED)) {
                ast_free(vmu);
        }
 }
@@ -2170,9 +2199,12 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
 {
        struct vm_state *vms_p;
        char *file, *filename;
+       char dest[PATH_MAX];
        char *attachment;
        int i;
        BODY *body;
+       int ret = 0;
+       int curr_mbox;
 
        /* This function is only used for retrieval of IMAP greetings
         * regular messages are not retrieved this way, nor are greetings
@@ -2190,9 +2222,9 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
        }
 
        /* check if someone is accessing this box right now... */
-       if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && 
+       if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
                !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
-               /* Unlike when retrieving a message, it is reasonable not to be able to find a 
+               /* Unlike when retrieving a message, it is reasonable not to be able to find a
                * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
                * that's all we need to do.
                */
@@ -2206,6 +2238,10 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
        *vms_p->introfn = '\0';
 
        ast_mutex_lock(&vms_p->lock);
+
+       /* get the current mailbox so that we can point the mailstream back to it later */
+       curr_mbox = get_folder_by_name(vms_p->curbox);
+
        if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
                ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
                ast_mutex_unlock(&vms_p->lock);
@@ -2220,21 +2256,29 @@ static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_
                        attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
                } else {
                        ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
-                       ast_mutex_unlock(&vms_p->lock);
-                       return -1;
+                       ret = -1;
+                       break;
                }
                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;
+                       create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
                        save_body(body, vms_p, "2", attachment, 0);
-                       ast_mutex_unlock(&vms_p->lock);
-                       return 0;
+                       ret = 0;
+                       break;
                }
        }
-       ast_mutex_unlock(&vms_p->lock);
 
-       return -1;
+       if (curr_mbox != -1) {
+               /* restore previous mbox stream */
+               if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
+                       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+                       ret = -1;
+               }
+       }
+       ast_mutex_unlock(&vms_p->lock);
+       return ret;
 }
 
 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
@@ -2248,12 +2292,13 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
        FILE *text_file_ptr;
        int res = 0;
        struct ast_vm_user *vmu;
+       int curr_mbox;
 
        if (!(vmu = find_user(NULL, context, mailbox))) {
                ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
                return -1;
        }
-       
+
        if (msgnum < 0) {
                if (imapgreetings) {
                        res = imap_retrieve_greeting(dir, msgnum, vmu);
@@ -2280,6 +2325,19 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
                goto exit;
        }
 
+       /* Ensure we have the correct mailbox open and have a valid mailstream for it */
+       curr_mbox = get_folder_by_name(vms->curbox);
+       if (curr_mbox < 0) {
+               ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
+               curr_mbox = 0;
+       }
+       init_mailstream(vms, curr_mbox);
+       if (!vms->mailstream) {
+               ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
+               res = -1;
+               goto exit;
+       }
+
        make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
        snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
 
@@ -2319,7 +2377,7 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
                res = -1;
                goto exit;
        }
-       
+
        /* Find the format of the attached file */
 
        strsep(&attachedfilefmt, ".");
@@ -2328,7 +2386,7 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
                res = -1;
                goto exit;
        }
-       
+
        save_body(body, vms, "2", attachedfilefmt, 0);
        if (save_body(body, vms, "3", attachedfilefmt, 1)) {
                *vms->introfn = '\0';
@@ -2338,7 +2396,8 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
        snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
 
        if (!(text_file_ptr = fopen(text_file, "w"))) {
-               ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+               ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+               goto exit;
        }
 
        fprintf(text_file_ptr, "%s\n", "[message]");
@@ -2417,7 +2476,7 @@ static int __messagecount(const char *context, const char *mailbox, const char *
        int ret = 0;
        int fold = folder_int(folder);
        int urgent = 0;
-       
+
        /* If URGENT, then look at INBOX */
        if (fold == 11) {
                fold = NEW_FOLDER;
@@ -2428,14 +2487,17 @@ static int __messagecount(const char *context, const char *mailbox, const char *
                return 0;
 
        /* We have to get the user before we can open the stream! */
+       memset(&vmus, 0, sizeof(vmus));
        vmu = find_user(&vmus, context, mailbox);
        if (!vmu) {
                ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
+               free_user(vmu);
                return -1;
        } else {
                /* No IMAP account available */
                if (vmu->imapuser[0] == '\0') {
                        ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
+                       free_user(vmu);
                        return -1;
                }
        }
@@ -2446,7 +2508,6 @@ static int __messagecount(const char *context, const char *mailbox, const char *
                free_user(vmu);
                return -1;
        }
-       ast_assert(msgnum < vms->msg_array_max);
 
        /* check if someone is accessing this box right now... */
        vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
@@ -2456,9 +2517,11 @@ static int __messagecount(const char *context, const char *mailbox, const char *
        if (vms_p) {
                ast_debug(3, "Returning before search - user is logged in\n");
                if (fold == 0) { /* INBOX */
+                       free_user(vmu);
                        return urgent ? vms_p->urgentmessages : vms_p->newmessages;
                }
                if (fold == 1) { /* Old messages */
+                       free_user(vmu);
                        return vms_p->oldmessages;
                }
        }
@@ -2475,6 +2538,7 @@ static int __messagecount(const char *context, const char *mailbox, const char *
        ret = init_mailstream(vms_p, fold);
        if (!vms_p->mailstream) {
                ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
+               free_user(vmu);
                return -1;
        }
        if (ret == 0) {
@@ -2518,6 +2582,7 @@ static int __messagecount(const char *context, const char *mailbox, const char *
                /*Freeing the searchpgm also frees the searchhdr*/
                mail_free_searchpgm(&pgm);
                ast_mutex_unlock(&vms_p->lock);
+               free_user(vmu);
                vms_p->updated = 0;
                return vms_p->vmArrayIndex;
        } else {
@@ -2525,6 +2590,7 @@ static int __messagecount(const char *context, const char *mailbox, const char *
                mail_ping(vms_p->mailstream);
                ast_mutex_unlock(&vms_p->lock);
        }
+       free_user(vmu);
        return 0;
 }
 
@@ -2558,7 +2624,7 @@ static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, str
  * \brief Gets the number of messages that exist in a mailbox folder.
  * \param mailbox_id
  * \param folder
- * 
+ *
  * This method is used when IMAP backend is used.
  * \return The number of messages in this mailbox folder (zero or more).
  */
@@ -2671,8 +2737,8 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
        }
 
        make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
-               S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
-               S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
+               chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
+               chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
                fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
        /* read mail file to memory */
        len = ftell(p);
@@ -2684,9 +2750,9 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
                        *(vmu->email) = '\0';
                return -1;
        }
-       if (fread(buf, len, 1, p) < len) {
+       if (fread(buf, 1, len, p) != len) {
                if (ferror(p)) {
-                       ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
+                       ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
                        return -1;
                }
        }
@@ -2724,7 +2790,7 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
- * 
+ *
  * This method is used when IMAP backend is used.
  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
  *
@@ -2762,7 +2828,7 @@ static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsg
                                        return -1;
                                else {
                                        if (newmsgs)
-                                               *newmsgs += tmpnew; 
+                                               *newmsgs += tmpnew;
                                        if (oldmsgs)
                                                *oldmsgs += tmpold;
                                        if (urgentmsgs)
@@ -2806,7 +2872,7 @@ static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsg
        return 0;
 }
 
-/** 
+/**
  * \brief Determines if the given folder has messages.
  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
  * \param folder the folder to look in
@@ -2884,7 +2950,7 @@ static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int
 {
        char tmp[256], *t = tmp;
        size_t left = sizeof(tmp);
-       
+
        if (box == OLD_FOLDER) {
                ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
        } else {
@@ -2980,6 +3046,10 @@ static int init_mailstream(struct vm_state *vms, int box)
        ast_mutex_lock(&vms->lock);
        ast_mutex_lock(&mail_open_lock);
        vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+       /* Create the folder if it dosn't exist */
+       if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
+               mail_create(vms->mailstream, tmp);
+       }
        ast_mutex_unlock(&mail_open_lock);
        ast_mutex_unlock(&vms->lock);
        if (vms->mailstream == NIL) {
@@ -3074,7 +3144,15 @@ static void write_file(char *filename, char *buffer, unsigned long len)
 {
        FILE *output;
 
-       output = fopen (filename, "w");
+       if (!filename || !buffer) {
+               return;
+       }
+
+       if (!(output = fopen(filename, "w"))) {
+               ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
+               return;
+       }
+
        if (fwrite(buffer, len, 1, output) != 1) {
                if (ferror(output)) {
                        ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
@@ -3209,18 +3287,31 @@ void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
 
 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
 {
-       ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
-       if (status->flags & SA_MESSAGES)
-               ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
-       if (status->flags & SA_RECENT)
-               ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
-       if (status->flags & SA_UNSEEN)
-               ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
-       if (status->flags & SA_UIDVALIDITY)
-               ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
-       if (status->flags & SA_UIDNEXT)
-               ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
-       ast_log(AST_LOG_NOTICE, "\n");
+       struct ast_str *str;
+
+       if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
+           return;
+       }
+
+       ast_str_append(&str, 0, " Mailbox %s", mailbox);
+       if (status->flags & SA_MESSAGES) {
+               ast_str_append(&str, 0, ", %lu messages", status->messages);
+       }
+       if (status->flags & SA_RECENT) {
+               ast_str_append(&str, 0, ", %lu recent", status->recent);
+       }
+       if (status->flags & SA_UNSEEN) {
+               ast_str_append(&str, 0, ", %lu unseen", status->unseen);
+       }
+       if (status->flags & SA_UIDVALIDITY) {
+               ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
+       }
+       if (status->flags & SA_UIDNEXT) {
+               ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
+       }
+       ast_log(LOG_DEBUG, "%s\n", ast_str_buffer(str));
+
+       ast_free(str);
 }
 
 
@@ -3408,8 +3499,9 @@ static struct vm_state *get_vm_state_by_imapuser(const char *user, int interacti
        if (interactive) {
                struct vm_state *vms;
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
-               vms = pthread_getspecific(ts_vmstate.key);
-               return vms;
+               if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
+                       return vms;
+               }
        }
 
        AST_LIST_LOCK(&vmstates);
@@ -3421,10 +3513,6 @@ static struct vm_state *get_vm_state_by_imapuser(const char *user, int interacti
                if (vlist->vms->imapversion != imapversion) {
                        continue;
                }
-               if (!vlist->vms->imapuser) {
-                       ast_debug(3, "error: imapuser is NULL for %s\n", user);
-                       continue;
-               }
 
                if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
                        AST_LIST_UNLOCK(&vmstates);
@@ -3447,8 +3535,10 @@ static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char
        if (interactive) {
                struct vm_state *vms;
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
-               vms = pthread_getspecific(ts_vmstate.key);
-               return vms;
+               if ((vms = pthread_getspecific(ts_vmstate.key)) &&
+                   !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
+                       return vms;
+               }
        }
 
        AST_LIST_LOCK(&vmstates);
@@ -3460,10 +3550,6 @@ static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char
                if (vlist->vms->imapversion != imapversion) {
                        continue;
                }
-               if (!vlist->vms->username || !vlist->vms->context) {
-                       ast_debug(3, "error: username is NULL for %s\n", mailbox);
-                       continue;
-               }
 
                ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
 
@@ -3551,7 +3637,7 @@ static void vmstate_delete(struct vm_state *vms)
        }
        AST_LIST_TRAVERSE_SAFE_END
        AST_LIST_UNLOCK(&vmstates);
-       
+
        if (vc) {
                ast_mutex_destroy(&vc->vms->lock);
                ast_free(vc->vms->msgArray);
@@ -3571,7 +3657,7 @@ static void set_update(MAILSTREAM * stream)
        char buf[1024] = "";
 
        if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
-               if (user && option_debug > 2)
+               if (user && DEBUG_ATLEAST(3))
                        ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
                return;
        }
@@ -3598,8 +3684,8 @@ static int save_body(BODY *body, struct vm_state *vms, char *section, char *form
        char *body_content;
        char *body_decoded;
        char *fn = is_intro ? vms->introfn : vms->fn;
-       unsigned long len;
-       unsigned long newlen;
+       unsigned long len = 0;
+       unsigned long newlen = 0;
        char filename[256];
 
        if (!body || body == NIL)
@@ -3608,12 +3694,18 @@ static int save_body(BODY *body, struct vm_state *vms, char *section, char *form
        ast_mutex_lock(&vms->lock);
        body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
        ast_mutex_unlock(&vms->lock);
-       if (body_content != NIL) {
+       if (len > MAX_MAIL_BODY_CONTENT_SIZE) {
+               ast_log(AST_LOG_ERROR,
+                       "Msgno %ld, section %s. The body's content size %ld is huge (max %ld). User:%s, mailbox %s\n",
+                       vms->msgArray[vms->curmsg], section, len, MAX_MAIL_BODY_CONTENT_SIZE, vms->imapuser, vms->username);
+               return -1;
+       }
+       if (body_content != NIL && len) {
                snprintf(filename, sizeof(filename), "%s.%s", fn, format);
                /* ast_debug(1, body_content); */
                body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
                /* If the body of the file is empty, return an error */
-               if (!newlen) {
+               if (!newlen || !body_decoded) {
                        return -1;
                }
                write_file(filename, (char *) body_decoded, newlen);
@@ -3624,8 +3716,8 @@ static int save_body(BODY *body, struct vm_state *vms, char *section, char *form
        return 0;
 }
 
-/*! 
- * \brief Get delimiter via mm_list callback 
+/*!
+ * \brief Get delimiter via mm_list callback
  * \param vms          The voicemail state object
  * \param stream
  *
@@ -3638,8 +3730,8 @@ static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
        mail_list(stream, tmp, "*");
 }
 
-/*! 
- * \brief Check Quota for user 
+/*!
+ * \brief Check Quota for user
  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
  * \param mailbox the mailbox to check the quota for.
  *
@@ -3698,12 +3790,12 @@ static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
        SQLHSTMT stmt;
 
        res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+       if (!SQL_SUCCEEDED(res)) {
                ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
                return NULL;
        }
        res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+       if (!SQL_SUCCEEDED(res)) {
                ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                return NULL;
@@ -3745,14 +3837,14 @@ static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
  * \brief Retrieves a file from an ODBC data store.
  * \param dir the path to the file to be retrieved.
  * \param msgnum the message number, such as within a mailbox folder.
- * 
+ *
  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
  * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
  *
  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
  * The output is the message information file with the name msgnum and the extension .txt
  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
- * 
+ *
  * \return 0 on success, -1 on error.
  */
 static int retrieve_file(char *dir, int msgnum)
@@ -3765,7 +3857,7 @@ static int retrieve_file(char *dir, int msgnum)
        SQLSMALLINT colcount = 0;
        SQLHSTMT stmt;
        char sql[PATH_MAX];
-       char fmt[80]="";
+       char fmt[80] = "";
        char *c;
        char coltitle[256];
        SQLSMALLINT collen;
@@ -3781,144 +3873,139 @@ static int retrieve_file(char *dir, int msgnum)
        char msgnums[80];
        char *argv[] = { dir, msgnums };
        struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
-
        struct odbc_obj *obj;
+
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               ast_copy_string(fmt, vmfmts, sizeof(fmt));
-               c = strchr(fmt, '|');
-               if (c)
-                       *c = '\0';
-               if (!strcasecmp(fmt, "wav49"))
-                       strcpy(fmt, "WAV");
-               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
-               if (msgnum > -1)
-                       make_file(fn, sizeof(fn), dir, msgnum);
-               else
-                       ast_copy_string(fn, dir, sizeof(fn));
+       if (!obj) {
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+               return -1;
+       }
 
-               /* Create the information file */
-               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-               
-               if (!(f = fopen(full_fn, "w+"))) {
-                       ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
-                       goto yuck;
-               }
-               
-               snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
-               snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if (res == SQL_NO_DATA) {
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+       ast_copy_string(fmt, vmfmts, sizeof(fmt));
+       c = strchr(fmt, '|');
+       if (c)
+               *c = '\0';
+       if (!strcasecmp(fmt, "wav49"))
+               strcpy(fmt, "WAV");
+
+       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+       if (msgnum > -1)
+               make_file(fn, sizeof(fn), dir, msgnum);
+       else
+               ast_copy_string(fn, dir, sizeof(fn));
+
+       /* Create the information file */
+       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+
+       if (!(f = fopen(full_fn, "w+"))) {
+               ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
+               goto bail;
+       }
+
+       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
+
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt) {
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               goto bail;
+       }
+
+       res = SQLFetch(stmt);
+       if (!SQL_SUCCEEDED(res)) {
+               if (res != SQL_NO_DATA) {
                        ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
                }
-               fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
-               if (fd < 0) {
-                       ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLNumResultCols(stmt, &colcount);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
-                       ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               if (f) 
-                       fprintf(f, "[message]\n");
-               for (x = 0; x < colcount; x++) {
-                       rowdata[0] = '\0';
-                       colsize = 0;
-                       collen = sizeof(coltitle);
-                       res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen, 
-                                               &datatype, &colsize, &decimaldigits, &nullable);
-                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                               ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
-                               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                               ast_odbc_release_obj(obj);
-                               goto yuck;
-                       }
-                       if (!strcasecmp(coltitle, "recording")) {
-                               off_t offset;
-                               res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
-                               fdlen = colsize2;
-                               if (fd > -1) {
-                                       char tmp[1]="";
-                                       lseek(fd, fdlen - 1, SEEK_SET);
-                                       if (write(fd, tmp, 1) != 1) {
-                                               close(fd);
-                                               fd = -1;
-                                               continue;
-                                       }
-                                       /* Read out in small chunks */
-                                       for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
-                                               if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
-                                                       ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
-                                                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-                                                       ast_odbc_release_obj(obj);
-                                                       goto yuck;
-                                               } else {
-                                                       res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
-                                                       munmap(fdm, CHUNKSIZE);
-                                                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                                               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                                                               unlink(full_fn);
-                                                               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-                                                               ast_odbc_release_obj(obj);
-                                                               goto yuck;
-                                                       }
-                                               }
+               goto bail_with_handle;
+       }
+
+       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
+       if (fd < 0) {
+               ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
+               goto bail_with_handle;
+       }
+
+       res = SQLNumResultCols(stmt, &colcount);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+
+       fprintf(f, "[message]\n");
+       for (x = 0; x < colcount; x++) {
+               rowdata[0] = '\0';
+               colsize = 0;
+               collen = sizeof(coltitle);
+               res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
+                                                       &datatype, &colsize, &decimaldigits, &nullable);
+               if (!SQL_SUCCEEDED(res)) {
+                       ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+                       goto bail_with_handle;
+               }
+               if (!strcasecmp(coltitle, "recording")) {
+                       off_t offset;
+                       res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
+                       fdlen = colsize2;
+                       if (fd > -1) {
+                               char tmp[1] = "";
+                               lseek(fd, fdlen - 1, SEEK_SET);
+                               if (write(fd, tmp, 1) != 1) {
+                                       close(fd);
+                                       fd = -1;
+                                       continue;
+                               }
+                               /* Read out in small chunks */
+                               for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
+                                       if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
+                                               ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
+                                               goto bail_with_handle;
                                        }
-                                       if (truncate(full_fn, fdlen) < 0) {
-                                               ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
+                                       res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
+                                       munmap(fdm, CHUNKSIZE);
+                                       if (!SQL_SUCCEEDED(res)) {
+                                               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                                               unlink(full_fn);
+                                               goto bail_with_handle;
                                        }
                                }
-                       } else {
-                               res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-                               if ((res == SQL_NULL_DATA) && (!strcasecmp(coltitle, "msg_id"))) {
-                                       char msg_id[MSG_ID_LEN];
-                                       generate_msg_id(msg_id);
-                                       snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
-                                       odbc_update_msg_id(dir, msgnum, msg_id);
-                               } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
-                                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                                       ast_odbc_release_obj(obj);
-                                       goto yuck;
+                               if (truncate(full_fn, fdlen) < 0) {
+                                       ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
                                }
-                               if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
-                                       fprintf(f, "%s=%s\n", coltitle, rowdata);
+                       }
+               } else {
+                       res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+                       if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
+                               char msg_id[MSG_ID_LEN];
+                               generate_msg_id(msg_id);
+                               snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
+                               odbc_update_msg_id(dir, msgnum, msg_id);
+                       } else if (!SQL_SUCCEEDED(res)) {
+                               ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
+                               goto bail_with_handle;
+                       }
+                       if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
+                               fprintf(f, "%s=%s\n", coltitle, rowdata);
                        }
                }
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
+       }
+
+bail_with_handle:
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
        if (f)
                fclose(f);
        if (fd > -1)
                close(fd);
+
+       ast_odbc_release_obj(obj);
+
        return x - 1;
 }
 
 /*!
  * \brief Determines the highest message number in use for a given user and mailbox folder.
- * \param vmu 
+ * \param vmu
  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
  *
  * This method is used when mailboxes are stored in an ODBC back end.
@@ -3929,58 +4016,61 @@ yuck:
  */
 static int last_message_index(struct ast_vm_user *vmu, char *dir)
 {
-       int x = 0;
+       int x = -1;
        int res;
        SQLHSTMT stmt;
        char sql[PATH_MAX];
        char rowdata[20];
        char *argv[] = { dir };
        struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
-
        struct odbc_obj *obj;
+
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
+       if (!obj) {
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+               return -1;
+       }
 
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       if (res == SQL_NO_DATA) {
-                               ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
-                       } else {
-                               ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       }
+       snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
 
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt) {
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               goto bail;
+       }
+
+       res = SQLFetch(stmt);
+       if (!SQL_SUCCEEDED(res)) {
+               if (res == SQL_NO_DATA) {
+                       ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
+               } else {
+                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
                }
-               if (sscanf(rowdata, "%30d", &x) != 1)
-                       ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-               return x;
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
-       return x - 1;
+               goto bail_with_handle;
+       }
+
+       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+
+       if (sscanf(rowdata, "%30d", &x) != 1) {
+               ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
+       }
+
+bail_with_handle:
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
+       ast_odbc_release_obj(obj);
+
+       return x;
 }
 
 /*!
  * \brief Determines if the specified message exists.
- * \param dir the folder the mailbox folder to look for messages. 
+ * \param dir the folder the mailbox folder to look for messages.
  * \param msgnum the message index to query for.
  *
  * This method is used when mailboxes are stored in an ODBC back end.
@@ -3997,39 +4087,43 @@ static int message_exists(char *dir, int msgnum)
        char msgnums[20];
        char *argv[] = { dir, msgnums };
        struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
-
        struct odbc_obj *obj;
+
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               if (sscanf(rowdata, "%30d", &x) != 1)
-                       ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
+               return 0;
+       }
+
+       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt) {
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               goto bail;
+       }
+
+       res = SQLFetch(stmt);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+
+       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+
+       if (sscanf(rowdata, "%30d", &x) != 1) {
+               ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
+       }
+
+bail_with_handle:
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
+       ast_odbc_release_obj(obj);
        return x;
 }
 
@@ -4044,48 +4138,50 @@ yuck:
  */
 static int count_messages(struct ast_vm_user *vmu, char *dir)
 {
-       int x = 0;
+       int x = -1;
        int res;
        SQLHSTMT stmt;
        char sql[PATH_MAX];
        char rowdata[20];
        char *argv[] = { dir };
        struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
-
        struct odbc_obj *obj;
+
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               if (sscanf(rowdata, "%30d", &x) != 1)
-                       ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-               return x;
-       } else
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
-       return x - 1;
+               return -1;
+       }
 
+       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt) {
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               goto bail;
+       }
+
+       res = SQLFetch(stmt);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+
+       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+
+       if (sscanf(rowdata, "%30d", &x) != 1) {
+               ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
+       }
+
+bail_with_handle:
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
+       ast_odbc_release_obj(obj);
+       return x;
 }
 
 /*!
@@ -4095,7 +4191,7 @@ yuck:
  *
  * This method is used when mailboxes are stored in an ODBC back end.
  * The specified message is directly deleted from the database 'voicemessages' table.
- * 
+ *
  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
  */
 static void delete_file(const char *sdir, int smsg)
@@ -4107,21 +4203,25 @@ static void delete_file(const char *sdir, int smsg)
        struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
        struct odbc_obj *obj;
 
-       argv[0] = ast_strdupa(sdir);
-
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-               snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt)
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-               else
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-       return; 
+               return;
+       }
+
+       argv[0] = ast_strdupa(sdir);
+
+       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt) {
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+       } else {
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+       }
+       ast_odbc_release_obj(obj);
+
+       return;
 }
 
 /*!
@@ -4149,19 +4249,22 @@ static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailbox
        generate_msg_id(msg_id);
        delete_file(ddir, dmsg);
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
-               snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, msg_id, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt)
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
-               else
-                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-       return; 
+               return;
+       }
+
+       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, msg_id, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt)
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+       else
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+       ast_odbc_release_obj(obj);
+
+       return;
 }
 
 struct insert_data {
@@ -4190,9 +4293,8 @@ static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
        SQLHSTMT stmt;
 
        res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+       if (!SQL_SUCCEEDED(res)) {
                ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                return NULL;
        }
 
@@ -4212,7 +4314,7 @@ static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
                SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
        }
        res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+       if (!SQL_SUCCEEDED(res)) {
                ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                return NULL;
@@ -4229,7 +4331,7 @@ static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
  * \param msgnum the message index for the message to be stored.
  *
  * This method is used when mailboxes are stored in an ODBC back end.
- * The message sound file and information file is looked up on the file system. 
+ * The message sound file and information file is looked up on the file system.
  * A SQL query is invoked to store the message into the (MySQL) database.
  *
  * \return the zero on success -1 on error.
@@ -4254,7 +4356,9 @@ static int store_file(const char *dir, const char *mailboxuser, const char *mail
        struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
 
        delete_file(dir, msgnum);
-       if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
+
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
                return -1;
        }
@@ -4317,25 +4421,25 @@ static int store_file(const char *dir, const char *mailboxuser, const char *mail
                        ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
                        res = -1;
                        break;
-               } 
+               }
                idata.data = fdm;
                idata.datalen = idata.indlen = fdlen;
 
-               if (!ast_strlen_zero(idata.category)) 
-                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table); 
+               if (!ast_strlen_zero(idata.category))
+                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
                else
                        snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
 
                if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
                } else {
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
                        res = -1;
                }
        } while (0);
-       if (obj) {
-               ast_odbc_release_obj(obj);
-       }
+
+       ast_odbc_release_obj(obj);
+
        if (valid_config(cfg))
                ast_config_destroy(cfg);
        if (fdm != MAP_FAILED)
@@ -4369,20 +4473,23 @@ static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxco
        struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
 
        delete_file(ddir, dmsg);
+
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
-               snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt)
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-               else
-                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-       return; 
+               return;
+       }
+
+       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt)
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+       else
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+       ast_odbc_release_obj(obj);
+       return;
 }
 
 /*!
@@ -4392,7 +4499,7 @@ static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxco
  *
  * Removes the message content file and the information file.
  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
- * Typical use is to clean up after a RETRIEVE operation. 
+ * Typical use is to clean up after a RETRIEVE operation.
  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
  * \return zero on success, -1 on error.
  */
@@ -4401,13 +4508,13 @@ static int 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));
-       ast_filedelete(fn, NULL);       
+       ast_filedelete(fn, NULL);
        snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
        unlink(full_fn);
        return 0;
@@ -4442,7 +4549,7 @@ static int count_messages(struct ast_vm_user *vmu, char *dir)
                closedir(vmdir);
        }
        ast_unlock_path(dir);
-       
+
        return vmcount;
 }
 
@@ -4466,9 +4573,9 @@ static void rename_file(char *sfn, char *dfn)
        rename(stxt, dtxt);
 }
 
-/*! 
+/*!
  * \brief Determines the highest message number in use for a given user and mailbox folder.
- * \param vmu 
+ * \param vmu
  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
  *
  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
@@ -4532,55 +4639,62 @@ static int copy(char *infile, char *outfile)
 {
        int ifd;
        int ofd;
-       int res;
+       int res = -1;
        int len;
        char buf[4096];
 
 #ifdef HARDLINK_WHEN_POSSIBLE
        /* Hard link if possible; saves disk space & is faster */
-       if (link(infile, outfile)) {
+       if (!link(infile, outfile)) {
+               return 0;
+       }
 #endif
-               if ((ifd = open(infile, O_RDONLY)) < 0) {
-                       ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
-                       return -1;
+
+       if ((ifd = open(infile, O_RDONLY)) < 0) {
+               ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
+               return -1;
+       }
+
+       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
+               ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
+               close(ifd);
+               return -1;
+       }
+
+       for (;;) {
+               int wrlen;
+
+               len = read(ifd, buf, sizeof(buf));
+               if (!len) {
+                       res = 0;
+                       break;
                }
-               if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
-                       ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
-                       close(ifd);
-                       return -1;
+
+               if (len < 0) {
+                       ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+                       break;
+               }
+
+               wrlen = write(ofd, buf, len);
+               if (errno == ENOMEM || errno == ENOSPC || wrlen != len) {
+                       ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, wrlen, len, strerror(errno));
+                       break;
                }
-               do {
-                       len = read(ifd, buf, sizeof(buf));
-                       if (len < 0) {
-                               ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
-                               close(ifd);
-                               close(ofd);
-                               unlink(outfile);
-                       } else if (len) {
-                               res = write(ofd, buf, len);
-                               if (errno == ENOMEM || errno == ENOSPC || res != len) {
-                                       ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
-                                       close(ifd);
-                                       close(ofd);
-                                       unlink(outfile);
-                               }
-                       }
-               } while (len);
-               close(ifd);
-               close(ofd);
-               return 0;
-#ifdef HARDLINK_WHEN_POSSIBLE
-       } else {
-               /* Hard link succeeded */
-               return 0;
        }
-#endif
+
+       close(ifd);
+       close(ofd);
+       if (res) {
+               unlink(outfile);
+       }
+
+       return res;
 }
 
 /*!
  * \brief Copies a voicemail information (envelope) file.
  * \param frompath
- * \param topath 
+ * \param topath
  *
  * Every voicemail has the data (.wav) file, and the information file.
  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
@@ -4629,9 +4743,9 @@ static void copy_plain_file(char *frompath, char *topath)
 }
 #endif
 
-/*! 
+/*!
  * \brief Removes the voicemail sound and information file.
- * \param file The path to the sound file. This will be the the folder and message index, without the extension.
+ * \param file The path to the sound file. This will be the folder and message index, without the extension.
  *
  * This is used by the DELETE macro when voicemails are stored on the file system.
  *
@@ -4665,12 +4779,12 @@ static int inbuf(struct baseio *bio, FILE *fi)
        if (bio->ateof)
                return 0;
 
-       if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
-               if (ferror(fi))
-                       return -1;
-
+       if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) != BASEMAXINLINE) {
                bio->ateof = 1;
-               return 0;
+               if (l == 0) {
+                       /* Assume EOF */
+                       return 0;
+               }
        }
 
        bio->iolen = l;
@@ -4775,7 +4889,7 @@ static int base_encode(char *filename, FILE *so)
        }
 
        fclose(fi);
-       
+
        if (fputs(ENDL, so) == EOF) {
                return 0;
        }
@@ -4842,7 +4956,7 @@ static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu
  * \param from The string to work with.
  * \param buf The buffer into which to write the modified quoted string.
  * \param maxlen Always zero, but see \see ast_str
- * 
+ *
  * \return The destination string with quotes wrapped on it (the to field).
  */
 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
@@ -4956,13 +5070,13 @@ static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, con
  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
  * \param vmu The voicemail user who is sending the voicemail.
  * \param msgnum The message index in the mailbox folder.
- * \param context 
+ * \param context
  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
  * \param fromfolder
  * \param cidnum The caller ID number.
  * \param cidname The caller ID name.
  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
- * \param attach2 
+ * \param attach2
  * \param format The message sound file format. i.e. .wav
  * \param duration The time of the message content, in seconds.
  * \param attach_user_voicemail if 1, the sound file is attached to the email.
@@ -5001,7 +5115,7 @@ static void make_email_file(FILE *p,
        struct ast_tm tm;
        char enc_cidnum[256] = "", enc_cidname[256] = "";
        struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
-       char *greeting_attachment; 
+       char *greeting_attachment;
        char filename[256];
        int first_line;
        char *emailsbuf;
@@ -5039,12 +5153,13 @@ static void make_email_file(FILE *p,
        /* Set date format for voicemail mail */
        ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
 
-       if (!ast_strlen_zero(fromstring)) {
+       if (!ast_strlen_zero(fromstring) || !ast_strlen_zero(vmu->fromstring)) {
                struct ast_channel *ast;
+               char *e_fromstring = !ast_strlen_zero(vmu->fromstring) ? vmu->fromstring : fromstring;
                if ((ast = ast_dummy_channel_alloc())) {
                        char *ptr;
                        prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
-                       ast_str_substitute_variables(&str1, 0, ast, fromstring);
+                       ast_str_substitute_variables(&str1, 0, ast, e_fromstring);
 
                        if (check_mime(ast_str_buffer(str1))) {
                                first_line = 1;
@@ -5089,7 +5204,9 @@ static void make_email_file(FILE *p,
                first_line = 0;
        }
 
-       if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
+       if (msgnum <= -1) {
+               fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
+       } else if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
                char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
                struct ast_channel *ast;
                if ((ast = ast_dummy_channel_alloc())) {
@@ -5142,9 +5259,8 @@ static void make_email_file(FILE *p,
                fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
 #endif
                /* flag added for Urgent */
-               fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
+               fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
                fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
-               fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan ? ast_channel_name(chan) : "");
                fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
                fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
                fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
@@ -5175,7 +5291,11 @@ static void make_email_file(FILE *p,
                fprintf(p, "--%s" ENDL, bound);
        }
        fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
-       if (emailbody || vmu->emailbody) {
+       if (msgnum <= -1) {
+               fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
+                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
+                               greeting_attachment, date);
+       } else if (emailbody || vmu->emailbody) {
                char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
                struct ast_channel *ast;
                if ((ast = ast_dummy_channel_alloc())) {
@@ -5201,7 +5321,7 @@ static void make_email_file(FILE *p,
                } else {
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
                }
-       } else if (msgnum > -1) {
+       } else {
                if (strcmp(vmu->mailbox, mailbox)) {
                        /* Forwarded type */
                        struct ast_config *msg_cfg;
@@ -5246,9 +5366,6 @@ plain_message:
                                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 (imap || attach_user_voicemail) {
@@ -5271,55 +5388,95 @@ plain_message:
 
 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
 {
-       char tmpdir[256], newtmp[256];
-       char fname[256];
-       char tmpcmd[256];
-       int tmpfd = -1;
-       int soxstatus = 0;
+       char fname[PATH_MAX] = "";
+       char sox_gain_tmpdir[PATH_MAX];
+       char *file_to_delete = NULL, *dir_to_delete = NULL;
+       int res;
 
        /* Eww. We want formats to tell us their own MIME type */
-       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+       char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+
+       /* This 'while' loop will only execute once. We use it so that we can 'break' */
+       while (vmu->volgain < -.001 || vmu->volgain > .001) {
+               char tmpdir[PATH_MAX];
 
-       if (vmu->volgain < -.001 || vmu->volgain > .001) {
                create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
-               snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
-               tmpfd = mkstemp(newtmp);
-               chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
-               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);
-                       if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
-                               attach = newtmp;
-                               ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+
+               res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir);
+               if (res >= sizeof(sox_gain_tmpdir)) {
+                       ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir);
+                       break;
+               }
+
+               if (mkdtemp(sox_gain_tmpdir)) {
+                       int soxstatus = 0;
+                       char sox_gain_cmd[PATH_MAX];
+
+                       ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
+
+                       /* Save for later */
+                       dir_to_delete = sox_gain_tmpdir;
+
+                       res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
+                       if (res >= sizeof(fname)) {
+                               ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format);
+                               break;
+                       }
+
+                       res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
+                                                  vmu->volgain, attach, format, fname);
+                       if (res >= sizeof(sox_gain_cmd)) {
+                               ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n");
+                               break;
+                       }
+
+                       soxstatus = ast_safe_system(sox_gain_cmd);
+                       if (!soxstatus) {
+                               /* Save for later */
+                               file_to_delete = fname;
+                               ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox);
                        } else {
-                               ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
-                                       soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
+                               ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n",
+                                               fname,
+                                               soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
                                ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
                        }
                }
+
+               break;
+       }
+
+       if (!file_to_delete) {
+               res = snprintf(fname, sizeof(fname), "%s.%s", attach, format);
+               if (res >= sizeof(fname)) {
+                       ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format);
+                       return -1;
+               }
        }
+
        fprintf(p, "--%s" ENDL, bound);
        if (msgnum > -1)
-               fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
+               fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);
        else
-               fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
+               fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format);
        fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
        fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
        if (msgnum > -1)
                fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
        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);
        if (last)
                fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
-       if (tmpfd > -1) {
-               if (soxstatus == 0) {
-                       unlink(fname);
-               }
-               close(tmpfd);
-               unlink(newtmp);
+
+       if (file_to_delete) {
+               unlink(file_to_delete);
        }
+
+       if (dir_to_delete) {
+               rmdir(dir_to_delete);
+       }
+
        return 0;
 }
 
@@ -5524,9 +5681,9 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
  * \brief Gets the current date and time, as formatted string.
  * \param s The buffer to hold the output formatted date.
  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
- * 
+ *
  * The date format string used is "%a %b %e %r UTC %Y".
- * 
+ *
  * \return zero on success, -1 on error.
  */
 static int get_date(char *s, int len)
@@ -5579,17 +5736,48 @@ static void free_zone(struct vm_zone *z)
 }
 
 #ifdef ODBC_STORAGE
-static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+
+static int count_messages_in_folder(struct odbc_obj *odbc, const char *context, const char *mailbox, const char *folder, int *messages)
 {
-       int x = -1;
        int res;
-       SQLHSTMT stmt = NULL;
        char sql[PATH_MAX];
        char rowdata[20];
+       SQLHSTMT stmt = NULL;
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
+
+       if (!messages) {
+               return 0;
+       }
+
+       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+       if (!(stmt = ast_odbc_prepare_and_execute(odbc, generic_prepare, &gps))) {
+               ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               return 1;
+       }
+       res = SQLFetch(stmt);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               return 1;
+       }
+       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               return 1;
+       }
+
+       *messages = atoi(rowdata);
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+       return 0;
+}
+
+static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+{
        char tmp[PATH_MAX] = "";
-       struct odbc_obj *obj = NULL;
+       struct odbc_obj *obj;
        char *context;
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
 
        if (newmsgs)
                *newmsgs = 0;
@@ -5631,87 +5819,28 @@ static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *
        } else
                context = "default";
 
-       if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
-               do {
-                       if (newmsgs) {
-                               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
-                               if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
-                                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               res = SQLFetch(stmt);
-                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               *newmsgs = atoi(rowdata);
-                               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       }
-
-                       if (oldmsgs) {
-                               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
-                               if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
-                                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               res = SQLFetch(stmt);
-                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-                               *oldmsgs = atoi(rowdata);
-                       }
-
-                       if (urgentmsgs) {
-                               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
-                               if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
-                                       ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               res = SQLFetch(stmt);
-                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                                       break;
-                               }
-                               *urgentmsgs = atoi(rowdata);
-                       }
-
-                       x = 0;
-               } while (0);
-       } else {
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+               return -1;
        }
 
-       if (stmt) {
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-       }
-       if (obj) {
-               ast_odbc_release_obj(obj);
+       if (count_messages_in_folder(obj, context, tmp, "INBOX", newmsgs)
+          || count_messages_in_folder(obj, context, tmp, "Old", oldmsgs)
+          || count_messages_in_folder(obj, context, tmp, "Urgent", urgentmsgs)) {
+               ast_log(AST_LOG_WARNING, "Failed to obtain message count for mailbox %s@%s\n",
+                               tmp, context);
        }
-       return x;
+
+       ast_odbc_release_obj(obj);
+       return 0;
 }
 
 /*!
  * \brief Gets the number of messages that exist in a mailbox folder.
  * \param mailbox_id
  * \param folder
- * 
+ *
  * This method is used when ODBC backend is used.
  * \return The number of messages in this mailbox folder (zero or more).
  */
@@ -5738,44 +5867,46 @@ static int messagecount(const char *mailbox_id, const char *folder)
        }
 
        obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               if (!strcmp(folder, "INBOX")) {
-                       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
-               } else {
-                       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
-               }
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       goto yuck;
-               }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       goto yuck;
-               }
-               nummsgs = atoi(rowdata);
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-       } else
+       if (!obj) {
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+               return 0;
+       }
 
-yuck:
-       if (obj)
-               ast_odbc_release_obj(obj);
+       if (!strcmp(folder, "INBOX")) {
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
+       } else {
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+       }
+
+       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+       if (!stmt) {
+               ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               goto bail;
+       }
+       res = SQLFetch(stmt);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+       if (!SQL_SUCCEEDED(res)) {
+               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+               goto bail_with_handle;
+       }
+       nummsgs = atoi(rowdata);
+
+bail_with_handle:
+       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+
+bail:
+       ast_odbc_release_obj(obj);
        return nummsgs;
 }
 
-/** 
+/**
  * \brief Determines if the given folder has messages.
  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- * 
+ *
  * This function is used when the mailbox is stored in an ODBC back end.
  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
  * \return 1 if the folder has one or more messages. zero otherwise.
@@ -5795,7 +5926,7 @@ static int has_voicemail(const char *mailboxes, const char *folder)
 }
 #endif
 #ifndef IMAP_STORAGE
-/*! 
+/*!
  * \brief Copies a message from one mailbox to another.
  * \param chan
  * \param vmu
@@ -5927,7 +6058,7 @@ static int __has_voicemail(const char *context, const char *mailbox, const char
        return ret;
 }
 
-/** 
+/**
  * \brief Determines if the given folder has messages.
  * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
  * \param folder the folder to look in
@@ -5958,22 +6089,33 @@ static int has_voicemail(const char *mailbox, const char *folder)
        return 0;
 }
 
-
+/*!
+ * \brief Check the given mailbox's message count.
+ * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
+ * \param urgentmsgs  urgent message count.
+ * \param newmsgs new message count.
+ * \param oldmsgs old message count pointer
+ * \return -1 if error occurred, 0 otherwise.
+ */
 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
 {
        char tmp[256];
        char *context;
 
        /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
+       if (ast_strlen_zero(mailbox)) {
                return 0;
+       }
 
-       if (newmsgs)
+       if (newmsgs) {
                *newmsgs = 0;
-       if (oldmsgs)
+       }
+       if (oldmsgs) {
                *oldmsgs = 0;
-       if (urgentmsgs)
+       }
+       if (urgentmsgs) {
                *urgentmsgs = 0;
+       }
 
        if (strchr(mailbox, ',')) {
                int tmpnew, tmpold, tmpurgent;
@@ -5983,15 +6125,18 @@ static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *
                mb = tmp;
                while ((cur = strsep(&mb, ", "))) {
                        if (!ast_strlen_zero(cur)) {
-                               if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+                               if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL)) {
                                        return -1;
-                               else {
-                                       if (newmsgs)
-                                               *newmsgs += tmpnew; 
-                                       if (oldmsgs)
+                               } else {
+                                       if (newmsgs) {
+                                               *newmsgs += tmpnew;
+                                       }
+                                       if (oldmsgs) {
                                                *oldmsgs += tmpold;
-                                       if (urgentmsgs)
+                                       }
+                                       if (urgentmsgs) {
                                                *urgentmsgs += tmpurgent;
+                                       }
                                }
                        }
                }
@@ -5999,18 +6144,22 @@ static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *
        }
 
        ast_copy_string(tmp, mailbox, sizeof(tmp));
-       
-       if ((context = strchr(tmp, '@')))
+
+       if ((context = strchr(tmp, '@'))) {
                *context++ = '\0';
-       else
+       } else {
                context = "default";
+       }
 
-       if (newmsgs)
+       if (newmsgs) {
                *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
-       if (oldmsgs)
+       }
+       if (oldmsgs) {
                *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
-       if (urgentmsgs)
+       }
+       if (urgentmsgs) {
                *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
+       }
 
        return 0;
 }
@@ -6041,7 +6190,7 @@ static void run_externnotify(char *context, char *extension, const char *flag)
                ast_copy_string(ext_context, extension, sizeof(ext_context));
 
        if (smdi_iface) {
-               if (ast_app_has_voicemail(ext_context, NULL)) 
+               if (ast_app_has_voicemail(ext_context, NULL))
                        ast_smdi_mwi_set(smdi_iface, extension);
                else
                        ast_smdi_mwi_unset(smdi_iface, extension);
@@ -6151,6 +6300,7 @@ static int msg_create_from_file(struct ast_vm_recording_data *recdata)
                return -1;
        }
 
+       memset(&svm, 0, sizeof(svm));
        if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
                ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
                return -1;
@@ -6388,9 +6538,9 @@ static int msg_create_from_file(struct ast_vm_recording_data *recdata)
  * \param chan
  * \param ext
  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
- * 
- * 
- * 
+ *
+ *
+ *
  * \return zero on success, -1 on error.
  */
 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
@@ -6414,6 +6564,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
        int ausemacro = 0;
        int ousemacro = 0;
        int ouseexten = 0;
+       int greeting_only = 0;
        char tmpdur[16];
        char priority[16];
        char origtime[16];
@@ -6466,19 +6617,27 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
        }
 
        ast_debug(3, "Before find_user\n");
+       memset(&svm, 0, sizeof(svm));
        if (!(vmu = find_user(&svm, context, ext))) {
                ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
                pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
                ast_free(tmp);
                return res;
        }
+
+       /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
+       if (vmu->maxmsg == 0) {
+               greeting_only = 1;
+               ast_set_flag(options, OPT_SILENT);
+       }
+
        /* Setup pre-file if appropriate */
        if (strcmp(vmu->context, "default"))
                snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
        else
                ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
 
-       /* Set the path to the prefile. Will be one of 
+       /* Set the path to the prefile. Will be one of
                VM_SPOOL_DIRcontext/ext/busy
                VM_SPOOL_DIRcontext/ext/unavail
           Depending on the flag set in options.
@@ -6495,6 +6654,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
        snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
        if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
                ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
+               free_user(vmu);
                ast_free(tmp);
                return -1;
        }
@@ -6564,11 +6724,11 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
        /* Play the beginning intro if desired */
        if (!ast_strlen_zero(prefile)) {
 #ifdef ODBC_STORAGE
-               int success = 
+               int success =
 #endif
                        RETRIEVE(prefile, -1, ext, context);
                if (ast_fileexists(prefile, NULL, NULL) > 0) {
-                       if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) 
+                       if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1)
                                res = ast_waitstream(chan, ecodes);
 #ifdef ODBC_STORAGE
                        if (success == -1) {
@@ -6595,12 +6755,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                ast_set_flag(options, OPT_SILENT);
                res = 0;
        }
-       /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
-       if (vmu->maxmsg == 0) {
-               ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
-               goto leave_vm_out;
-       }
        if (!res && !ast_test_flag(options, OPT_SILENT)) {
                res = ast_stream_and_wait(chan, INTRO, ecodes);
                if (res == '#') {
@@ -6638,9 +6792,9 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                        }
                        ast_play_and_wait(chan, "transfer");
                        ast_channel_priority_set(chan, 0);
-                       free_user(vmu);
                        pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
                }
+               free_user(vmu);
                ast_free(tmp);
                return OPERATOR_EXIT;
        }
@@ -6656,6 +6810,13 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                return res;
        }
 
+       if (greeting_only) {
+               ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
+               pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
+               res = 0;
+               goto leave_vm_out;
+       }
+
        if (res < 0) {
                free_user(vmu);
                pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
@@ -6674,6 +6835,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                res = inboxcount(ext_context, &newmsgs, &oldmsgs);
                if (res < 0) {
                        ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
+                       free_user(vmu);
                        ast_free(tmp);
                        return -1;
                }
@@ -6684,6 +6846,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                 */
                        if (!(vms = create_vm_state_from_user(vmu))) {
                                ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
+                               free_user(vmu);
                                ast_free(tmp);
                                return -1;
                        }
@@ -6730,7 +6893,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                        /* Unless we're *really* silent, try to send the beep */
                        res = ast_stream_and_wait(chan, "beep", "");
                }
-                               
+
                /* Store information in real-time storage */
                if (ast_check_realtime("voicemail_data")) {
                        snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
@@ -6764,7 +6927,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
                                S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
                                "Unknown");
-                       fprintf(txt, 
+                       fprintf(txt,
                                ";\n"
                                "; Message Information file\n"
                                ";\n"
@@ -6783,7 +6946,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                "msg_id=%s\n",
                                ext,
                                ast_channel_context(chan),
-                               ast_channel_macrocontext(chan), 
+                               ast_channel_macrocontext(chan),
                                ast_channel_exten(chan),
                                S_COR(ast_channel_redirecting(chan)->from.number.valid,
                                        ast_channel_redirecting(chan)->from.number.str, "unknown"),
@@ -6802,7 +6965,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                        res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
                        goto leave_vm_out;
                }
-               res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id);
+               res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id, 0);
 
                if (txt) {
                        fprintf(txt, "flag=%s\n", flag);
@@ -6838,7 +7001,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 #endif
                                        make_file(fn, sizeof(fn), dir, msgnum);
 
-                                       /* assign a variable with the name of the voicemail file */ 
+                                       /* assign a variable with the name of the voicemail file */
 #ifndef IMAP_STORAGE
                                        pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
 #else
@@ -6878,6 +7041,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                                        *cntx = '\0';
                                                        cntx++;
                                                }
+                                               memset(&recipu, 0, sizeof(recipu));
                                                if ((recip = find_user(&recipu, cntx, exten))) {
                                                        copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
                                                        free_user(recip);
@@ -6948,7 +7112,7 @@ leave_vm_out:
 #ifdef HAVE_IMAP_TK2006
                if (LEVELUIDPLUS (vms->mailstream)) {
                        mail_expunge_full(vms->mailstream, NIL, EX_UID);
-               } else 
+               } else
 #endif
                        mail_expunge(vms->mailstream);
                ast_mutex_unlock(&vms->lock);
@@ -7005,6 +7169,7 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
        char sequence[10];
        char mailbox[256];
        int res;
+       int curr_mbox;
 
        /* get the real IMAP message number for this message */
        snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
@@ -7014,26 +7179,35 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
        /* if save to Old folder, put in INBOX as read */
        if (box == OLD_FOLDER) {
                mail_setflag(vms->mailstream, sequence, "\\Seen");
-               mail_clearflag(vms->mailstream, sequence, "\\Unseen");
        } else if (box == NEW_FOLDER) {
-               mail_setflag(vms->mailstream, sequence, "\\Unseen");
                mail_clearflag(vms->mailstream, sequence, "\\Seen");
        }
        if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
                ast_mutex_unlock(&vms->lock);
                return 0;
        }
-       /* Create the folder if it don't exist */
+
+       /* get the current mailbox so that we can point the mailstream back to it later */
+       curr_mbox = get_folder_by_name(vms->curbox);
+
+       /* Create the folder if it dosn't exist */
        imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
-       ast_debug(5, "Checking if folder exists: %s\n", mailbox);
-       if (mail_create(vms->mailstream, mailbox) == NIL)
-               ast_debug(5, "Folder exists.\n");
-       else
-               ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
-       if (move) {
-               res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
+       if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
+               if (mail_create(vms->mailstream, mailbox) != NIL) {
+                       ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
+               }
+       }
+
+       /* restore previous mbox stream */
+       if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
+               ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+               res = -1;
        } else {
-               res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
+               if (move) {
+                       res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
+               } else {
+                       res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
+               }
        }
        ast_mutex_unlock(&vms->lock);
        return res;
@@ -7067,7 +7241,7 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
        } else {
                if (x >= vmu->maxmsg) {
                        ast_unlock_path(ddir);
-                       return -1;
+                       return ERROR_MAX_MSGS;
                }
        }
        make_file(sfn, sizeof(sfn), dir, msg);
@@ -7318,7 +7492,7 @@ static void adsi_folders(struct ast_channel *chan, int start, char *label)
 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
 {
        int bytes = 0;
-       unsigned char buf[256]; 
+       unsigned char buf[256];
        char buf1[256], buf2[256];
        char fn2[PATH_MAX];
 
@@ -7339,7 +7513,7 @@ static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
        snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
        f = fopen(fn2, "r");
        if (f) {
-               while (!feof(f)) {      
+               while (!feof(f)) {
                        if (!fgets((char *) buf, sizeof(buf), f)) {
                                continue;
                        }
@@ -7555,7 +7729,7 @@ static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
        bytes += ast_adsi_voice_mode(buf + bytes, 0);
 
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-       
+
 }
 
 /*
@@ -7672,9 +7846,9 @@ static int get_folder_ja(struct ast_channel *chan, int start)
  * \param start Does not appear to be used at this time.
  *
  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
- * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the 
+ * After playing the  message identified by the fn parameter value, it calls get_folder(), which plays the
  * prompting for the number inputs that correspond to the available folders.
- * 
+ *
  * \return zero on success, or -1 on error.
  */
 static int get_folder2(struct ast_channel *chan, char *fn, int start)
@@ -7713,7 +7887,7 @@ static int get_folder2(struct ast_channel *chan, char *fn, int start)
  * \param record_gain
  * \param duration
  * \param vms
- * \param flag 
+ * \param flag
  *
  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
  *
@@ -7755,7 +7929,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                if (cmd)
                        retries = 0;
                switch (cmd) {
-               case '1': 
+               case '1':
 
 #ifdef IMAP_STORAGE
                        /* Record new intro file */
@@ -7764,9 +7938,9 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                        }
                        make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
                        strncat(vms->introfn, "intro", sizeof(vms->introfn));
-                       ast_play_and_wait(chan, INTRO);
+                       ast_play_and_wait(chan, "vm-record-prepend");
                        ast_play_and_wait(chan, "beep");
-                       cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id);
+                       cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id, 1);
                        if (cmd == -1) {
                                break;
                        }
@@ -7813,7 +7987,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                        if (record_gain)
                                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
 
-                       
+
                        if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
                                *duration = atoi(duration_str);
 
@@ -7824,7 +7998,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
 
                                *duration += prepend_duration;
                                msg_cat = ast_category_get(msg_cfg, "message", NULL);
-                               snprintf(duration_buf, 11, "%ld", *duration);
+                               snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
                                if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
                                        ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
                                }
@@ -7832,7 +8006,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
 
 #endif
                        break;
-               case '2': 
+               case '2':
                        /* NULL out introfile so we know there is no intro! */
 #ifdef IMAP_STORAGE
                        *vms->introfn = '\0';
@@ -7842,7 +8016,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                case '*':
                        cmd = '*';
                        break;
-               default: 
+               default:
                        /* If time_out and return to menu, reset already_recorded */
                        already_recorded = 0;
 
@@ -7977,7 +8151,7 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu,
                DELETE(todir, msgnum, fn, vmu);
 
        /* Leave voicemail for someone */
-       if (ast_app_has_voicemail(ext_context, NULL)) 
+       if (ast_app_has_voicemail(ext_context, NULL))
                ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
 
        queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
@@ -8001,17 +8175,17 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu,
  * \param vms
  * \param sender
  * \param fmt
- * \param is_new_message Used to indicate the mode for which this method was invoked. 
+ * \param is_new_message Used to indicate the mode for which this method was invoked.
  *             Will be 0 when called to forward an existing message (option 8)
  *             Will be 1 when called to leave a message (option 3->5)
- * \param record_gain 
+ * \param record_gain
  * \param urgent
  *
  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
- * 
+ *
  * When in the leave message mode (is_new_message == 1):
  *   - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
- *   - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
+ *   - attempt to determine the context and mailbox, and then invoke leave_message() function to record and store the message.
  *
  * When in the forward message mode (is_new_message == 0):
  *   - retrieves the current message to be forwarded
@@ -8035,6 +8209,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
        AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
        char *stringp;
        const char *s;
+       const char mailbox_context[256];
        int saved_messages = 0;
        int valid_extensions = 0;
        char *dir;
@@ -8063,19 +8238,19 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                if (cmd)
                                        retries = 0;
                                switch (cmd) {
-                               case '1': 
+                               case '1':
                                        use_directory = 0;
                                        done = 1;
                                        break;
-                               case '2': 
+                               case '2':
                                        use_directory = 1;
                                        done = 1;
                                        break;
-                               case '*': 
+                               case '*':
                                        cmd = 't';
                                        done = 1;
                                        break;
-                               default: 
+                               default:
                                        /* Press 1 to enter an extension press 2 to use the directory */
                                        cmd = ast_play_and_wait(chan, "vm-forward");
                                        if (!cmd) {
@@ -8111,7 +8286,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                old_exten = ast_strdupa(ast_channel_exten(chan));
                                old_priority = ast_channel_priority(chan);
 
-                               /* call the the Directory, changes the channel */
+                               /* call the Directory, changes the channel */
                                snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
                                res = pbx_exec(chan, directory_app, vmcontext);
 
@@ -8143,20 +8318,44 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                /* start optimistic */
                valid_extensions = 1;
                while (s) {
+                       snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
                        if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
                                int oldmsgs;
                                int newmsgs;
                                int capacity;
-                               if (inboxcount(s, &newmsgs, &oldmsgs)) {
-                                       ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
+
+                               if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
+                                       ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
                                        /* Shouldn't happen, but allow trying another extension if it does */
                                        res = ast_play_and_wait(chan, "pbx-invalid");
                                        valid_extensions = 0;
                                        break;
                                }
+#ifdef IMAP_STORAGE
+                               if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
+                                       if (!(dstvms = create_vm_state_from_user(receiver))) {
+                                               ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
+                                               /* Shouldn't happen, but allow trying another extension if it does */
+                                               res = ast_play_and_wait(chan, "pbx-invalid");
+                                               valid_extensions = 0;
+                                               break;
+                                       }
+                               }
+                               check_quota(dstvms, imapfolder);
+                               if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
+                                       ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
+                                       res = ast_play_and_wait(chan, "vm-mailboxfull");
+                                       valid_extensions = 0;
+                                       while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
+                                               inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
+                                               free_user(vmtmp);
+                                       }
+                                       break;
+                               }
+#endif
                                capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
                                if ((newmsgs + oldmsgs) >= capacity) {
-                                       ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
+                                       ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
                                        res = ast_play_and_wait(chan, "vm-mailboxfull");
                                        valid_extensions = 0;
                                        while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
@@ -8176,7 +8375,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
                                        free_user(receiver);
                                }
-                               ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
+                               ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
                                /* "I am sorry, that's not a valid extension.  Please try again." */
                                res = ast_play_and_wait(chan, "pbx-invalid");
                                valid_extensions = 0;
@@ -8245,7 +8444,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
 #ifdef IMAP_STORAGE
                                int attach_user_voicemail;
                                char *myserveremail = serveremail;
-                               
+
                                /* get destination mailbox */
                                dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
                                if (!dstvms) {
@@ -8256,8 +8455,8 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                        if (!dstvms->mailstream) {
                                                ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
                                        } else {
-                                               copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
-                                               run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str); 
+                                               copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
+                                               run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
                                        }
                                } else {
                                        ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
@@ -8292,10 +8491,6 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                        res = ast_play_and_wait(chan, "vm-messages");
                                if (!res)
                                        res = ast_play_and_wait(chan, "vm-saved"); */
-#ifdef IMAP_STORAGE
-                               /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
-                               if (ast_strlen_zero(vmstmp.introfn))
-#endif
                                res = ast_play_and_wait(chan, "vm-msgforwarded");
                        }
 #ifndef IMAP_STORAGE
@@ -8341,12 +8536,12 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
 {
        int res;
-       if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
-               ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file); 
+       if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
+               ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
        return res;
 }
 
-static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
+static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
 {
        ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
        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);
@@ -8414,6 +8609,8 @@ static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *v
                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
        } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {     /* GREEK syntax */
                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q  H 'digits/kai' M ", NULL);
+       } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {     /* ICELANDIC syntax */
+               res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
        } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {     /* ITALIAN syntax */
                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
        } else if (!strcasecmp(ast_channel_language(chan),"ja")) {     /* Japanese syntax */
@@ -8573,7 +8770,7 @@ static int play_message_duration(struct ast_channel *chan, struct vm_state *vms,
 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
 {
        int res = 0;
-       char filename[256], *cid;
+       char filename[PATH_MAX], *cid;
        const char *origtime, *context, *category, *duration, *flag;
        struct ast_config *msg_cfg;
        struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
@@ -8640,6 +8837,12 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc
                                res = wait_file2(chan, vms, "vm-number");
                                res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
                        }
+               /* ICELANDIC syntax */
+               } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {
+                       res = wait_file2(chan, vms, "vm-message");
+                       if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
+                               res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n");
+                       }
                /* VIETNAMESE syntax */
                } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
                        if (!vms->curmsg) {
@@ -8737,13 +8940,13 @@ static int imap_remove_file(char *dir, int msgnum)
        char fn[PATH_MAX];
        char full_fn[PATH_MAX];
        char intro[PATH_MAX] = {0,};
-       
+
        if (msgnum > -1) {
                make_file(fn, sizeof(fn), dir, msgnum);
                snprintf(intro, sizeof(intro), "%sintro", fn);
        } else
                ast_copy_string(fn, dir, sizeof(fn));
-       
+
        if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
                ast_filedelete(fn, NULL);
                if (!ast_strlen_zero(intro)) {
@@ -8761,9 +8964,10 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
 {
        char *file, *filename;
        char *attachment;
-       char arg[10];
+       char arg[11];
        int i;
        BODY* body;
+       int curr_mbox;
 
        file = strrchr(ast_strdupa(dir), '/');
        if (file) {
@@ -8774,6 +8978,16 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
        }
 
        ast_mutex_lock(&vms->lock);
+
+       /* get the current mailbox so that we can point the mailstream back to it later */
+       curr_mbox = get_folder_by_name(vms->curbox);
+
+       if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
+               ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+               ast_mutex_unlock(&vms->lock);
+               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. */
@@ -8786,11 +9000,19 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
                }
                filename = strsep(&attachment, ".");
                if (!strcmp(filename, file)) {
-                       sprintf(arg, "%d", i + 1);
+                       snprintf(arg, sizeof(arg), "%d", i + 1);
                        mail_setflag(vms->mailstream, arg, "\\DELETED");
                }
        }
        mail_expunge(vms->mailstream);
+
+       if (curr_mbox != -1) {
+               /* restore previous mbox stream */
+               if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
+                       ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
+               }
+       }
+
        ast_mutex_unlock(&vms->lock);
        return 0;
 }
@@ -8892,7 +9114,7 @@ static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
                } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
                        /* Move to old folder before deleting */
                        res = save_to_folder(vmu, vms, x, 1, NULL, 0);
-                       if (res == ERROR_LOCK_PATH) {
+                       if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
                                /* If save failed do not delete the message */
                                ast_log(AST_LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
                                vms->deleted[x] = 0;
@@ -9070,10 +9292,10 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
 
        if (vms->newmessages) {
                res = ast_play_and_wait(chan, "vm-youhave");
-               if (!res) 
+               if (!res)
                        res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
                if (!res) {
-                       if ((vms->newmessages == 1)) {
+                       if (vms->newmessages == 1) {
                                res = ast_play_and_wait(chan, "vm-INBOX");
                                if (!res)
                                        res = ast_play_and_wait(chan, "vm-message");
@@ -9087,7 +9309,7 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
                res = ast_play_and_wait(chan, "vm-youhave");
                if (!res)
                        res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
-               if ((vms->oldmessages == 1)){
+               if (vms->oldmessages == 1){
                        res = ast_play_and_wait(chan, "vm-Old");
                        if (!res)
                                res = ast_play_and_wait(chan, "vm-message");
@@ -9096,20 +9318,20 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
                        if (!res)
                                res = ast_play_and_wait(chan, "vm-messages");
                }
-       } else if (!vms->oldmessages && !vms->newmessages) 
-               res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
+       } else if (!vms->oldmessages && !vms->newmessages)
+               res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
        return res;
 }
 
 /* Version of vm_intro() designed to work for many languages.
  *
- * It is hoped that this function can prevent the proliferation of 
+ * It is hoped that this function can prevent the proliferation of
  * language-specific vm_intro() functions and in time replace the language-
  * specific functions which already exist.  An examination of the language-
  * specific functions revealed that they all corrected the same deficiencies
  * in vm_intro_en() (which was the default function). Namely:
  *
- *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
+ *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English
  *     wording of the voicemail greeting hides this problem.  For example,
  *     vm-INBOX contains only the word "new".  This means that both of these
  *     sequences produce valid utterances:
@@ -9135,7 +9357,7 @@ static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
  *  3) Call ast_say_counted_adjective() to put the proper gender and number
  *     prefix on vm-new and vm-old (none for English).
  *  4) Pass the gender of the language's word for "message" as an agument to
- *     this function which is can in turn pass on to the functions which 
+ *     this function which is can in turn pass on to the functions which
  *     say numbers and put endings on nounds and adjectives.
  *
  * All languages require these messages:
@@ -9247,7 +9469,7 @@ static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
                                        if (vms->oldmessages == 2) {
                                                res = ast_play_and_wait(chan, "vm-shtei");
                                        } else {
-                                               res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");            
+                                               res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
                                        }
                                        res = ast_play_and_wait(chan, "vm-Old");
                                }
@@ -9302,7 +9524,7 @@ static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
       }
       return res;
 } /* Japanese */
-       
+
 /* Default English syntax */
 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
 {
@@ -9318,7 +9540,7 @@ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
                        if ((vms->oldmessages || vms->newmessages) && !res) {
                                res = ast_play_and_wait(chan, "vm-and");
                        } else if (!res) {
-                               if ((vms->urgentmessages == 1))
+                               if (vms->urgentmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-message");
                                else
                                        res = ast_play_and_wait(chan, "vm-messages");
@@ -9331,12 +9553,12 @@ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
                        if (vms->oldmessages && !res)
                                res = ast_play_and_wait(chan, "vm-and");
                        else if (!res) {
-                               if ((vms->newmessages == 1))
+                               if (vms->newmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-message");
                                else
                                        res = ast_play_and_wait(chan, "vm-messages");
                        }
-                               
+
                }
                if (!res && vms->oldmessages) {
                        res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
@@ -9360,6 +9582,75 @@ static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
        return res;
 }
 
+/* ICELANDIC syntax */
+static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
+{
+       int res;
+
+       /* Introduce messages they have */
+       res = ast_play_and_wait(chan, "vm-youhave");
+       if (!res) {
+               if (vms->urgentmessages) {
+                       /* Digits 1-4 are spoken in neutral and plural when talking about messages,
+                          however, feminine is used for 1 as it is the same as the neutral for plural,
+                          and singular neutral is the same after 1. */
+                       if (vms->urgentmessages < 5) {
+                               char recname[16];
+                               if (vms->urgentmessages == 1)
+                                       snprintf(recname, sizeof(recname), "digits/1kvk");
+                               else
+                                       snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages);
+                               res = ast_play_and_wait(chan, recname);
+                       } else if (!res)
+                               res = ast_play_and_wait(chan, "vm-Urgent");
+                       if ((vms->oldmessages || vms->newmessages) && !res) {
+                               res = ast_play_and_wait(chan, "vm-and");
+                       } else if (!res)
+                               res = ast_play_and_wait(chan, "vm-messages");
+               }
+               if (vms->newmessages) {
+                       if (vms->newmessages < 5) {
+                               char recname[16];
+                               if (vms->newmessages == 1)
+                                       snprintf(recname, sizeof(recname), "digits/1kvk");
+                               else
+                                       snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages);
+                               res = ast_play_and_wait(chan, recname);
+                       } else
+                               res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
+                       if (!res)
+                               res = ast_play_and_wait(chan, "vm-INBOX");
+                       if (vms->oldmessages && !res)
+                               res = ast_play_and_wait(chan, "vm-and");
+                       else if (!res)
+                               res = ast_play_and_wait(chan, "vm-messages");
+               }
+               if (!res && vms->oldmessages) {
+                       if (vms->oldmessages < 5) {
+                               char recname[16];
+                               if (vms->oldmessages == 1)
+                                       snprintf(recname, sizeof(recname), "digits/1kvk");
+                               else
+                                       snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages);
+                               res = ast_play_and_wait(chan, recname);
+                       } else
+                               res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
+                       if (!res)
+                               res = ast_play_and_wait(chan, "vm-Old");
+                       if (!res)
+                               res = ast_play_and_wait(chan, "vm-messages");
+               }
+               if (!res) {
+                       if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
+                               res = ast_play_and_wait(chan, "vm-no");
+                               if (!res)
+                                       res = ast_play_and_wait(chan, "vm-messages");
+                       }
+               }
+       }
+       return res;
+}
+
 /* ITALIAN syntax */
 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
 {
@@ -9483,7 +9774,7 @@ static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
        }
 
        if (vms->newmessages) {
-               if ((vms->newmessages == 1)) {
+               if (vms->newmessages == 1) {
                        res = ast_play_and_wait(chan, "digits/ett");
                        res = res ? res : ast_play_and_wait(chan, "vm-nytt");
                        res = res ? res : ast_play_and_wait(chan, "vm-message");
@@ -9527,7 +9818,7 @@ static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
        }
 
        if (vms->newmessages) {
-               if ((vms->newmessages == 1)) {
+               if (vms->newmessages == 1) {
                        res = ast_play_and_wait(chan, "digits/1");
                        res = res ? res : ast_play_and_wait(chan, "vm-ny");
                        res = res ? res : ast_play_and_wait(chan, "vm-message");
@@ -9562,7 +9853,7 @@ static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
        res = ast_play_and_wait(chan, "vm-youhave");
        if (!res) {
                if (vms->newmessages) {
-                       if ((vms->newmessages == 1))
+                       if (vms->newmessages == 1)
                                res = ast_play_and_wait(chan, "digits/1F");
                        else
                                res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
@@ -9571,12 +9862,12 @@ static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
                        if (vms->oldmessages && !res)
                                res = ast_play_and_wait(chan, "vm-and");
                        else if (!res) {
-                               if ((vms->newmessages == 1))
+                               if (vms->newmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-message");
                                else
                                        res = ast_play_and_wait(chan, "vm-messages");
                        }
-                               
+
                }
                if (!res && vms->oldmessages) {
                        if (vms->oldmessages == 1)
@@ -9618,7 +9909,7 @@ static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
        if (!res) {
                if (vms->newmessages) {
                        if (!res) {
-                               if ((vms->newmessages == 1)) {
+                               if (vms->newmessages == 1) {
                                        res = ast_play_and_wait(chan, "digits/1M");
                                        if (!res)
                                                res = ast_play_and_wait(chan, "vm-message");
@@ -9669,7 +9960,7 @@ static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
        if (vms->newmessages) {
                if (!res)
                        res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
-               if ((vms->newmessages == 1)) {
+               if (vms->newmessages == 1) {
                        if (!res)
                                res = ast_play_and_wait(chan, "vm-message");
                        if (!res)
@@ -9715,12 +10006,12 @@ static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
                        if (vms->oldmessages && !res)
                                res = ast_play_and_wait(chan, "vm-and");
                        else if (!res) {
-                               if ((vms->newmessages == 1))
+                               if (vms->newmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-message");
                                else
                                        res = ast_play_and_wait(chan, "vm-messages");
                        }
-                               
+
                }
                if (!res && vms->oldmessages) {
                        res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
@@ -9762,12 +10053,12 @@ static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
                        if (vms->oldmessages && !res)
                                res = ast_play_and_wait(chan, "vm-and");
                        else if (!res) {
-                               if ((vms->newmessages == 1))
+                               if (vms->newmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-message");
                                else
                                        res = ast_play_and_wait(chan, "vm-messages");
                        }
-                               
+
                }
                if (!res && vms->oldmessages) {
                        res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
@@ -9805,7 +10096,7 @@ static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
                if (vms->newmessages) {
                        res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
                        if (!res) {
-                               if ((vms->newmessages == 1)) {
+                               if (vms->newmessages == 1) {
                                        res = ast_play_and_wait(chan, "vm-message");
                                        if (!res)
                                                res = ast_play_and_wait(chan, "vm-INBOXs");
@@ -9848,14 +10139,14 @@ static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
 /* in czech there must be declension of word new and message
  * czech        : english        : czech      : english
  * --------------------------------------------------------
- * vm-youhave   : you have 
+ * vm-youhave   : you have
  * vm-novou     : one new        : vm-zpravu  : message
  * vm-nove      : 2-4 new        : vm-zpravy  : messages
  * vm-novych    : 5-infinite new : vm-zprav   : messages
  * vm-starou   : one old
- * vm-stare     : 2-4 old 
+ * vm-stare     : 2-4 old
  * vm-starych   : 5-infinite old
- * jednu        : one  - falling 4. 
+ * jednu        : one  - falling 4.
  * vm-no        : no  ( no messages )
  */
 
@@ -9871,7 +10162,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
                                res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
                        }
                        if (!res) {
-                               if ((vms->newmessages == 1))
+                               if (vms->newmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-novou");
                                if ((vms->newmessages) > 1 && (vms->newmessages < 5))
                                        res = ast_play_and_wait(chan, "vm-nove");
@@ -9881,7 +10172,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
                        if (vms->oldmessages && !res)
                                res = ast_play_and_wait(chan, "vm-and");
                        else if (!res) {
-                               if ((vms->newmessages == 1))
+                               if (vms->newmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-zpravu");
                                if ((vms->newmessages) > 1 && (vms->newmessages < 5))
                                        res = ast_play_and_wait(chan, "vm-zpravy");
@@ -9892,7 +10183,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
                if (!res && vms->oldmessages) {
                        res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
                        if (!res) {
-                               if ((vms->oldmessages == 1))
+                               if (vms->oldmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-starou");
                                if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
                                        res = ast_play_and_wait(chan, "vm-stare");
@@ -9900,7 +10191,7 @@ static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
                                        res = ast_play_and_wait(chan, "vm-starych");
                        }
                        if (!res) {
-                               if ((vms->oldmessages == 1))
+                               if (vms->oldmessages == 1)
                                        res = ast_play_and_wait(chan, "vm-zpravu");
                                if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
                                        res = ast_play_and_wait(chan, "vm-zpravy");
@@ -9936,7 +10227,7 @@ static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
                        res = ast_play_and_wait(chan, "vm-INBOX");
                if (vms->oldmessages && !res)
                        res = ast_play_and_wait(chan, "vm-and");
-               else if (!res) 
+               else if (!res)
                        res = ast_play_and_wait(chan, "vm-messages");
        }
        if (!res && vms->oldmessages) {
@@ -9976,7 +10267,7 @@ static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
                if (!res && vms->oldmessages) {
                        res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
                        if (!res)
-                               res = ast_play_and_wait(chan, "vm-Old");                        
+                               res = ast_play_and_wait(chan, "vm-Old");
                }
                if (!res) {
                        if (!vms->oldmessages && !vms->newmessages) {
@@ -9992,7 +10283,7 @@ static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
 {
        char prefile[256];
-       
+
        /* Notify the user that the temp greeting is set and give them the option to remove it */
        snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
        if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
@@ -10024,6 +10315,8 @@ static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm
                return vm_intro_gr(chan, vms);
        } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* HEBREW syntax */
                return vm_intro_he(chan, vms);
+       } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {  /* ICELANDIC syntax */
+               return vm_intro_is(chan, vms);
        } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {  /* ITALIAN syntax */
                return vm_intro_it(chan, vms);
        } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {  /* JAPANESE syntax */
@@ -10273,7 +10566,7 @@ static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, st
 }
 
 
-static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
+static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
 {
        int cmd = 0;
        int duration = 0;
@@ -10298,7 +10591,7 @@ static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct
        if (ast_test_flag(vmu, VM_FORCENAME)) {
                snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
                if (ast_fileexists(prefile, NULL, NULL) < 1) {
-                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd < 0 || cmd == 't' || cmd == '#')
                                return cmd;
                }
@@ -10308,14 +10601,14 @@ static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct
        if (ast_test_flag(vmu, VM_FORCEGREET)) {
                snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
                if (ast_fileexists(prefile, NULL, NULL) < 1) {
-                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd < 0 || cmd == 't' || cmd == '#')
                                return cmd;
                }
 
                snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
                if (ast_fileexists(prefile, NULL, NULL) < 1) {
-                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd < 0 || cmd == 't' || cmd == '#')
                                return cmd;
                }
@@ -10397,15 +10690,15 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct
                switch (cmd) {
                case '1': /* Record your unavailable message */
                        snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
-                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        break;
                case '2':  /* Record your busy message */
                        snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
-                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        break;
                case '3': /* Record greeting */
                        snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
-                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        break;
                case '4':  /* manage the temporary greeting */
                        cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
@@ -10499,7 +10792,7 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct
 }
 
 /*!
- * \brief The handler for 'record a temporary greeting'. 
+ * \brief The handler for 'record a temporary greeting'.
  * \param chan
  * \param vmu
  * \param vms
@@ -10539,22 +10832,22 @@ static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, st
                        retries = 0;
                RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
                if (ast_fileexists(prefile, NULL, NULL) <= 0) {
-                       cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                       cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                        if (cmd == -1) {
                                break;
                        }
-                       cmd = 't';      
+                       cmd = 't';
                } else {
                        switch (cmd) {
                        case '1':
-                               cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL);
+                               cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
                                break;
                        case '2':
                                DELETE(prefile, -1, prefile, vmu);
                                ast_play_and_wait(chan, "vm-tempremoved");
-                               cmd = 't';      
+                               cmd = 't';
                                break;
-                       case '*': 
+                       case '*':
                                cmd = 't';
                                break;
                        default:
@@ -10587,7 +10880,7 @@ static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, st
  * \param vmu
  *
  * \return zero on success, -1 on error.
- */    
+ */
 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 {
        int cmd = 0;
@@ -10611,7 +10904,7 @@ static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms,
                                cmd = ast_play_and_wait(chan, vms->fn);
                        }
                }
-       } 
+       }
        return cmd;
 }
 
@@ -10632,7 +10925,7 @@ static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms,
        return cmd;
 }
 
-/*! 
+/*!
  * \brief Default English syntax for 'You have N messages' greeting.
  * \param chan
  * \param vms
@@ -10648,7 +10941,7 @@ static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms,
                cmd = play_message(chan, vmu, vms);
        } else {
                cmd = ast_play_and_wait(chan, "vm-youhave");
-               if (!cmd) 
+               if (!cmd)
                        cmd = ast_play_and_wait(chan, "vm-no");
                if (!cmd) {
                        snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
@@ -10660,7 +10953,7 @@ static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms,
        return cmd;
 }
 
-/*! 
+/*!
  *\brief Italian syntax for 'You have N messages' greeting.
  * \param chan
  * \param vms
@@ -10713,7 +11006,7 @@ static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms,
         return cmd;
 }
 
-/*! 
+/*!
  * \brief Spanish syntax for 'You have N messages' greeting.
  * \param chan
  * \param vms
@@ -10739,7 +11032,7 @@ static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms,
        return cmd;
 }
 
-/*! 
+/*!
  * \brief Portuguese syntax for 'You have N messages' greeting.
  * \param chan
  * \param vms
@@ -10765,7 +11058,7 @@ static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms,
        return cmd;
 }
 
-/*! 
+/*!
  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
  * \param chan
  * \param vms
@@ -10781,7 +11074,7 @@ static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms,
                cmd = play_message(chan, vmu, vms);
        } else {
                cmd = ast_play_and_wait(chan, "vm-you");
-               if (!cmd) 
+               if (!cmd)
                        cmd = ast_play_and_wait(chan, "vm-haveno");
                if (!cmd)
                        cmd = ast_play_and_wait(chan, "vm-messages");
@@ -10793,7 +11086,7 @@ static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms,
        return cmd;
 }
 
-/*! 
+/*!
  * \brief Vietnamese syntax for 'You have N messages' greeting.
  * \param chan
  * \param vms
@@ -10822,7 +11115,7 @@ static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms,
  * \param chan The channel for the current user. We read the language property from this.
  * \param vms passed into the language-specific vm_browse_messages function.
  * \param vmu passed into the language-specific vm_browse_messages function.
- * 
+ *
  * The method to be invoked is determined by the value of language code property in the user's channel.
  * The default (when unable to match) is to use english.
  *
@@ -10856,14 +11149,14 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                        int skipuser, int max_logins, int silent)
 {
        int useadsi = 0, valid = 0, logretries = 0;
-       char password[AST_MAX_EXTENSION]="", *passptr;
+       char password[AST_MAX_EXTENSION], *passptr;
        struct ast_vm_user vmus, *vmu = NULL;
 
        /* If ADSI is supported, setup login screen */
        adsi_begin(chan, &useadsi);
        if (!skipuser && useadsi)
                adsi_login(chan);
-       if (!silent && !skipuser && ast_streamfile(chan, "vm-login", ast_channel_language(chan))) {
+       if (!silent && !skipuser && ast_streamfile(chan, vm_login, ast_channel_language(chan))) {
                ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
                return -1;
        }
@@ -10880,7 +11173,7 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                        if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
                                ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
                        } else {
-                               ast_verb(3, "Username not entered\n");  
+                               ast_verb(3, "Username not entered\n");
                                return -1;
                        }
                } else if (mailbox[0] == '*') {
@@ -10898,13 +11191,15 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                        adsi_password(chan);
 
                if (!ast_strlen_zero(prefix)) {
-                       char fullusername[80] = "";
+                       char fullusername[80];
+
                        ast_copy_string(fullusername, prefix, sizeof(fullusername));
                        strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
                        ast_copy_string(mailbox, fullusername, mailbox_size);
                }
 
                ast_debug(1, "Before find user for mailbox %s\n", mailbox);
+               memset(&vmus, 0, sizeof(vmus));
                vmu = find_user(&vmus, context, mailbox);
                if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
                        /* saved password is blank, so don't bother asking */
@@ -10912,10 +11207,12 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                } else {
                        if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
                                ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
+                               free_user(vmu);
                                return -1;
                        }
                        if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
                                ast_log(AST_LOG_WARNING, "Unable to read password\n");
+                               free_user(vmu);
                                return -1;
                        } else if (password[0] == '*') {
                                /* user entered '*' */
@@ -10923,11 +11220,13 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                                if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
                                        S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
                                        mailbox[0] = '*';
+                                       free_user(vmu);
                                        return -1;
                                }
                                ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
                                mailbox[0] = '\0';
                                /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
+                               free_user(vmu);
                                vmu = NULL;
                        }
                }
@@ -10948,6 +11247,11 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                        if (skipuser || logretries >= max_logins) {
                                if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
                                        ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
+                                       free_user(vmu);
+                                       return -1;
+                               }
+                               if (ast_waitstream(chan, "")) { /* Channel is hung up */
+                                       free_user(vmu);
                                        return -1;
                                }
                        } else {
@@ -10955,20 +11259,20 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                                        adsi_login(chan);
                                if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
                                        ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
+                                       free_user(vmu);
                                        return -1;
                                }
                        }
-                       if (ast_waitstream(chan, ""))   /* Channel is hung up */
-                               return -1;
                }
        }
        if (!valid && (logretries >= max_logins)) {
                ast_stopstream(chan);
                ast_play_and_wait(chan, "vm-goodbye");
+               free_user(vmu);
                return -1;
        }
        if (vmu && !skipuser) {
-               memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
+               *res_vmu = *vmu;
        }
        return 0;
 }
@@ -10984,7 +11288,6 @@ static int play_message_by_id_helper(struct ast_channel *chan,
        /* Found the msg, so play it back */
 
        make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
-       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
 
 #ifdef IMAP_STORAGE
        /*IMAP storage stores any prepended message from a forward
@@ -10994,6 +11297,8 @@ static int play_message_by_id_helper(struct ast_channel *chan,
                wait_file(chan, vms, vms->introfn);
        }
 #endif
+       RETRIEVE(vms->curdir,vms->curmsg,vmu->mailbox, vmu->context);
+
        if ((wait_file(chan, vms, vms->fn)) < 0) {
                ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
        } else {
@@ -11005,7 +11310,7 @@ static int play_message_by_id_helper(struct ast_channel *chan,
                ast_mutex_unlock(&vms->lock);
 #endif
        }
-
+       DISPOSE(vms->curdir, vms->curmsg);
        return 0;
 }
 
@@ -11072,6 +11377,8 @@ play_msg_cleanup:
        }
 #endif
 
+       free_user(vmu);
+
        return res;
 }
 
@@ -11127,8 +11434,8 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
        int box;
        int useadsi = 0;
        int skipuser = 0;
-       struct vm_state vms;
-       struct ast_vm_user *vmu = NULL, vmus;
+       struct vm_state vms = {{0}};
+       struct ast_vm_user *vmu = NULL, vmus = {{0}};
        char *context = NULL;
        int silentexit = 0;
        struct ast_flags flags = { 0 };
@@ -11141,11 +11448,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
 #endif
 
        /* Add the vm_state to the active list and keep it active */
-       memset(&vms, 0, sizeof(vms));
-
-       vms.lastmsg = -1;
-
-       memset(&vmus, 0, sizeof(vmus));
+       vms.lastmsg = -1;
 
        ast_test_suite_event_notify("START", "Message: vm_execmain started");
        if (ast_channel_state(chan) != AST_STATE_UP) {
@@ -11209,7 +11512,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
                                        ast_set_flag(&flags, OPT_SILENT);
                                else if (*(args.argv0) == 'p')
                                        ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
-                               else 
+                               else
                                        break;
                                (args.argv0)++;
                        }
@@ -11346,11 +11649,11 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
        res = 0;
 
        /* Check to see if this is a new user */
-       if (!strcasecmp(vmu->mailbox, vmu->password) && 
+       if (!strcasecmp(vmu->mailbox, vmu->password) &&
                (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
-               if (ast_play_and_wait(chan, "vm-newuser") == -1)
+               if (ast_play_and_wait(chan, vm_newuser) == -1)
                        ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
-               cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
+               cmd = vm_newuser_setup(chan, vmu, &vms, vmfmts, record_gain);
                if ((cmd == 't') || (cmd == '#')) {
                        /* Timeout */
                        ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
@@ -11738,7 +12041,7 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
                        snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
                        if (!cmd) {
                                cmd = ast_play_and_wait(chan, "vm-message");
-                               if (!cmd) 
+                               if (!cmd)
                                        cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
                                if (!cmd)
                                        cmd = ast_play_and_wait(chan, "vm-savedto");
@@ -11814,7 +12117,13 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
                        cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
                        if (useadsi)
                                adsi_status(chan, &vms);
-                       break;
+                       /* Reopen play_folder */
+                       res = open_mailbox(&vms, vmu, play_folder);
+                       if (res < 0) {
+                               goto out;
+                       }
+                       vms.starting = 1;
+                       break;
                default:        /* Nothing */
                        ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
                        cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
@@ -11836,7 +12145,7 @@ out:
                if (valid && res != OPERATOR_EXIT) {
                        if (silentexit)
                                res = ast_play_and_wait(chan, "vm-dialout");
-                       else 
+                       else
                                res = ast_play_and_wait(chan, "vm-goodbye");
                }
                if ((valid && res > 0) || res == OPERATOR_EXIT) {
@@ -11894,7 +12203,7 @@ static int vm_exec(struct ast_channel *chan, const char *data)
                AST_APP_ARG(argv0);
                AST_APP_ARG(argv1);
        );
-       
+
        memset(&leave_options, 0, sizeof(leave_options));
 
        if (ast_channel_state(chan) != AST_STATE_UP)
@@ -12008,15 +12317,15 @@ static struct ast_vm_user *find_or_create(const char *context, const char *box)
                        return NULL;
                }
        }
-       
+
        if (!(vmu = ast_calloc(1, sizeof(*vmu))))
                return NULL;
-       
+
        ast_copy_string(vmu->context, context, sizeof(vmu->context));
        ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
 
        AST_LIST_INSERT_TAIL(&users, vmu, list);
-       
+
        return vmu;
 }
 
@@ -12072,18 +12381,22 @@ static int append_mailbox(const char *context, const char *box, const char *data
        strcat(mailbox_full, context);
 
        inboxcount2(mailbox_full, &urgent, &new, &old);
+#ifdef IMAP_STORAGE
+       imap_logout(mailbox_full);
+#endif
        queue_mwi_event(NULL, mailbox_full, urgent, new, old);
 
        return 0;
 }
 
+#ifdef TEST_FRAMEWORK
 AST_TEST_DEFINE(test_voicemail_vmuser)
 {
        int res = 0;
        struct ast_vm_user *vmu;
        /* language parameter seems to only be used for display in manager action */
        static const char options_string[] = "attach=yes|attachfmt=wav49|"
-               "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
+               "serveremail=someguy@digium.com|fromstring=Voicemail System|tz=central|delete=yes|saycid=yes|"
                "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
                "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
                "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
@@ -12124,6 +12437,10 @@ AST_TEST_DEFINE(test_voicemail_vmuser)
                ast_test_status_update(test, "Parse failure for attachftm option\n");
                res = 1;
        }
+       if (strcasecmp(vmu->fromstring, "Voicemail System")) {
+               ast_test_status_update(test, "Parse failure for fromstring option\n");
+               res = 1;
+       }
        if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
                ast_test_status_update(test, "Parse failure for serveremail option\n");
                res = 1;
@@ -12264,10 +12581,11 @@ AST_TEST_DEFINE(test_voicemail_vmuser)
        free_user(vmu);
        return res ? AST_TEST_FAIL : AST_TEST_PASS;
 }
+#endif
 
-static int vm_box_exists(struct ast_channel *chan, const char *data) 
+static int vm_box_exists(struct ast_channel *chan, const char *data)
 {
-       struct ast_vm_user svm;
+       struct ast_vm_user svm, *vmu;
        char *context, *box;
        AST_DECLARE_APP_ARGS(args,
                AST_APP_ARG(mbox);
@@ -12297,8 +12615,11 @@ static int vm_box_exists(struct ast_channel *chan, const char *data)
                context++;
        }
 
-       if (find_user(&svm, context, args.mbox)) {
+       memset(&svm, 0, sizeof(svm));
+       vmu = find_user(&svm, context, args.mbox);
+       if (vmu) {
                pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
+               free_user(vmu);
        } else
                pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
 
@@ -12307,7 +12628,7 @@ static int vm_box_exists(struct ast_channel *chan, const char *data)
 
 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
 {
-       struct ast_vm_user svm;
+       struct ast_vm_user svm, *vmu;
        AST_DECLARE_APP_ARGS(arg,
                AST_APP_ARG(mbox);
                AST_APP_ARG(context);
@@ -12326,7 +12647,11 @@ static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *a
                ast_log(AST_LOG_WARNING, "MAILBOX_EXISTS is deprecated.  Please use ${VM_INFO(%s,exists)} instead.\n", args);
        }
 
-       ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
+       memset(&svm, 0, sizeof(svm));
+       vmu = find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox);
+       ast_copy_string(buf, vmu ? "1" : "0", len);
+       free_user(vmu);
+
        return 0;
 }
 
@@ -12362,10 +12687,12 @@ static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, ch
                return -1;
        }
 
+       memset(&svm, 0, sizeof(svm));
        vmu = find_user(&svm, context, mailbox);
 
        if (!strncasecmp(arg.attribute, "exists", 5)) {
                ast_copy_string(buf, vmu ? "1" : "0", len);
+               free_user(vmu);
                return 0;
        }
 
@@ -12394,13 +12721,16 @@ static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, ch
                        res = messagecount(mailbox_id, arg.folder);
                        if (res < 0) {
                                ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
+                               free_user(vmu);
                                return -1;
                        }
                        snprintf(buf, len, "%d", res);
                } else {
                        ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
+                       free_user(vmu);
                        return -1;
                }
+               free_user(vmu);
        }
 
        return 0;
@@ -12419,11 +12749,11 @@ static struct ast_custom_function vm_info_acf = {
 static int vmauthenticate(struct ast_channel *chan, const char *data)
 {
        char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
-       struct ast_vm_user vmus;
+       struct ast_vm_user vmus = {{0}};
        char *options = NULL;
        int silent = 0, skipuser = 0;
        int res = -1;
-       
+
        if (data) {
                s = ast_strdupa(data);
                user = strsep(&s, ",");
@@ -12462,7 +12792,7 @@ static char *show_users_realtime(int fd, const char *context)
        struct ast_config *cfg;
        const char *cat = NULL;
 
-       if (!(cfg = ast_load_realtime_multientry("voicemail", 
+       if (!(cfg = ast_load_realtime_multientry("voicemail",
                "context", context, SENTINEL))) {
                return CLI_FAILURE;
        }
@@ -12503,11 +12833,9 @@ static char *complete_voicemail_show_users(const char *line, const char *word, i
        struct ast_vm_user *vmu;
        const char *context = "";
 
-       /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
+       /* 0 - voicemail; 1 - show; 2 - users; 3 - for; 4 - <context> */
        if (pos > 4)
                return NULL;
-       if (pos == 3)
-               return (state == 0) ? ast_strdup("for") : NULL;
        wordlen = strlen(word);
        AST_LIST_TRAVERSE(&users, vmu, list) {
                if (!strncasecmp(word, vmu->context, wordlen)) {
@@ -12530,14 +12858,14 @@ static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struc
 
        switch (cmd) {
        case CLI_INIT:
-               e->command = "voicemail show users";
+               e->command = "voicemail show users [for]";
                e->usage =
                        "Usage: voicemail show users [for <context>]\n"
                        "       Lists all mailboxes currently set up\n";
                return NULL;
        case CLI_GENERATE:
                return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
-       }       
+       }
 
        if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
                return CLI_SHOWUSAGE;
@@ -12649,9 +12977,9 @@ static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct as
        if (a->argc != 2)
                return CLI_SHOWUSAGE;
 
-       ast_cli(a->fd, "Reloading voicemail configuration...\n");       
+       ast_cli(a->fd, "Reloading voicemail configuration...\n");
        load_config(1);
-       
+
        return CLI_SUCCESS;
 }
 
@@ -12661,151 +12989,18 @@ static struct ast_cli_entry cli_voicemail[] = {
        AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
 };
 
-#ifdef IMAP_STORAGE
-       #define DATA_EXPORT_VM_USERS(USER)                                      \
-               USER(ast_vm_user, context, AST_DATA_STRING)                     \
-               USER(ast_vm_user, mailbox, AST_DATA_STRING)                     \
-               USER(ast_vm_user, password, AST_DATA_PASSWORD)                  \
-               USER(ast_vm_user, fullname, AST_DATA_STRING)                    \
-               USER(ast_vm_user, email, AST_DATA_STRING)                       \
-               USER(ast_vm_user, emailsubject, AST_DATA_STRING)                \
-               USER(ast_vm_user, emailbody, AST_DATA_STRING)                   \
-               USER(ast_vm_user, pager, AST_DATA_STRING)                       \
-               USER(ast_vm_user, serveremail, AST_DATA_STRING)                 \
-               USER(ast_vm_user, language, AST_DATA_STRING)                    \
-               USER(ast_vm_user, zonetag, AST_DATA_STRING)                     \
-               USER(ast_vm_user, callback, AST_DATA_STRING)                    \
-               USER(ast_vm_user, dialout, AST_DATA_STRING)                     \
-               USER(ast_vm_user, uniqueid, AST_DATA_STRING)                    \
-               USER(ast_vm_user, exit, AST_DATA_STRING)                        \
-               USER(ast_vm_user, attachfmt, AST_DATA_STRING)                   \
-               USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)             \
-               USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)               \
-               USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)                     \
-               USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)              \
-               USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)                    \
-               USER(ast_vm_user, imapuser, AST_DATA_STRING)                    \
-               USER(ast_vm_user, imappassword, AST_DATA_STRING)                \
-               USER(ast_vm_user, imapvmshareid, AST_DATA_STRING)               \
-               USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
-#else
-       #define DATA_EXPORT_VM_USERS(USER)                                      \
-               USER(ast_vm_user, context, AST_DATA_STRING)                     \
-               USER(ast_vm_user, mailbox, AST_DATA_STRING)                     \
-               USER(ast_vm_user, password, AST_DATA_PASSWORD)                  \
-               USER(ast_vm_user, fullname, AST_DATA_STRING)                    \
-               USER(ast_vm_user, email, AST_DATA_STRING)                       \
-               USER(ast_vm_user, emailsubject, AST_DATA_STRING)                \
-               USER(ast_vm_user, emailbody, AST_DATA_STRING)                   \
-               USER(ast_vm_user, pager, AST_DATA_STRING)                       \
-               USER(ast_vm_user, serveremail, AST_DATA_STRING)                 \
-               USER(ast_vm_user, language, AST_DATA_STRING)                    \
-               USER(ast_vm_user, zonetag, AST_DATA_STRING)                     \
-               USER(ast_vm_user, callback, AST_DATA_STRING)                    \
-               USER(ast_vm_user, dialout, AST_DATA_STRING)                     \
-               USER(ast_vm_user, uniqueid, AST_DATA_STRING)                    \
-               USER(ast_vm_user, exit, AST_DATA_STRING)                        \
-               USER(ast_vm_user, attachfmt, AST_DATA_STRING)                   \
-               USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER)             \
-               USER(ast_vm_user, saydurationm, AST_DATA_INTEGER)               \
-               USER(ast_vm_user, maxmsg, AST_DATA_INTEGER)                     \
-               USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER)              \
-               USER(ast_vm_user, maxsecs, AST_DATA_INTEGER)                    \
-               USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
-#endif
-
-AST_DATA_STRUCTURE(ast_vm_user, DATA_EXPORT_VM_USERS);
-
-#define DATA_EXPORT_VM_ZONES(ZONE)                     \
-       ZONE(vm_zone, name, AST_DATA_STRING)            \
-       ZONE(vm_zone, timezone, AST_DATA_STRING)        \
-       ZONE(vm_zone, msg_format, AST_DATA_STRING)
-
-AST_DATA_STRUCTURE(vm_zone, DATA_EXPORT_VM_ZONES);
-
-/*!
- * \internal
- * \brief Add voicemail user to the data_root.
- * \param[in] search The search tree.
- * \param[in] data_root The main result node.
- * \param[in] user The voicemail user.
- */
-static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
-    struct ast_data *data_root, struct ast_vm_user *user)
-{
-       struct ast_data *data_user, *data_zone;
-       struct ast_data *data_state;
-       struct vm_zone *zone = NULL;
-       int urgentmsg = 0, newmsg = 0, oldmsg = 0;
-       char ext_context[256] = "";
-
-       data_user = ast_data_add_node(data_root, "user");
-       if (!data_user) {
-               return -1;
-       }
-
-       ast_data_add_structure(ast_vm_user, data_user, user);
-
-       AST_LIST_LOCK(&zones);
-       AST_LIST_TRAVERSE(&zones, zone, list) {
-               if (!strcmp(zone->name, user->zonetag)) {
-                       break;
-               }
-       }
-       AST_LIST_UNLOCK(&zones);
-
-       /* state */
-       data_state = ast_data_add_node(data_user, "state");
-       if (!data_state) {
-               return -1;
-       }
-       snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
-       inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
-       ast_data_add_int(data_state, "urgentmsg", urgentmsg);
-       ast_data_add_int(data_state, "newmsg", newmsg);
-       ast_data_add_int(data_state, "oldmsg", oldmsg);
-
-       if (zone) {
-               data_zone = ast_data_add_node(data_user, "zone");
-               ast_data_add_structure(vm_zone, data_zone, zone);
-       }
-
-       if (!ast_data_search_match(search, data_user)) {
-               ast_data_remove_node(data_root, data_user);
-       }
-
-       return 0;
-}
-
-static int vm_users_data_provider_get(const struct ast_data_search *search,
-       struct ast_data *data_root)
-{
-       struct ast_vm_user *user;
-
-       AST_LIST_LOCK(&users);
-       AST_LIST_TRAVERSE(&users, user, list) {
-               vm_users_data_provider_get_helper(search, data_root, user);
-       }
-       AST_LIST_UNLOCK(&users);
-
-       return 0;
-}
-
-static const struct ast_data_handler vm_users_data_provider = {
-       .version = AST_DATA_HANDLER_VERSION,
-       .get = vm_users_data_provider_get
-};
-
-static const struct ast_data_entry vm_data_providers[] = {
-       AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
-};
-
 static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
 {
        int new = 0, old = 0, urgent = 0;
 
        inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
 
+#ifdef IMAP_STORAGE
+       if (imap_poll_logout) {
+               imap_logout(mwi_sub->mailbox);
+       }
+#endif
+
        if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
                mwi_sub->old_urgent = urgent;
                mwi_sub->old_new = new;
@@ -12857,6 +13052,55 @@ static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
        ast_free(mwi_sub);
 }
 
+#ifdef IMAP_STORAGE
+static void imap_logout(const char *mailbox_id)
+{
+       char *context;
+       char *mailbox;
+       struct ast_vm_user vmus;
+       RAII_VAR(struct ast_vm_user *, vmu, NULL, free_user);
+       struct vm_state *vms = NULL;
+
+       if (ast_strlen_zero(mailbox_id)
+               || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
+               return;
+       }
+
+       memset(&vmus, 0, sizeof(vmus));
+
+       if (!(vmu = find_user(&vmus, context, mailbox)) || vmu->imapuser[0] == '\0') {
+               return;
+       }
+
+       vms = get_vm_state_by_imapuser(vmu->imapuser, 0);
+       if (!vms) {
+               vms = get_vm_state_by_mailbox(mailbox, context, 0);
+       }
+       if (!vms) {
+               return;
+       }
+
+       ast_mutex_lock(&vms->lock);
+       vms->mailstream = mail_close(vms->mailstream);
+       ast_mutex_unlock(&vms->lock);
+
+       vmstate_delete(vms);
+}
+
+static void imap_close_subscribed_mailboxes(void)
+{
+       struct mwi_sub *mwi_sub;
+
+       AST_RWLIST_RDLOCK(&mwi_subs);
+       AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
+               if (!ast_strlen_zero(mwi_sub->mailbox)) {
+                       imap_logout(mwi_sub->mailbox);
+               }
+       }
+       AST_RWLIST_UNLOCK(&mwi_subs);
+}
+#endif
+
 static int handle_unsubscribe(void *datap)
 {
        struct mwi_sub *mwi_sub;
@@ -12868,6 +13112,9 @@ static int handle_unsubscribe(void *datap)
                        AST_LIST_REMOVE_CURRENT(entry);
                        /* Don't break here since a duplicate uniqueid
                         * may have been added as a result of a cache dump. */
+#ifdef IMAP_STORAGE
+                       imap_logout(mwi_sub->mailbox);
+#endif
                        mwi_sub_destroy(mwi_sub);
                }
        }
@@ -12937,6 +13184,7 @@ static void mwi_sub_event_cb(struct stasis_subscription_change *change)
        }
 
        if (separate_mailbox(ast_strdupa(stasis_topic_name(change->topic)), &mailbox, &context)) {
+               ast_free(mwist);
                return;
        }
 
@@ -13011,6 +13259,127 @@ static void stop_poll_thread(void)
        poll_thread = AST_PTHREADT_NULL;
 }
 
+/*!
+ * \brief Append vmu info string into given astman with event_name.
+ * \return 0 failed. 1 otherwise.
+*/
+static int append_vmu_info_astman(
+               struct mansession *s,
+               struct ast_vm_user *vmu,
+               const char* event_name,
+               const char* actionid
+               )
+{
+       int new;
+       int old;
+       char *mailbox;
+       int ret;
+
+       if((s == NULL) || (vmu == NULL) || (event_name == NULL) || (actionid == NULL)) {
+               ast_log(LOG_ERROR, "Wrong input parameter.");
+               return 0;
+       }
+
+       /* create mailbox string */
+       if (!ast_strlen_zero(vmu->context)) {
+               ret = ast_asprintf(&mailbox, "%s@%s", vmu->mailbox, vmu->context);
+       } else {
+               ret = ast_asprintf(&mailbox, "%s", vmu->mailbox);
+       }
+       if (ret == -1) {
+               ast_log(LOG_ERROR, "Could not create mailbox string. err[%s]\n", strerror(errno));
+               return 0;
+       }
+
+       /* get mailbox count */
+       ret = inboxcount(mailbox, &new, &old);
+       ast_free(mailbox);
+       if (ret == -1) {
+               ast_log(LOG_ERROR, "Could not get mailbox count. user[%s], context[%s]\n",
+                       vmu->mailbox ?: "", vmu->context ?: "");
+               return 0;
+       }
+
+       astman_append(s,
+               "Event: %s\r\n"
+               "%s"
+               "VMContext: %s\r\n"
+               "VoiceMailbox: %s\r\n"
+               "Fullname: %s\r\n"
+               "Email: %s\r\n"
+               "Pager: %s\r\n"
+               "ServerEmail: %s\r\n"
+               "FromString: %s\r\n"
+               "MailCommand: %s\r\n"
+               "Language: %s\r\n"
+               "TimeZone: %s\r\n"
+               "Callback: %s\r\n"
+               "Dialout: %s\r\n"
+               "UniqueID: %s\r\n"
+               "ExitContext: %s\r\n"
+               "SayDurationMinimum: %d\r\n"
+               "SayEnvelope: %s\r\n"
+               "SayCID: %s\r\n"
+               "AttachMessage: %s\r\n"
+               "AttachmentFormat: %s\r\n"
+               "DeleteMessage: %s\r\n"
+               "VolumeGain: %.2f\r\n"
+               "CanReview: %s\r\n"
+               "CallOperator: %s\r\n"
+               "MaxMessageCount: %d\r\n"
+               "MaxMessageLength: %d\r\n"
+               "NewMessageCount: %d\r\n"
+               "OldMessageCount: %d\r\n"
+#ifdef IMAP_STORAGE
+               "IMAPUser: %s\r\n"
+               "IMAPServer: %s\r\n"
+               "IMAPPort: %s\r\n"
+               "IMAPFlags: %s\r\n"
+#endif
+               "\r\n",
+
+               event_name,
+               actionid,
+               vmu->context,
+               vmu->mailbox,
+               vmu->fullname,
+               vmu->email,
+               vmu->pager,
+               ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
+               ast_strlen_zero(vmu->fromstring) ? fromstring : vmu->fromstring,
+               mailcmd,
+               vmu->language,
+               vmu->zonetag,
+               vmu->callback,
+               vmu->dialout,
+               vmu->uniqueid,
+               vmu->exit,
+               vmu->saydurationm,
+               ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
+               ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
+               ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
+               vmu->attachfmt,
+               ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
+               vmu->volgain,
+               ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
+               ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
+               vmu->maxmsg,
+               vmu->maxsecs,
+               new,
+               old
+#ifdef IMAP_STORAGE
+               ,
+               vmu->imapuser,
+               vmu->imapserver,
+               vmu->imapport,
+               vmu->imapflags
+#endif
+               );
+
+       return 1;
+
+}
+
 static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
 {
        const char *context = astman_get_header(m, "Context");
@@ -13047,6 +13416,51 @@ static int manager_voicemail_refresh(struct mansession *s, const struct message
        return RESULT_SUCCESS;
 }
 
+static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
+{
+       struct ast_vm_user *vmu = NULL;
+       const char *id = astman_get_header(m, "ActionID");
+       char actionid[128];
+       struct ast_vm_user svm;
+       int ret;
+
+       const char *context = astman_get_header(m, "Context");
+       const char *mailbox = astman_get_header(m, "Mailbox");
+
+       if ((ast_strlen_zero(context) || ast_strlen_zero(mailbox))) {
+               astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
+               return RESULT_SUCCESS;
+       }
+
+       actionid[0] = '\0';
+       if (!ast_strlen_zero(id)) {
+               snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
+       }
+
+       /* find user */
+       memset(&svm, 0, sizeof(svm));
+       vmu = find_user(&svm, context, mailbox);
+       if (!vmu) {
+               /* could not find it */
+               astman_send_ack(s, m, "There is no voicemail user of the given info.");
+               return RESULT_SUCCESS;
+       }
+
+       astman_send_listack(s, m, "Voicemail user detail will follow", "start");
+
+       /* append vmu info event */
+       ret = append_vmu_info_astman(s, vmu, "VoicemailUserDetail", actionid);
+       free_user(vmu);
+       if(ret == 0) {
+               ast_log(LOG_ERROR, "Could not append voicemail user info.");
+       }
+
+       astman_send_list_complete_start(s, m, "VoicemailUserDetailComplete", 1);
+       astman_send_list_complete_end(s);
+
+       return RESULT_SUCCESS;
+}
+
 /*! \brief Manager list voicemail users command */
 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
 {
@@ -13054,6 +13468,7 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
        const char *id = astman_get_header(m, "ActionID");
        char actionid[128];
        int num_users = 0;
+       int ret;
 
        actionid[0] = '\0';
        if (!ast_strlen_zero(id)) {
@@ -13067,91 +13482,18 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
                AST_LIST_UNLOCK(&users);
                return RESULT_SUCCESS;
        }
-       
+
        astman_send_listack(s, m, "Voicemail user list will follow", "start");
-       
+
        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,
-                       "Event: VoicemailUserEntry\r\n"
-                       "%s"
-                       "VMContext: %s\r\n"
-                       "VoiceMailbox: %s\r\n"
-                       "Fullname: %s\r\n"
-                       "Email: %s\r\n"
-                       "Pager: %s\r\n"
-                       "ServerEmail: %s\r\n"
-                       "MailCommand: %s\r\n"
-                       "Language: %s\r\n"
-                       "TimeZone: %s\r\n"
-                       "Callback: %s\r\n"
-                       "Dialout: %s\r\n"
-                       "UniqueID: %s\r\n"
-                       "ExitContext: %s\r\n"
-                       "SayDurationMinimum: %d\r\n"
-                       "SayEnvelope: %s\r\n"
-                       "SayCID: %s\r\n"
-                       "AttachMessage: %s\r\n"
-                       "AttachmentFormat: %s\r\n"
-                       "DeleteMessage: %s\r\n"
-                       "VolumeGain: %.2f\r\n"
-                       "CanReview: %s\r\n"
-                       "CallOperator: %s\r\n"
-                       "MaxMessageCount: %d\r\n"
-                       "MaxMessageLength: %d\r\n"
-                       "NewMessageCount: %d\r\n"
-#ifdef IMAP_STORAGE
-                       "OldMessageCount: %d\r\n"
-                       "IMAPUser: %s\r\n"
-                       "IMAPServer: %s\r\n"
-                       "IMAPPort: %s\r\n"
-                       "IMAPFlags: %s\r\n"
-#endif
-                       "\r\n",
-                       actionid,
-                       vmu->context,
-                       vmu->mailbox,
-                       vmu->fullname,
-                       vmu->email,
-                       vmu->pager,
-                       ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
-                       mailcmd,
-                       vmu->language,
-                       vmu->zonetag,
-                       vmu->callback,
-                       vmu->dialout,
-                       vmu->uniqueid,
-                       vmu->exit,
-                       vmu->saydurationm,
-                       ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
-                       ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
-                       ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
-                       vmu->attachfmt,
-                       ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
-                       vmu->volgain,
-                       ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
-                       ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
-                       vmu->maxmsg,
-                       vmu->maxsecs,
-#ifdef IMAP_STORAGE
-                       new, old,
-                       vmu->imapuser,
-                       vmu->imapserver,
-                       vmu->imapport,
-                       vmu->imapflags
-#else
-                       count_messages(vmu, dirname)
-#endif
-                       );
+               /* append vmu info event */
+               ret = append_vmu_info_astman(s, vmu, "VoicemailUserEntry", actionid);
+               if(ret == 0) {
+                       ast_log(LOG_ERROR, "Could not append voicemail user info.");
+                       continue;
+               }
                ++num_users;
-       }               
+       }
 
        astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
        astman_send_list_complete_end(s);
@@ -13162,7 +13504,7 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
 }
 
 /*! \brief Free the users structure. */
-static void free_vm_users(void) 
+static void free_vm_users(void)
 {
        struct ast_vm_user *current;
        AST_LIST_LOCK(&users);
@@ -13191,7 +13533,7 @@ static const char *substitute_escapes(const char *value)
        struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
 
        ast_str_reset(str);
-       
+
        /* Substitute strings \r, \n, and \t into the appropriate characters */
        for (current = (char *) value; *current; current++) {
                if (*current == '\\') {
@@ -13290,6 +13632,8 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
        int x;
        unsigned int tmpadsi[4];
        char secretfn[PATH_MAX] = "";
+       long tps_queue_low;
+       long tps_queue_high;
 
 #ifdef IMAP_STORAGE
        ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
@@ -13301,13 +13645,17 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
        strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
        strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
 
-       /* Free all the users structure */      
+#ifdef IMAP_STORAGE
+       imap_close_subscribed_mailboxes();
+#endif
+
+       /* Free all the users structure */
        free_vm_users();
 
        /* Free all the zones structure */
        free_vm_zones();
 
-       AST_LIST_LOCK(&users);  
+       AST_LIST_LOCK(&users);
 
        memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
        memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
@@ -13319,9 +13667,9 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
                        val = "default";
                ast_copy_string(userscontext, val, sizeof(userscontext));
                /* Attach voice message to mail message ? */
-               if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
+               if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
                        val = "yes";
-               ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);        
+               ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
 
                if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
                        val = "no";
@@ -13340,7 +13688,7 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
                if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
                        ast_copy_string(odbc_table, val, sizeof(odbc_table));
                }
-#endif         
+#endif
                /* Mail command */
                strcpy(mailcmd, SENDMAIL);
                if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
@@ -13352,7 +13700,7 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
                        if (maxsilence > 0)
                                maxsilence *= 1000;
                }
-               
+
                if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
                        maxmsg = MAXMSG;
                } else {
@@ -13466,9 +13814,14 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
                } else {
                        ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
                }
+               if ((val = ast_variable_retrieve(cfg, "general", "imap_poll_logout"))) {
+                       imap_poll_logout = ast_true(val);
+               } else {
+                       imap_poll_logout = 0;
+               }
 
                /* There is some very unorthodox casting done here. This is due
-                * to the way c-client handles the argument passed in. It expects a 
+                * to the way c-client handles the argument passed in. It expects a
                 * void pointer and casts the pointer directly to a long without
                 * first dereferencing it. */
                if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
@@ -13577,7 +13930,7 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
 
                val = ast_variable_retrieve(cfg, "general", "format");
                if (!val) {
-                       val = "wav";    
+                       val = "wav";
                } else {
                        tmp = ast_strdupa(val);
                        val = ast_format_str_reduce(tmp);
@@ -13747,6 +14100,10 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
                }
 
                /* load password sounds configuration */
+               if ((val = ast_variable_retrieve(cfg, "general", "vm-login")))
+                       ast_copy_string(vm_login, val, sizeof(vm_login));
+               if ((val = ast_variable_retrieve(cfg, "general", "vm-newuser")))
+                       ast_copy_string(vm_newuser, val, sizeof(vm_newuser));
                if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
                        ast_copy_string(vm_password, val, sizeof(vm_password));
                if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
@@ -13777,9 +14134,9 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
                if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
                        ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
 
-               if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
+               if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
                        val = "no";
-               ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);  
+               ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
 
                if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
                        val = "voicemail.conf";
@@ -13865,8 +14222,27 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
                        pagerbody = ast_strdup(substitute_escapes(val));
                }
 
+               tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
+               if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
+                       if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
+                               ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
+                               tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
+                       }
+               }
+               tps_queue_low = -1;
+               if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
+                       if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
+                               tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
+                               ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
+                               tps_queue_low = -1;
+                       }
+               }
+               if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
+                       ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
+               }
+
                /* load mailboxes from users.conf */
-               if (ucfg) {     
+               if (ucfg) {
                        for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
                                if (!strcasecmp(cat, "general")) {
                                        continue;
@@ -14130,7 +14506,7 @@ AST_TEST_DEFINE(test_voicemail_vmsayname)
                                ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
                                goto exit_vmsayname_test;
                        }
-                       snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
+                       snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_DATA_DIR);
                        snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
                        /* we're not going to hear the sound anyway, just use a valid gsm audio file */
                        if ((res = symlink(dir, dir2))) {
@@ -14214,6 +14590,7 @@ AST_TEST_DEFINE(test_voicemail_msgcount)
        }
 #endif
 
+       memset(&svm, 0, sizeof(svm));
        if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
                !(vmu = find_or_create(testcontext, testmailbox))) {
                ast_test_status_update(test, "Cannot create vmu structure\n");
@@ -14243,6 +14620,7 @@ AST_TEST_DEFINE(test_voicemail_msgcount)
 #ifdef IMAP_STORAGE
                                chan = ast_channel_unref(chan);
 #endif
+                               free_user(vmu);
                                return AST_TEST_FAIL;
                        }
                }
@@ -14260,7 +14638,7 @@ AST_TEST_DEFINE(test_voicemail_msgcount)
 
                /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
                for (j = 0; j < 3; j++) {
-                       /* folder[2] is INBOX, __has_voicemail will default back to INBOX */ 
+                       /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
                        if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
                                ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
                                        testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
@@ -14326,6 +14704,7 @@ AST_TEST_DEFINE(test_voicemail_msgcount)
                        syserr > 0 ? strerror(syserr) : "unable to fork()");
        }
 
+       free_user(vmu);
        return res;
 }
 
@@ -14373,8 +14752,8 @@ AST_TEST_DEFINE(test_voicemail_notify_endl)
                break;
        }
 
-       snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
-       snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
+       snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_DATA_DIR);
+       snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_DATA_DIR);
 
        if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
                !(vmu = find_or_create(testcontext, testmailbox))) {
@@ -14422,17 +14801,20 @@ AST_TEST_DEFINE(test_voicemail_notify_endl)
                rewind(file);
                while (fgets(buf, sizeof(buf), file)) {
                        if (
+                       (strlen(buf) > 1 &&
 #ifdef IMAP_STORAGE
                        buf[strlen(buf) - 2] != '\r'
 #else
                        buf[strlen(buf) - 2] == '\r'
 #endif
+                       )
                        || buf[strlen(buf) - 1] != '\n') {
                                res = AST_TEST_FAIL;
                        }
                }
        }
        fclose(file);
+       free_user(vmu);
        return res;
 }
 
@@ -14593,6 +14975,7 @@ AST_TEST_DEFINE(test_voicemail_vm_info)
        }
 
        chan = ast_channel_unref(chan);
+       free_user(vmu);
        return res;
 }
 #endif /* defined(TEST_FRAMEWORK) */
@@ -14640,8 +15023,8 @@ static int unload_module(void)
        res |= ast_custom_function_unregister(&mailbox_exists_acf);
        res |= ast_custom_function_unregister(&vm_info_acf);
        res |= ast_manager_unregister("VoicemailUsersList");
+       res |= ast_manager_unregister("VoicemailUserStatus");
        res |= ast_manager_unregister("VoicemailRefresh");
-       res |= ast_data_unregister(NULL);
 #ifdef TEST_FRAMEWORK
        res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
        res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
@@ -14665,6 +15048,9 @@ static int unload_module(void)
        ast_unload_realtime("voicemail");
        ast_unload_realtime("voicemail_data");
 
+#ifdef IMAP_STORAGE
+       imap_close_subscribed_mailboxes();
+#endif
        free_vm_users();
        free_vm_zones();
        return res;
@@ -14675,10 +15061,14 @@ static int unload_module(void)
  *
  * Module loading including tests for configuration or dependencies.
  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
- * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
- * configuration file or other non-critical problem return 
- * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
+ * or AST_MODULE_LOAD_SUCCESS.
+ *
+ * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
+ *
+ * If the module can't load the configuration file, can't register as a provider or
+ * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
+ *
+ * On success return AST_MODULE_LOAD_SUCCESS.
  */
 static int load_module(void)
 {
@@ -14692,13 +15082,15 @@ static int load_module(void)
 
        /* compute the location of the voicemail spool directory */
        snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
-       
+
        if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
                ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor.  MWI will not work\n");
        }
 
-       if ((res = load_config(0)))
-               return res;
+       if ((res = load_config(0))) {
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
 
        res = ast_register_application_xml(app, vm_exec);
        res |= ast_register_application_xml(app2, vm_execmain);
@@ -14709,6 +15101,7 @@ static int load_module(void)
        res |= ast_custom_function_register(&mailbox_exists_acf);
        res |= ast_custom_function_register(&vm_info_acf);
        res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
+       res |= ast_manager_register_xml("VoicemailUserStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_status_voicemail_user);
        res |= ast_manager_register_xml("VoicemailRefresh", EVENT_FLAG_USER, manager_voicemail_refresh);
 #ifdef TEST_FRAMEWORK
        res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
@@ -14719,14 +15112,29 @@ static int load_module(void)
        res |= AST_TEST_REGISTER(test_voicemail_vm_info);
 #endif
 
-       res |= ast_vm_register(&vm_table);
-       res |= ast_vm_greeter_register(&vm_greeter_table);
        if (res) {
-               return res;
+               ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       /* ast_vm_register may return DECLINE if another module registered for vm */
+       res = ast_vm_register(&vm_table);
+       if (res) {
+               ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
+       res = ast_vm_greeter_register(&vm_greeter_table);
+       if (res) {
+               ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
        }
 
        ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
-       ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
 
 #ifdef TEST_FRAMEWORK
        ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
@@ -14735,10 +15143,10 @@ static int load_module(void)
        ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
        ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
 
-       return res;
+       return AST_MODULE_LOAD_SUCCESS;
 }
 
-static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
+static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
 {
        int cmd = 0;
        char destination[80] = "";
@@ -14768,7 +15176,7 @@ static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num,
                                        ast_verb(3, "User hit '*' to cancel outgoing call\n");
                                        return 0;
                                }
-                               if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0) 
+                               if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
                                        retries++;
                                else
                                        cmd = 't';
@@ -14778,7 +15186,7 @@ static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num,
                if (retries >= 3) {
                        return 0;
                }
-               
+
        } else {
                ast_verb(3, "Destination number is CID number '%s'\n", num);
                ast_copy_string(destination, num, sizeof(destination));
@@ -14786,7 +15194,7 @@ static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num,
 
        if (!ast_strlen_zero(destination)) {
                if (destination[strlen(destination) -1 ] == '*')
-                       return 0; 
+                       return 0;
                ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
                ast_channel_exten_set(chan, destination);
                ast_channel_context_set(chan, outgoing_context);
@@ -14799,13 +15207,13 @@ static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num,
 /*!
  * \brief The advanced options within a message.
  * \param chan
- * \param vmu 
+ * \param vmu
  * \param vms
  * \param msg
  * \param option
  * \param record_gain
  *
- * Provides handling for the play message envelope, call the person back, or reply to message. 
+ * Provides handling for the play message envelope, call the person back, or reply to message.
  *
  * \return zero on success, -1 on error.
  */
@@ -14820,7 +15228,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
        char *cid;
        struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
 
-       vms->starting = 0; 
+       vms->starting = 0;
 
        make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
 
@@ -14939,8 +15347,8 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
                                        }
                                }
                                ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
-                               break; 
-                               
+                               break;
+
                        }
                        if (res == 't')
                                res = 0;
@@ -14948,7 +15356,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
                                res = -1;
                }
                break;
-               
+
        case 1: /* Reply */
                /* Send reply directly to sender */
                if (ast_strlen_zero(cid))
@@ -14962,20 +15370,23 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
                        ast_config_destroy(msg_cfg);
                        return res;
                } else {
-                       struct ast_vm_user vmu2;
-                       if (find_user(&vmu2, vmu->context, num)) {
+                       struct ast_vm_user vmu2, *vmu3;
+                       memset(&vmu2, 0, sizeof(vmu2));
+                       vmu3 = find_user(&vmu2, vmu->context, num);
+                       if (vmu3) {
                                struct leave_vm_options leave_options;
                                char mailbox[AST_MAX_EXTENSION * 2 + 2];
                                snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
 
                                ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
-                               
+
                                memset(&leave_options, 0, sizeof(leave_options));
                                leave_options.record_gain = record_gain;
                                res = leave_voicemail(chan, mailbox, &leave_options);
                                if (!res)
                                        res = 't';
                                ast_config_destroy(msg_cfg);
+                               free_user(vmu3);
                                return res;
                        } else {
                                /* Sender has no mailbox, can't reply */
@@ -14985,7 +15396,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
                                ast_config_destroy(msg_cfg);
                                return res;
                        }
-               } 
+               }
                res = 0;
 
                break;
@@ -15005,7 +15416,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
 
 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
                        int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
-                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id)
+                       signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
 {
        /* Record message & let caller review or re-record it, or set options if applicable */
        int res = 0;
@@ -15045,9 +15456,11 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                        } else {
                                /* Otherwise 1 is to save the existing message */
                                ast_verb(3, "Saving message as is\n");
-                               if (!outsidecaller) 
+                               if (!outsidecaller)
                                        ast_filerename(tempfile, recordfile, NULL);
-                               ast_stream_and_wait(chan, "vm-msgsaved", "");
+                               if (!forwardintro) {
+                                       ast_stream_and_wait(chan, "vm-msgsaved", "");
+                               }
                                if (!outsidecaller) {
                                        /* Saves to IMAP server only if imapgreeting=yes */
                                        STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
@@ -15064,13 +15477,17 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                case '3':
                        msg_exists = 0;
                        /* Record */
-                       if (recorded == 1) 
+                       if (recorded == 1)
                                ast_verb(3, "Re-recording the message\n");
-                       else    
+                       else
                                ast_verb(3, "Recording the message\n");
-                       
+
                        if (recorded && outsidecaller) {
-                               cmd = ast_play_and_wait(chan, INTRO);
+                               if (forwardintro) {
+                                       cmd = ast_play_and_wait(chan, "vm-record-prepend");
+                               } else {
+                                       cmd = ast_play_and_wait(chan, INTRO);
+                               }
                                cmd = ast_play_and_wait(chan, "beep");
                        }
                        recorded = 1;
@@ -15091,7 +15508,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                                if (!outsidecaller) {
                                        /* user was recording a greeting and they hung up, so let's delete the recording. */
                                        ast_filedelete(tempfile, NULL);
-                               }               
+                               }
                                return cmd;
                        }
                        if (cmd == '0') {
@@ -15148,7 +15565,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                case '#':
                        cmd = ast_play_and_wait(chan, "vm-sorry");
                        break;
-#if 0 
+#if 0
 /*  XXX Commented out for the moment because of the dangers of deleting
     a message while recording (can put the message numbers out of sync) */
                case '*':
@@ -15191,10 +15608,10 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                        }
                        return cmd;
                default:
-                       /* If the caller is an ouside caller, and the review option is enabled,
+                       /* If the caller is an ouside caller and the review option is enabled or it's forward intro
                           allow them to review the message, but let the owner of the box review
                           their OGM's */
-                       if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
+                       if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro)
                                return cmd;
                        if (msg_exists) {
                                cmd = ast_play_and_wait(chan, "vm-review");
@@ -15210,7 +15627,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                                if (!cmd)
                                        cmd = ast_waitfordigit(chan, 600);
                        }
-                       
+
                        if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
                                cmd = ast_play_and_wait(chan, "vm-reachoper");
                                if (!cmd)
@@ -15470,11 +15887,13 @@ static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *ma
 
        if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
                ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
+               free_user(vmu);
                return NULL;
        }
 
        if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
                ast_free(mailbox_snapshot);
+               free_user(vmu);
                return NULL;
        }
 
@@ -15535,6 +15954,7 @@ snapshot_cleanup:
        }
 #endif
 
+       free_user(vmu);
        return mailbox_snapshot;
 }
 
@@ -15689,6 +16109,7 @@ static int vm_msg_forward(const char *from_mailbox,
 
        if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
                ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
+               free_user(vmu);
                return -1;
        }
 
@@ -15769,6 +16190,8 @@ vm_forward_cleanup:
                notify_new_state(to_vmu);
        }
 
+       free_user(vmu);
+       free_user(to_vmu);
        return res;
 }
 
@@ -15872,6 +16295,7 @@ vm_move_cleanup:
                notify_new_state(vmu);
        }
 
+       free_user(vmu);
        return res;
 }
 
@@ -15969,6 +16393,7 @@ vm_remove_cleanup:
                notify_new_state(vmu);
        }
 
+       free_user(vmu);
        return res;
 }
 
@@ -16082,6 +16507,7 @@ play2_msg_cleanup:
                notify_new_state(vmu);
        }
 
+       free_user(vmu);
        return res;
 }
 
@@ -16090,9 +16516,9 @@ play2_msg_cleanup:
  */
 
 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
-               .support_level = AST_MODULE_SUPPORT_CORE,
-               .load = load_module,
-               .unload = unload_module,
-               .reload = reload,
-               .nonoptreq = "res_adsi,res_smdi",
-               );
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .reload = reload,
+       .optional_modules = "res_adsi,res_smdi",
+);