Merge "BuildSystem: For consistency, avoid double-checking via if clauses."
[asterisk/asterisk.git] / apps / app_voicemail.c
index 4dffbbe..82fa3b3 100644 (file)
  * \addtogroup configuration_file Configuration Files
  */
 
-/*! 
+/*!
  * \page voicemail.conf voicemail.conf
  * \verbinclude voicemail.conf.sample
  */
 
 /*** MODULEINFO
+       <defaultenabled>yes</defaultenabled>
        <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>
@@ -133,6 +132,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/astobj2.h"
 #include "asterisk/taskprocessor.h"
 #include "asterisk/test.h"
+#include "asterisk/format_cache.h"
 
 #ifdef ODBC_STORAGE
 #include "asterisk/res_odbc.h"
@@ -463,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
@@ -505,8 +522,12 @@ 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 */
+ast_mutex_t mail_open_lock;
+
 struct vm_state;
 struct ast_vm_user;
 
@@ -539,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;
@@ -575,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
 
@@ -611,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 {
@@ -753,6 +779,15 @@ For vm_intro_it:
 \arg \b vm-vecchio     old
 \arg \b vm-vecchi      old plural
 
+Japanese requires the following additional soundfile:
+\arg \b jp-arimasu          there is
+\arg \b jp-arimasen         there is not
+\arg \b jp-oshitekudasai    please press
+\arg \b jp-ni               article ni
+\arg \b jp-ga               article ga
+\arg \b jp-wa               article wa
+\arg \b jp-wo               article wo
+
 Chinese (Taiwan) requires the following additional soundfile:
 \arg \b vm-tong                A class-word for call (tong1)
 \arg \b vm-ri          A class-word for day (ri4)
@@ -776,18 +811,19 @@ 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 */
        char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
        char password[80];               /*!< Secret pin code, numbers only */
        char fullname[80];               /*!< Full name, for directory app */
-       char email[80];                  /*!< E-mail address */
+       char *email;                     /*!< E-mail address */
        char *emailsubject;              /*!< E-mail subject */
        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) */
@@ -796,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 */
@@ -849,7 +885,8 @@ struct vm_state {
 #ifdef IMAP_STORAGE
        ast_mutex_t lock;
        int updated;                         /*!< decremented on each mail check until 1 -allows delay */
-       long msgArray[VMSTATE_MAX_MSG_ARRAY];
+       long *msgArray;
+       unsigned msg_array_max;
        MAILSTREAM *mailstream;
        int vmArrayIndex;
        char imapuser[80];                   /*!< IMAP server login */
@@ -893,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
@@ -947,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;
@@ -1018,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";
@@ -1070,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);
@@ -1082,6 +1121,10 @@ static void read_password_from_file(const char *secretfn, char *password, int pa
 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
  *
@@ -1111,6 +1154,34 @@ static int vm_test_destroy_user(const char *context, const char *mailbox);
 static int vm_test_create_user(const char *context, const char *mailbox);
 #endif
 
+/*!
+ * \internal
+ * \brief Parse the given mailbox_id into mailbox and context.
+ * \since 12.0.0
+ *
+ * \param mailbox_id The mailbox@context string to separate.
+ * \param mailbox Where the mailbox part will start.
+ * \param context Where the context part will start.  ("default" if not present)
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
+{
+       if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
+               return -1;
+       }
+       *context = mailbox_id;
+       *mailbox = strsep(context, "@");
+       if (ast_strlen_zero(*mailbox)) {
+               return -1;
+       }
+       if (ast_strlen_zero(*context)) {
+               *context = "default";
+       }
+       return 0;
+}
+
 struct ao2_container *inprocess_container;
 
 struct inprocess {
@@ -1229,6 +1300,8 @@ static void populate_defaults(struct ast_vm_user *vmu)
                vmu->maxdeletedmsg = maxdeletedmsg;
        }
        vmu->volgain = volgain;
+       ast_free(vmu->email);
+       vmu->email = NULL;
        ast_free(vmu->emailsubject);
        vmu->emailsubject = NULL;
        ast_free(vmu->emailbody);
@@ -1242,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)
@@ -1258,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));
@@ -1294,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;
@@ -1320,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")) {
@@ -1386,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;
 
@@ -1424,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);
                }
@@ -1470,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.
  *
@@ -1503,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;
@@ -1513,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)
@@ -1540,7 +1615,8 @@ static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *
                } else if (!strcasecmp(var->name, "pager")) {
                        ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
                } else if (!strcasecmp(var->name, "email")) {
-                       ast_copy_string(retval->email, var->value, sizeof(retval->email));
+                       ast_free(retval->email);
+                       retval->email = ast_strdup(var->value);
                } else if (!strcasecmp(var->name, "fullname")) {
                        ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
                } else if (!strcasecmp(var->name, "context")) {
@@ -1583,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)
@@ -1635,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;
 }
 
@@ -1649,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)
@@ -1674,12 +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->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;
                }
@@ -1726,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).
@@ -1777,7 +1855,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                                                new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
                                                sprintf(new, "%s%s", newpassword, value);
                                        }
-                                       if (!(cat = ast_category_get(cfg, category))) {
+                                       if (!(cat = ast_category_get(cfg, category, NULL))) {
                                                ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
                                                break;
                                        }
@@ -1790,7 +1868,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                                ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
                                reset_user_pw(vmu->context, vmu->mailbox, newpassword);
                                ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
-                               ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
+                               ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
                                ast_config_destroy(cfg);
                                break;
                        }
@@ -1814,7 +1892,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                                        }
                                        new = ast_alloca(strlen(newpassword) + 1);
                                        sprintf(new, "%s", newpassword);
-                                       if (!(cat = ast_category_get(cfg, category))) {
+                                       if (!(cat = ast_category_get(cfg, category, NULL))) {
                                                ast_debug(4, "failed to get category!\n");
                                                ast_free(var);
                                                break;
@@ -1833,7 +1911,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                                ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
                                reset_user_pw(vmu->context, vmu->mailbox, newpassword);
                                ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
-                               ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
+                               ast_config_text_file_save("users.conf", cfg, "app_voicemail");
                        }
 
                        ast_config_destroy(cfg);
@@ -1854,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.
@@ -1872,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.
@@ -1910,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)
@@ -1957,14 +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->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);
        }
 }
@@ -2117,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
@@ -2137,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.
                */
@@ -2153,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);
@@ -2167,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)
@@ -2195,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);
@@ -2227,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);
 
@@ -2266,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, ".");
@@ -2275,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';
@@ -2285,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]");
@@ -2364,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;
@@ -2375,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_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
+               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;
                }
        }
@@ -2402,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;
                }
        }
@@ -2421,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) {
@@ -2464,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 {
@@ -2471,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;
 }
 
@@ -2502,15 +2622,22 @@ 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 context
- * \param mailbox
+ * \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).
  */
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+static int messagecount(const char *mailbox_id, const char *folder)
 {
+       char *context;
+       char *mailbox;
+
+       if (ast_strlen_zero(mailbox_id)
+               || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
+               return 0;
+       }
+
        if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
                return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
        } else {
@@ -2533,9 +2660,12 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
        STRING str;
        int ret; /* for better error checking */
        char *imap_flags = NIL;
-       int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
+       int msgcount;
        int box = NEW_FOLDER;
 
+       snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
+       msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
+
        /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
        if (msgnum < 0) {
                if(!imapgreetings) {
@@ -2579,7 +2709,7 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
                 * of this function, we will revert back to an empty string if tempcopy
                 * is 1.
                 */
-               ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
+               vmu->email = ast_strdup(vmu->imapuser);
                tempcopy = 1;
        }
 
@@ -2591,8 +2721,10 @@ static int imap_store_file(const char *dir, const char *mailboxuser, const char
           command hangs. */
        if (!(p = vm_mkftemp(tmp))) {
                ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
-               if (tempcopy)
-                       *(vmu->email) = '\0';
+               if (tempcopy) {
+                       ast_free(vmu->email);
+                       vmu->email = NULL;
+               }
                return -1;
        }
 
@@ -2605,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);
@@ -2618,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;
                }
        }
@@ -2658,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.
  *
@@ -2696,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)
@@ -2740,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
@@ -2818,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 {
@@ -2894,7 +3026,9 @@ static int init_mailstream(struct vm_state *vms, int box)
                /* Connect to INBOX first to get folders delimiter */
                imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
                ast_mutex_lock(&vms->lock);
+               ast_mutex_lock(&mail_open_lock);
                stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+               ast_mutex_unlock(&mail_open_lock);
                ast_mutex_unlock(&vms->lock);
                if (stream == NIL) {
                        ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
@@ -2910,7 +3044,13 @@ static int init_mailstream(struct vm_state *vms, int box)
        imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
        ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, 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) {
                return -1;
@@ -3004,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));
@@ -3022,6 +3170,17 @@ static void update_messages_by_imapuser(const char *user, unsigned long number)
        }
 
        ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
+
+       /* Ensure we have room for the next message. */
+       if (vms->vmArrayIndex >= vms->msg_array_max) {
+               long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
+               if (!new_mem) {
+                       return;
+               }
+               vms->msgArray = new_mem;
+               vms->msg_array_max *= 2;
+       }
+
        vms->msgArray[vms->vmArrayIndex++] = number;
 }
 
@@ -3128,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);
 }
 
 
@@ -3253,7 +3425,7 @@ static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
        if (taglen < 1)
                return NULL;
 
-       if (!(start = strstr(header, tag)))
+       if (!(start = strcasestr(header, tag)))
                return NULL;
 
        /* Since we can be called multiple times we should clear our buffer */
@@ -3299,6 +3471,7 @@ static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
                return vms_p;
        }
        ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
+       /* XXX: Is this correctly freed always? */
        if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
                return NULL;
        ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
@@ -3326,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);
@@ -3339,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);
@@ -3365,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);
@@ -3378,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);
 
@@ -3413,6 +3581,7 @@ static void vmstate_insert(struct vm_state *vms)
                        vms->newmessages = altvms->newmessages;
                        vms->oldmessages = altvms->oldmessages;
                        vms->vmArrayIndex = altvms->vmArrayIndex;
+                       /* XXX: no msgArray copying? */
                        vms->lastmsg = altvms->lastmsg;
                        vms->curmsg = altvms->curmsg;
                        /* get a pointer to the persistent store */
@@ -3468,13 +3637,17 @@ 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);
+               vc->vms->msgArray = NULL;
+               vc->vms->msg_array_max = 0;
+               /* XXX: is no one supposed to free vms itself? */
                ast_free(vc);
-       }
-       else
+       } else {
                ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
+       }
 }
 
 static void set_update(MAILSTREAM * stream)
@@ -3484,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;
        }
@@ -3496,11 +3669,13 @@ static void set_update(MAILSTREAM * stream)
 
 static void init_vm_state(struct vm_state *vms)
 {
-       int x;
-       vms->vmArrayIndex = 0;
-       for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
-               vms->msgArray[x] = 0;
+       vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
+       vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
+       if (!vms->msgArray) {
+               /* Out of mem? This can't be good. */
+               vms->msg_array_max = 0;
        }
+       vms->vmArrayIndex = 0;
        ast_mutex_init(&vms->lock);
 }
 
@@ -3509,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)
@@ -3519,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);
@@ -3535,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
  *
@@ -3549,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.
  *
@@ -3609,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;
@@ -3654,16 +3835,16 @@ 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 retreived.
+ * \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)
@@ -3676,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;
@@ -3692,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.
@@ -3840,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.
@@ -3908,41 +4087,45 @@ 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 x;
-}
+               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;
+}
 
 /*!
  * \brief returns the number of messages found.
@@ -3955,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;
 }
 
 /*!
@@ -4006,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)
@@ -4018,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;
 }
 
 /*!
@@ -4060,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 {
@@ -4101,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;
        }
 
@@ -4123,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;
@@ -4140,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.
@@ -4165,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;
        }
@@ -4228,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)
@@ -4280,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;
 }
 
 /*!
@@ -4303,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.
  */
@@ -4312,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;
@@ -4353,7 +4549,7 @@ static int count_messages(struct ast_vm_user *vmu, char *dir)
                closedir(vmdir);
        }
        ast_unlock_path(dir);
-       
+
        return vmcount;
 }
 
@@ -4377,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).
@@ -4443,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.
@@ -4540,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.
  *
@@ -4576,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;
@@ -4686,7 +4889,7 @@ static int base_encode(char *filename, FILE *so)
        }
 
        fclose(fi);
-       
+
        if (fputs(ENDL, so) == EOF) {
                return 0;
        }
@@ -4753,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)
@@ -4867,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.
@@ -4912,8 +5115,11 @@ 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;
+       char *email;
 
        if (!str1 || !str2) {
                ast_free(str1);
@@ -4947,15 +5153,16 @@ 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))) {
-                               int first_line = 1;
+                               first_line = 1;
                                ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
                                while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
                                        *ptr = '\0';
@@ -4976,31 +5183,38 @@ static void make_email_file(FILE *p,
                fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
        }
 
-       if (check_mime(vmu->fullname)) {
-               int first_line = 1;
-               char *ptr;
-               ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
-               while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
-                       *ptr = '\0';
-                       fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
-                       first_line = 0;
-                       /* Substring is smaller, so this will never grow */
-                       ast_str_set(&str2, 0, "%s", ptr + 1);
+       emailsbuf = ast_strdupa(vmu->email);
+       fprintf(p, "To:");
+       first_line = 1;
+       while ((email = strsep(&emailsbuf, "|"))) {
+               char *next = emailsbuf;
+               if (check_mime(vmu->fullname)) {
+                       char *ptr;
+                       ast_str_encode_mime(&str2, 0, vmu->fullname, first_line ? strlen("To: ") : 0, strlen(email) + 3 + (next ? strlen(",") : 0));
+                       while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
+                               *ptr = '\0';
+                               fprintf(p, " %s" ENDL, ast_str_buffer(str2));
+                               /* Substring is smaller, so this will never grow */
+                               ast_str_set(&str2, 0, "%s", ptr + 1);
+                       }
+                       fprintf(p, " %s <%s>%s" ENDL, ast_str_buffer(str2), email, next ? "," : "");
+               } else {
+                       fprintf(p, " %s <%s>%s" ENDL, ast_str_quote(&str2, 0, vmu->fullname), email, next ? "," : "");
                }
-               fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
-       } else {
-               fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
+               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())) {
                        prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
                        ast_str_substitute_variables(&str1, 0, ast, e_subj);
                        if (check_mime(ast_str_buffer(str1))) {
-                               int first_line = 1;
                                char *ptr;
+                               first_line = 1;
                                ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
                                while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
                                        *ptr = '\0';
@@ -5031,7 +5245,7 @@ static void make_email_file(FILE *p,
                }
        }
 
-       fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1,
+       fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
                (unsigned int) ast_random(), mailbox, (int) getpid(), host);
        if (imap) {
                /* additional information needed for IMAP searching */
@@ -5045,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);
@@ -5070,7 +5283,7 @@ static void make_email_file(FILE *p,
        fprintf(p, "MIME-Version: 1.0" ENDL);
        if (attach_user_voicemail) {
                /* Something unique. */
-               snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox,
+               snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
                        (int) getpid(), (unsigned int) ast_random());
 
                fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
@@ -5078,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())) {
@@ -5104,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;
@@ -5149,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) {
@@ -5174,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;
 }
 
@@ -5261,7 +5515,7 @@ static int sendmail(char *srcemail,
 
        if (!strcmp(format, "wav49"))
                format = "WAV";
-       ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
+       ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %u\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
        /* Make a temporary file instead of piping directly to sendmail, in case the mail
           command hangs */
        if ((p = vm_mkftemp(tmp)) == NULL) {
@@ -5427,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)
@@ -5482,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;
@@ -5534,94 +5819,36 @@ 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 (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);
        }
-       if (obj) {
-               ast_odbc_release_obj(obj);
-       }
-       return x;
+
+       ast_odbc_release_obj(obj);
+       return 0;
 }
 
 /*!
  * \brief Gets the number of messages that exist in a mailbox folder.
- * \param context
- * \param mailbox
+ * \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).
  */
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+static int messagecount(const char *mailbox_id, const char *folder)
 {
        struct odbc_obj *obj = NULL;
+       char *context;
+       char *mailbox;
        int nummsgs = 0;
        int res;
        SQLHSTMT stmt = NULL;
@@ -5630,7 +5857,8 @@ static int messagecount(const char *context, const char *mailbox, const char *fo
        struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
 
        /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox)) {
+       if (ast_strlen_zero(mailbox_id)
+               || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
                return 0;
        }
 
@@ -5639,64 +5867,66 @@ static int messagecount(const char *context, const char *mailbox, const char *fo
        }
 
        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;
+       }
+
+       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);
 
-yuck:
-       if (obj)
-               ast_odbc_release_obj(obj);
+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.
  */
-static int has_voicemail(const char *mailbox, const char *folder)
+static int has_voicemail(const char *mailboxes, const char *folder)
 {
-       char tmp[256], *tmp2 = tmp, *box, *context;
-       ast_copy_string(tmp, mailbox, sizeof(tmp));
-       while ((context = box = strsep(&tmp2, ",&"))) {
-               strsep(&context, "@");
-               if (ast_strlen_zero(context))
-                       context = "default";
-               if (messagecount(context, box, folder))
+       char *parse;
+       char *mailbox;
+
+       parse = ast_strdupa(mailboxes);
+       while ((mailbox = strsep(&parse, ",&"))) {
+               if (messagecount(mailbox, folder)) {
                        return 1;
+               }
        }
        return 0;
 }
 #endif
 #ifndef IMAP_STORAGE
-/*! 
+/*!
  * \brief Copies a message from one mailbox to another.
  * \param chan
  * \param vmu
@@ -5778,8 +6008,16 @@ static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int i
 #endif
 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
 
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+static int messagecount(const char *mailbox_id, const char *folder)
 {
+       char *context;
+       char *mailbox;
+
+       if (ast_strlen_zero(mailbox_id)
+               || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
+               return 0;
+       }
+
        return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
 }
 
@@ -5820,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
@@ -5851,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;
@@ -5876,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;
+                                       }
                                }
                        }
                }
@@ -5892,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;
 }
@@ -5934,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);
@@ -5946,7 +6202,7 @@ static void run_externnotify(char *context, char *extension, const char *flag)
                        else if (!strncmp(mwi_msg->cause, "BLK", 3))
                                ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
                        ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
-                       ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+                       ao2_ref(mwi_msg, -1);
                } else {
                        ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
                }
@@ -5983,7 +6239,7 @@ static void generate_msg_id(char *dst)
         * called each time a new msg_id is generated. This should achieve uniqueness,
         * but only in single system solutions.
         */
-       int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
+       unsigned int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
        snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
 }
 
@@ -6044,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;
@@ -6053,14 +6310,14 @@ static int msg_create_from_file(struct ast_vm_recording_data *recdata)
        if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
                if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
                        long framelength = ast_tellstream(recording_fs);
-                       struct ast_format result = {0,};
-                       /* XXX This use of ast_getformatbyname seems incorrect here. The file extension does not necessarily correspond
-                        * to the name of the format. For instance, if "raw" were passed in, I don't think ast_getformatbyname would
-                        * find the slinear format
-                        */
-                       ast_getformatbyname(recdata->recording_ext, &result);
-                       duration = (int) (framelength / ast_format_rate(&result));
+                       int sample_rate = ast_ratestream(recording_fs);
+                       if (sample_rate) {
+                               duration = (int) (framelength / sample_rate);
+                       } else {
+                               ast_log(LOG_ERROR,"Unable to determine sample rate of recording %s\n", recdata->recording_file);
+                       }
                }
+               ast_closestream(recording_fs);
        }
 
        /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
@@ -6268,6 +6525,7 @@ static int msg_create_from_file(struct ast_vm_recording_data *recdata)
                }
 
                STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
+               notify_new_state(recipient);
        }
 
        free_user(recipient);
@@ -6280,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)
@@ -6306,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];
@@ -6358,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.
@@ -6387,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;
        }
@@ -6456,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) {
@@ -6487,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 == '#') {
@@ -6530,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;
        }
@@ -6548,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");
@@ -6566,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;
                }
@@ -6576,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;
                        }
@@ -6622,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));
@@ -6656,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"
@@ -6675,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"),
@@ -6694,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);
@@ -6730,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
@@ -6770,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);
@@ -6840,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);
@@ -6897,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]);
@@ -6906,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;
@@ -6959,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);
@@ -7210,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];
 
@@ -7231,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;
                        }
@@ -7447,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);
-       
+
 }
 
 /*
@@ -7507,7 +7789,7 @@ static int get_folder(struct ast_channel *chan, int start)
                        if (ast_fileexists(fn, NULL, NULL)) {
                                d = vm_play_folder_name(chan, fn);
                        } else {
-                               ast_verb(1, "failed to find %s\n", fn);
+                               ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
                                d = vm_play_folder_name(chan, "vm-INBOX");
                        }
                } else {
@@ -7529,6 +7811,34 @@ static int get_folder(struct ast_channel *chan, int start)
        return d;
 }
 
+/* Japanese Syntax */
+static int get_folder_ja(struct ast_channel *chan, int start)
+{
+        int x;
+        int d;
+        char fn[256];
+        for (x = start; x< 5; x++) {    /* For all folders */
+                if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
+                        return d;
+               }
+               snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x));     /* Folder name */
+               d = vm_play_folder_name(chan, fn);
+               if (d) {
+                        return d;
+               }
+                d = ast_waitfordigit(chan, 500);
+                if (d) {
+                        return d;
+               }
+        }
+        d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
+        if (d) {
+                return d;
+       }
+        d = ast_waitfordigit(chan, 4000);
+        return d;
+}
+
 /*!
  * \brief plays a prompt and waits for a keypress.
  * \param chan
@@ -7536,9 +7846,9 @@ static int get_folder(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)
@@ -7550,7 +7860,12 @@ static int get_folder2(struct ast_channel *chan, char *fn, int start)
        while (((res < '0') || (res > '9')) &&
                        (res != '#') && (res >= 0) &&
                        loops < 4) {
-               res = get_folder(chan, 0);
+                /* res = get_folder(chan, 0); */
+                if (!strcasecmp(ast_channel_language(chan),"ja")) {   /* Japanese syntax */
+                      res = get_folder_ja(chan, 0);
+                } else { /* Default syntax */
+                     res = get_folder(chan, 0);
+               }
                loops++;
        }
        if (loops == 4) { /* give up */
@@ -7572,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.
  *
@@ -7614,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 */
@@ -7623,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;
                        }
@@ -7672,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);
 
@@ -7682,8 +7997,8 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                                char duration_buf[12];
 
                                *duration += prepend_duration;
-                               msg_cat = ast_category_get(msg_cfg, "message");
-                               snprintf(duration_buf, 11, "%ld", *duration);
+                               msg_cat = ast_category_get(msg_cfg, "message", NULL);
+                               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");
                                }
@@ -7691,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';
@@ -7701,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;
 
@@ -7742,13 +8057,11 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
 
 static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
 {
-       char *mailbox, *context;
+       char *mailbox;
+       char *context;
 
-       /* Strip off @default */
-       context = mailbox = ast_strdupa(box);
-       strsep(&context, "@");
-       if (ast_strlen_zero(context)) {
-               context = "default";
+       if (separate_mailbox(ast_strdupa(box), &mailbox, &context)) {
+               return;
        }
 
        ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
@@ -7838,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);
@@ -7862,20 +8175,20 @@ 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):
- *   - retreives the current message to be forwarded
+ *   - retrieves the current message to be forwarded
  *   - copies the original message to a temporary file, so updates to the envelope can be done.
  *   - determines the target mailbox and folders
  *   - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
@@ -7896,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;
@@ -7924,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) {
@@ -7972,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);
 
@@ -7988,7 +8302,6 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                        }
                } else {
                        /* Ask for an extension */
-                       ast_test_suite_event_notify("PLAYBACK", "Message: vm-extension");
                        res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
                        prompt_played++;
                        if (res || prompt_played > 4)
@@ -8005,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))) {
@@ -8038,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;
@@ -8107,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) {
@@ -8118,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);
@@ -8154,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
@@ -8203,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);
@@ -8276,8 +8609,12 @@ 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 */
+               res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
        } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) {     /* DUTCH syntax */
                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
        } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) {     /* NORWEGIAN syntax */
@@ -8433,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 };
@@ -8500,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) {
@@ -8597,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)) {
@@ -8621,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) {
@@ -8634,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. */
@@ -8646,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;
 }
@@ -8752,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;
@@ -8843,6 +9205,19 @@ static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
        }
 }
 
+static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
+{
+        int cmd;
+
+        if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
+                cmd = ast_play_and_wait(chan, box);
+                return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
+        } else {
+                cmd = ast_play_and_wait(chan, box);
+                return cmd;
+        }
+}
+
 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
 {
        int cmd;
@@ -8885,6 +9260,8 @@ static int vm_play_folder_name(struct ast_channel *chan, char *box)
                return vm_play_folder_name_gr(chan, box);
        } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {  /* Hebrew syntax */
                return ast_play_and_wait(chan, box);
+        } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {  /* Japanese syntax */
+                return vm_play_folder_name_ja(chan, box);
        } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
                return vm_play_folder_name_pl(chan, box);
        } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) {  /* Ukrainian syntax */
@@ -8915,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");
@@ -8932,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");
@@ -8941,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:
@@ -8980,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:
@@ -9092,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");
                                }
@@ -9108,7 +9485,46 @@ static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
        }
        return res;
 }
-       
+
+/* Japanese syntax */
+static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
+{
+      /* Introduce messages they have */
+      int res;
+      if (vms->newmessages) {
+              res = ast_play_and_wait(chan, "vm-INBOX");
+              if (!res)
+                      res = ast_play_and_wait(chan, "vm-message");
+              if (!res)
+                      res = ast_play_and_wait(chan, "jp-ga");
+              if (!res)
+                      res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
+              if (vms->oldmessages && !res)
+                      res = ast_play_and_wait(chan, "silence/1");
+
+      }
+      if (vms->oldmessages) {
+              res = ast_play_and_wait(chan, "vm-Old");
+              if (!res)
+                      res = ast_play_and_wait(chan, "vm-message");
+              if (!res)
+                      res = ast_play_and_wait(chan, "jp-ga");
+              if (!res)
+                      res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
+      }
+      if (!vms->oldmessages && !vms->newmessages) {
+              res = ast_play_and_wait(chan, "vm-messages");
+              if (!res)
+                      res = ast_play_and_wait(chan, "jp-wa");
+              if (!res)
+                      res = ast_play_and_wait(chan, "jp-arimasen");
+      }
+      else {
+              res = ast_play_and_wait(chan, "jp-arimasu");
+      }
+      return res;
+} /* Japanese */
+
 /* Default English syntax */
 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
 {
@@ -9124,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");
@@ -9137,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));
@@ -9166,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)
 {
@@ -9289,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");
@@ -9333,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");
@@ -9368,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));
@@ -9377,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)
@@ -9424,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");
@@ -9475,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)
@@ -9521,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));
@@ -9568,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));
@@ -9611,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");
@@ -9654,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 )
  */
 
@@ -9677,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");
@@ -9687,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");
@@ -9698,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");
@@ -9706,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");
@@ -9742,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) {
@@ -9782,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) {
@@ -9798,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)) {
@@ -9830,8 +10315,12 @@ 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 */
+               return vm_intro_ja(chan, vms);
        } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) {  /* DUTCH syntax */
                return vm_intro_nl(chan, vms);
        } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) {  /* NORWEGIAN syntax */
@@ -9945,6 +10434,102 @@ static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu,
        return res;
 }
 
+static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
+{
+        int res = 0;
+        /* Play instructions and wait for new command */
+        while (!res) {
+                if (vms->starting) {
+                        if (vms->lastmsg > -1) {
+                                res = vm_play_folder_name(chan, vms->vmbox);
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "jp-wa");
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "digits/1");
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "jp-wo");
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "silence/1");
+                        }
+                        if (!res)
+                                res = ast_play_and_wait(chan, "vm-opts");
+                } else {
+                        /* Added for additional help */
+                        if (skipadvanced) {
+                                res = vm_play_folder_name(chan, vms->vmbox);
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "jp-wa");
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "digits/1");
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "jp-wo");
+                                if (!res)
+                                        res = ast_play_and_wait(chan, "silence/1");
+                                res = ast_play_and_wait(chan, "vm-opts-full");
+                        }
+                        /* Logic:
+                         * If the current message is not the first OR
+                         * if we're listening to the first new message and there are
+                         * also urgent messages, then prompt for navigation to the
+                         * previous message
+                         */
+                        if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
+                                res = ast_play_and_wait(chan, "vm-prev");
+                        }
+                        if (!res && !skipadvanced)
+                                res = ast_play_and_wait(chan, "vm-advopts");
+                        if (!res)
+                                res = ast_play_and_wait(chan, "vm-repeat");
+                        /* Logic:
+                         * If we're not listening to the last message OR
+                         * we're listening to the last urgent message and there are
+                         * also new non-urgent messages, then prompt for navigation
+                         * to the next message
+                         */
+                        if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
+                                (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
+                                res = ast_play_and_wait(chan, "vm-next");
+                        }
+                        if (!res) {
+                                int curmsg_deleted;
+#ifdef IMAP_STORAGE
+                                ast_mutex_lock(&vms->lock);
+#endif
+                                curmsg_deleted = vms->deleted[vms->curmsg];
+#ifdef IMAP_STORAGE
+                                ast_mutex_unlock(&vms->lock);
+#endif
+                                if (!curmsg_deleted) {
+                                        res = ast_play_and_wait(chan, "vm-delete");
+                                } else {
+                                        res = ast_play_and_wait(chan, "vm-undelete");
+                                }
+                                if (!res) {
+                                        res = ast_play_and_wait(chan, "vm-toforward");
+                                }
+                                if (!res) {
+                                        res = ast_play_and_wait(chan, "vm-savemessage");
+                                }
+                        }
+                }
+
+               if (!res) {
+                       res = ast_play_and_wait(chan, "vm-helpexit");
+               }
+               if (!res)
+                       res = ast_waitfordigit(chan, 6000);
+               if (!res) {
+                       vms->repeats++;
+                       if (vms->repeats > 2) {
+                               res = 't';
+                       }
+               }
+
+       }
+
+        return res;
+}
+
 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
 {
        int res = 0;
@@ -9971,7 +10556,9 @@ static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu,
 
 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
 {
-       if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
+        if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
+                return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent);
+        } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
                return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
        } else {                                        /* Default to ENGLISH */
                return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
@@ -9979,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;
@@ -10004,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;
                }
@@ -10014,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;
                }
@@ -10103,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);
@@ -10205,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
@@ -10245,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:
@@ -10293,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;
@@ -10317,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;
 }
 
@@ -10338,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
@@ -10354,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);
@@ -10366,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
@@ -10392,7 +10979,34 @@ static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms,
        return cmd;
 }
 
-/*! 
+/*!
+ * \brief Japanese syntax for 'You have N messages' greeting.
+ * \param chan
+ * \param vms
+ * \param vmu
+ *
+ * \return zero on success, -1 on error.
+ */
+static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
+{
+        int cmd = 0;
+
+        if (vms->lastmsg > -1) {
+                cmd = play_message(chan, vmu, vms);
+        } else {
+                snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
+                cmd = ast_play_and_wait(chan, vms->fn);
+                if (!cmd)
+                        cmd = ast_play_and_wait(chan, "vm-messages");
+                if (!cmd)
+                        cmd = ast_play_and_wait(chan, "jp-wa");
+                if (!cmd)
+                        cmd = ast_play_and_wait(chan, "jp-arimasen");
+        }
+        return cmd;
+}
+
+/*!
  * \brief Spanish syntax for 'You have N messages' greeting.
  * \param chan
  * \param vms
@@ -10418,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
@@ -10444,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
@@ -10460,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");
@@ -10472,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
@@ -10501,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.
  *
@@ -10517,6 +11131,8 @@ static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, st
                return vm_browse_messages_he(chan, vms, vmu);
        } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) {  /* ITALIAN */
                return vm_browse_messages_it(chan, vms, vmu);
+        } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {  /* JAPANESE */
+                return vm_browse_messages_ja(chan, vms, vmu);
        } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) {  /* PORTUGUESE */
                return vm_browse_messages_pt(chan, vms, vmu);
        } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {  /* VIETNAMESE */
@@ -10533,15 +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);
-       ast_test_suite_event_notify("PLAYBACK", "Message: vm-login");
-       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;
        }
@@ -10558,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] == '*') {
@@ -10576,25 +11191,28 @@ 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 */
                        password[0] = '\0';
                } else {
-                       ast_test_suite_event_notify("PLAYBACK", "Message: %s", vm_password);
                        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 '*' */
@@ -10602,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;
                        }
                }
@@ -10625,31 +11245,34 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                logretries++;
                if (!valid) {
                        if (skipuser || logretries >= max_logins) {
-                               ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect");
                                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 {
-                               ast_test_suite_event_notify("PLAYBACK", "Message: vm-incorrect-mailbox");
                                if (useadsi)
                                        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;
 }
@@ -10665,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
@@ -10675,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 {
@@ -10686,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;
 }
 
@@ -10753,6 +11377,8 @@ play_msg_cleanup:
        }
 #endif
 
+       free_user(vmu);
+
        return res;
 }
 
@@ -10808,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 };
@@ -10822,12 +11448,8 @@ 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));
-
        ast_test_suite_event_notify("START", "Message: vm_execmain started");
        if (ast_channel_state(chan) != AST_STATE_UP) {
                ast_debug(1, "Before ast_answer\n");
@@ -10890,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)++;
                        }
@@ -10958,8 +11580,11 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
 #endif
 
        /* Set language from config to override channel language */
-       if (!ast_strlen_zero(vmu->language))
+       if (!ast_strlen_zero(vmu->language)) {
+               ast_channel_lock(chan);
                ast_channel_language_set(chan, vmu->language);
+               ast_channel_unlock(chan);
+       }
 
        /* Retrieve urgent, old and new message counts */
        ast_debug(1, "Before open_mailbox\n");
@@ -11024,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");
@@ -11416,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");
@@ -11459,6 +12084,22 @@ static int vm_execmain(struct ast_channel *chan, const char *data)
                        break;
                case '*': /* Help */
                        if (!vms.starting) {
+                                if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
+                                        cmd = vm_play_folder_name(chan, vms.vmbox);
+                                        if (!cmd)
+                                                cmd = ast_play_and_wait(chan, "jp-wa");
+                                        if (!cmd)
+                                                cmd = ast_play_and_wait(chan, "digits/1");
+                                        if (!cmd)
+                                                cmd = ast_play_and_wait(chan, "jp-wo");
+                                        if (!cmd)
+                                                cmd = ast_play_and_wait(chan, "silence/1");
+                                        if (!cmd)
+                                                cmd = ast_play_and_wait(chan, "vm-opts");
+                                        if (!cmd)
+                                                cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
+                                        break;
+                                }
                                cmd = ast_play_and_wait(chan, "vm-onefor");
                                if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
                                        cmd = ast_play_and_wait(chan, "vm-for");
@@ -11476,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);
@@ -11498,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) {
@@ -11556,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)
@@ -11624,7 +12271,7 @@ static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *
                return -1;
        }
 
-       cat = ast_category_get(msg_cfg, "message");
+       cat = ast_category_get(msg_cfg, "message", NULL);
        if (!cat) {
                ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
                ast_variables_destroy(var);
@@ -11670,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;
 }
 
@@ -11713,7 +12360,7 @@ static int append_mailbox(const char *context, const char *box, const char *data
                ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
        }
        if (stringp && (s = strsep(&stringp, ","))) {
-               ast_copy_string(vmu->email, s, sizeof(vmu->email));
+               vmu->email = ast_strdup(s);
        }
        if (stringp && (s = strsep(&stringp, ","))) {
                ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
@@ -11734,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|"
@@ -11786,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;
@@ -11926,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);
@@ -11959,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");
 
@@ -11969,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);
@@ -11988,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;
 }
 
@@ -11996,7 +12659,9 @@ static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, ch
 {
        struct ast_vm_user svm;
        struct ast_vm_user *vmu = NULL;
-       char *tmp, *mailbox, *context, *parse;
+       char *parse;
+       char *mailbox;
+       char *context;
        int res = 0;
 
        AST_DECLARE_APP_ARGS(arg,
@@ -12015,23 +12680,19 @@ static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, ch
        parse = ast_strdupa(args);
        AST_STANDARD_APP_ARGS(arg, parse);
 
-       if (ast_strlen_zero(arg.mailbox_context) || ast_strlen_zero(arg.attribute)) {
+       if (ast_strlen_zero(arg.mailbox_context)
+               || ast_strlen_zero(arg.attribute)
+               || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
                ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
                return -1;
        }
 
-       tmp = ast_strdupa(arg.mailbox_context);
-       mailbox = strsep(&tmp, "@");
-       context = strsep(&tmp, "");
-
-       if (ast_strlen_zero(context)) {
-                context = "default";
-       }
-
+       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;
        }
 
@@ -12051,17 +12712,25 @@ static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, ch
                } else if (!strncasecmp(arg.attribute, "tz", 2)) {
                        ast_copy_string(buf, vmu->zonetag, len);
                } else if (!strncasecmp(arg.attribute, "count", 5)) {
+                       char *mailbox_id;
+
+                       mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
+                       sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
+
                        /* If mbxfolder is empty messagecount will default to INBOX */
-                       res = messagecount(context, mailbox, arg.folder);
+                       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;
@@ -12080,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, ",");
@@ -12123,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;
        }
@@ -12164,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)) {
@@ -12191,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;
@@ -12310,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;
 }
 
@@ -12322,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;
@@ -12518,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;
@@ -12529,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);
                }
        }
@@ -12589,14 +13175,18 @@ static void mwi_unsub_event_cb(struct stasis_subscription_change *change)
 static void mwi_sub_event_cb(struct stasis_subscription_change *change)
 {
        struct mwi_sub_task *mwist;
-       char *context = ast_strdupa(stasis_topic_name(change->topic));
+       char *context;
        char *mailbox;
 
-       if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
+       mwist = ast_calloc(1, (sizeof(*mwist)));
+       if (!mwist) {
                return;
        }
 
-       mailbox = strsep(&context, "@");
+       if (separate_mailbox(ast_strdupa(stasis_topic_name(change->topic)), &mailbox, &context)) {
+               ast_free(mwist);
+               return;
+       }
 
        mwist->mailbox = ast_strdup(mailbox);
        mwist->context = ast_strdup(context);
@@ -12669,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");
@@ -12705,15 +13416,64 @@ 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)
 {
        struct ast_vm_user *vmu = NULL;
        const char *id = astman_get_header(m, "ActionID");
-       char actionid[128] = "";
+       char actionid[128];
+       int num_users = 0;
+       int ret;
 
-       if (!ast_strlen_zero(id))
+       actionid[0] = '\0';
+       if (!ast_strlen_zero(id)) {
                snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
+       }
 
        AST_LIST_LOCK(&users);
 
@@ -12722,91 +13482,21 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
                AST_LIST_UNLOCK(&users);
                return RESULT_SUCCESS;
        }
-       
-       astman_send_ack(s, m, "Voicemail user list will follow");
-       
+
+       astman_send_listack(s, m, "Voicemail user list will follow", "start");
+
        AST_LIST_TRAVERSE(&users, vmu, list) {
-               char dirname[256];
+               /* 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;
+       }
 
-#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,
-                       "%s"
-                       "Event: VoicemailUserEntry\r\n"
-                       "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
-                       );
-       }               
-       astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
+       astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
+       astman_send_list_complete_end(s);
 
        AST_LIST_UNLOCK(&users);
 
@@ -12814,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);
@@ -12843,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 == '\\') {
@@ -12940,8 +13630,10 @@ static int actual_load_config(int reload, struct ast_config *cfg, struct ast_con
        const char *val;
        char *q, *stringp, *tmp;
        int x;
-       int tmpadsi[4];
+       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));
@@ -12953,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));
@@ -12971,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";
@@ -12992,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")))
@@ -13004,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 {
@@ -13118,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"))) {
@@ -13229,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);
@@ -13399,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")))
@@ -13429,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";
@@ -13517,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;
@@ -13612,6 +14336,29 @@ static int sayname(struct ast_channel *chan, const char *mailbox, const char *co
        return res;
 }
 
+/*!
+ * \internal
+ * \brief Play a recorded user name for the mailbox to the specified channel.
+ *
+ * \param chan Where to play the recorded name file.
+ * \param mailbox_id The mailbox name.
+ *
+ * \retval 0 Name played without interruption
+ * \retval dtmf ASCII value of the DTMF which interrupted playback.
+ * \retval -1 Unable to locate mailbox or hangup occurred.
+ */
+static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
+{
+       char *context;
+       char *mailbox;
+
+       if (ast_strlen_zero(mailbox_id)
+               || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
+               return -1;
+       }
+       return sayname(chan, mailbox, context);
+}
+
 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
        struct ast_config *pwconf;
        struct ast_flags config_flags = { 0 };
@@ -13665,26 +14412,20 @@ static int write_password_to_file(const char *secretfn, const char *password) {
 static int vmsayname_exec(struct ast_channel *chan, const char *data)
 {
        char *context;
-       char *args_copy;
+       char *mailbox;
        int res;
 
-       if (ast_strlen_zero(data)) {
+       if (ast_strlen_zero(data)
+               || separate_mailbox(ast_strdupa(data), &mailbox, &context)) {
                ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
                return -1;
        }
 
-       args_copy = ast_strdupa(data);
-       if ((context = strchr(args_copy, '@'))) {
-               *context++ = '\0';
-       } else {
-               context = "default";
-       }
-
-       if ((res = sayname(chan, args_copy, context)) < 0) {
-               ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
+       if ((res = sayname(chan, mailbox, context)) < 0) {
+               ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
                res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
                if (!res) {
-                       res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
+                       res = ast_say_character_str(chan, mailbox, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
                }
        }
 
@@ -13711,7 +14452,7 @@ AST_TEST_DEFINE(test_voicemail_vmsayname)
 
        struct ast_channel *test_channel1 = NULL;
        int res = -1;
-       struct ast_format_cap *nativeformats;
+       struct ast_format_cap *capabilities;
 
        static const struct ast_channel_tech fake_tech = {
                .write = fake_write,
@@ -13730,18 +14471,23 @@ AST_TEST_DEFINE(test_voicemail_vmsayname)
                break;
        }
 
-       if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
+       if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
         NULL, NULL, 0, 0, "TestChannel1"))) {
                goto exit_vmsayname_test;
        }
 
        /* normally this is done in the channel driver */
-       ast_format_set(ast_channel_writeformat(test_channel1), AST_FORMAT_GSM, 0);
-       nativeformats = ast_channel_nativeformats(test_channel1);
-       ast_format_cap_add(nativeformats, ast_channel_writeformat(test_channel1));
-       ast_format_set(ast_channel_rawwriteformat(test_channel1), AST_FORMAT_GSM, 0);
-       ast_format_set(ast_channel_readformat(test_channel1), AST_FORMAT_GSM, 0);
-       ast_format_set(ast_channel_rawreadformat(test_channel1), AST_FORMAT_GSM, 0);
+       capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+       if (!capabilities) {
+               goto exit_vmsayname_test;
+       }
+       ast_format_cap_append(capabilities, ast_format_gsm, 0);
+       ast_channel_nativeformats_set(test_channel1, capabilities);
+       ao2_ref(capabilities, -1);
+       ast_channel_set_writeformat(test_channel1, ast_format_gsm);
+       ast_channel_set_rawwriteformat(test_channel1, ast_format_gsm);
+       ast_channel_set_readformat(test_channel1, ast_format_gsm);
+       ast_channel_set_rawreadformat(test_channel1, ast_format_gsm);
        ast_channel_tech_set(test_channel1, &fake_tech);
 
        ast_channel_unlock(test_channel1);
@@ -13760,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))) {
@@ -13844,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");
@@ -13873,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;
                        }
                }
@@ -13890,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]) {
  &