remove the PBX_ODBC logic from the configure script, and add GENERIC_ODCB logic that...
[asterisk/asterisk.git] / apps / app_voicemail.c
index e27f317..04ddf1f 100644 (file)
@@ -45,9 +45,9 @@ c-client (http://www.washington.edu/imap/
  ***/
 
 /*** MAKEOPTS
-<category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
+<category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_voicemail.so apps/app_directory.o apps/app_directory.so">
        <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
-               <depend>unixodbc</depend>
+               <depend>generic_odbc</depend>
                <depend>ltdl</depend>
                <conflict>IMAP_STORAGE</conflict>
                <defaultenabled>no</defaultenabled>
@@ -55,18 +55,14 @@ c-client (http://www.washington.edu/imap/
        <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
                <depend>imap_tk</depend>
                <conflict>ODBC_STORAGE</conflict>
-               <use>ssl</use>
+               <use>openssl</use>
                <defaultenabled>no</defaultenabled>
        </member>
 </category>
  ***/
 
-/* It is important to include the IMAP_STORAGE related headers
- * before asterisk.h since asterisk.h includes logger.h. logger.h
- * and c-client.h have conflicting definitions for AST_LOG_WARNING and
- * AST_LOG_DEBUG, so it's important that we use Asterisk's definitions
- * here instead of the c-client's 
- */
+#include "asterisk.h"
+
 #ifdef IMAP_STORAGE
 #include <ctype.h>
 #include <signal.h>
@@ -86,8 +82,6 @@ c-client (http://www.washington.edu/imap/
 #endif
 #endif
 
-#include "asterisk.h"
-
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
 #include "asterisk/paths.h"    /* use ast_config_AST_SPOOL_DIR */
@@ -97,6 +91,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <time.h>
 #include <dirent.h>
 
+#include "asterisk/logger.h"
 #include "asterisk/lock.h"
 #include "asterisk/file.h"
 #include "asterisk/channel.h"
@@ -120,6 +115,187 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/res_odbc.h"
 #endif
 
+/*** DOCUMENTATION
+       <application name="VoiceMail" language="en_US">
+               <synopsis>
+                       Leave a Voicemail message.
+               </synopsis>
+               <syntax>
+                       <parameter name="mailboxs" argsep="&amp;" required="true">
+                               <argument name="mailbox1" argsep="@" required="true">
+                                       <argument name="mailbox" required="true" />
+                                       <argument name="context" />
+                               </argument>
+                               <argument name="mailbox2" argsep="@" multiple="true">
+                                       <argument name="mailbox" required="true" />
+                                       <argument name="context" />
+                               </argument>
+                       </parameter>
+                       <parameter name="options">
+                               <optionlist>
+                                       <option name="b">
+                                               <para>Play the <literal>busy</literal> greeting to the calling party.</para>
+                                       </option>
+                                       <option name="d">
+                                               <argument name="c" />
+                                               <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
+                                               if played during the greeting. Context defaults to the current context.</para>
+                                       </option>
+                                       <option name="g">
+                                               <argument name="#" required="true" />
+                                               <para>Use the specified amount of gain when recording the voicemail
+                                               message. The units are whole-number decibels (dB). Only works on supported
+                                               technologies, which is DAHDI only.</para>
+                                       </option>
+                                       <option name="s">
+                                               <para>Skip the playback of instructions for leaving a message to the
+                                               calling party.</para>
+                                       </option>
+                                       <option name="u">
+                                               <para>Play the <literal>unavailable</literal> greeting.</para>
+                                       </option>
+                                       <option name="U">
+                                               <para>Mark message as <literal>URGENT</literal>.</para>
+                                       </option>
+                                       <option name="P">
+                                               <para>Mark message as <literal>PRIORITY</literal>.</para>
+                                       </option>
+                               </optionlist>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>This application allows the calling party to leave a message for the specified
+                       list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
+                       the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
+                       exist.</para>
+                       <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
+                       <enumlist>
+                               <enum name="0">
+                                       <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
+                               </enum>
+                               <enum name="*">
+                                       <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
+                               </enum>
+                       </enumlist>
+                       <para>This application will set the following channel variable upon completion:</para>
+                       <variablelist>
+                               <variable name="VMSTATUS">
+                                       <para>This indicates the status of the execution of the VoiceMail application.</para>
+                                       <value name="SUCCESS" />
+                                       <value name="USEREXIT" />
+                                       <value name="FAILED" />
+                               </variable>
+                       </variablelist>
+               </description>
+       </application>
+       <application name="VoiceMailMain" language="en_US">
+               <synopsis>
+                       Check Voicemail messages.
+               </synopsis>
+               <syntax>
+                       <parameter name="mailbox" required="true" argsep="@">
+                               <argument name="mailbox" />
+                               <argument name="context" />
+                       </parameter>
+                       <parameter name="options">
+                               <optionlist>
+                                       <option name="p">
+                                               <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
+                                               the mailbox that is entered by the caller.</para>
+                                       </option>
+                                       <option name="g">
+                                               <argument name="#" required="true" />
+                                               <para>Use the specified amount of gain when recording a voicemail message.
+                                               The units are whole-number decibels (dB).</para>
+                                       </option>
+                                       <option name="s">
+                                               <para>Skip checking the passcode for the mailbox.</para>
+                                       </option>
+                                       <option name="a">
+                                               <argument name="folder" required="true" />
+                                               <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
+                                               Defaults to <literal>INBOX</literal>.</para>
+                                       </option>
+                               </optionlist>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>This application allows the calling party to check voicemail messages. A specific
+                       <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
+                       may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
+                       be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
+                       <literal>default</literal> context will be used.</para>
+               </description>
+       </application>
+       <application name="MailboxExists" language="en_US">
+               <synopsis>
+                       Check to see if Voicemail mailbox exists.
+               </synopsis>
+               <syntax>
+                       <parameter name="mailbox" required="true" argsep="@">
+                               <argument name="mailbox" required="true" />
+                               <argument name="context" />
+                       </parameter>
+                       <parameter name="options">
+                               <para>None options.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
+                       <replaceable>context</replaceable> is specified, the <literal>default</literal> context
+                       will be used.</para>
+                       <para>This application will set the following channel variable upon completion:</para>
+                       <variablelist>
+                               <variable name="VMBOXEXISTSSTATUS">
+                                       <para>This will contain the status of the execution of the MailboxExists application.
+                                       Possible values include:</para>
+                                       <value name="SUCCESS" />
+                                       <value name="FAILED" />
+                               </variable>
+                       </variablelist>
+               </description>
+       </application>
+       <application name="VMAuthenticate" language="en_US">
+               <synopsis>
+                       Authenticate with Voicemail passwords.
+               </synopsis>
+               <syntax>
+                       <parameter name="mailbox" required="true" argsep="@">
+                               <argument name="mailbox" />
+                               <argument name="context" />
+                       </parameter>
+                       <parameter name="options">
+                               <optionlist>
+                                       <option name="s">
+                                               <para>Skip playing the initial prompts.</para>
+                                       </option>
+                               </optionlist>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>This application behaves the same way as the Authenticate application, but the passwords
+                       are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
+                       specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
+                       is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
+                       mailbox.</para>
+               </description>
+       </application>
+       <function name="MAILBOX_EXISTS" language="en_US">
+               <synopsis>
+                       Tell if a mailbox is configured.
+               </synopsis>
+               <syntax argsep="@">
+                       <parameter name="mailbox" required="true" />
+                       <parameter name="context" />
+               </syntax>
+               <description>
+                       <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
+                       If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
+                       context.</para>
+               </description>
+       </function>
+ ***/
+
 #ifdef IMAP_STORAGE
 static char imapserver[48];
 static char imapport[8];
@@ -144,7 +320,7 @@ static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
-static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
+static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
 static void vmstate_insert(struct vm_state *vms);
 static void vmstate_delete(struct vm_state *vms);
@@ -156,6 +332,7 @@ static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pq
 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
 static void update_messages_by_imapuser(const char *user, unsigned long number);
+static int vm_delete(char *file);
 
 static int imap_remove_file (char *dir, int msgnum);
 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
@@ -228,6 +405,7 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
 #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
 
 
@@ -415,6 +593,7 @@ struct vm_zone {
 struct vm_state {
        char curbox[80];
        char username[80];
+       char context[80];
        char curdir[PATH_MAX];
        char vmbox[PATH_MAX];
        char fn[PATH_MAX];
@@ -498,77 +677,6 @@ static char userscontext[AST_MAX_EXTENSION] = "default";
 
 static char *addesc = "Comedian Mail";
 
-static char *synopsis_vm = "Leave a Voicemail message";
-
-static char *descrip_vm =
-       "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
-       "application allows the calling party to leave a message for the specified\n"
-       "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
-       "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
-       "specified mailbox does not exist.\n"
-       "  The Voicemail application will exit if any of the following DTMF digits are\n"
-       "received:\n"
-       "    0 - Jump to the 'o' extension in the current dialplan context.\n"
-       "    * - Jump to the 'a' extension in the current dialplan context.\n"
-       "  This application will set the following channel variable upon completion:\n"
-       "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
-       "               application. The possible values are:\n"
-       "               SUCCESS | USEREXIT | FAILED\n\n"
-       "  Options:\n"
-       "    b    - Play the 'busy' greeting to the calling party.\n"
-       "    d([c]) - Accept digits for a new extension in context c, if played during\n"
-       "             the greeting.  Context defaults to the current context.\n"
-       "    g(#) - Use the specified amount of gain when recording the voicemail\n"
-       "           message. The units are whole-number decibels (dB).\n"
-       "           Only works on supported technologies, which is DAHDI only.\n"
-       "    s    - Skip the playback of instructions for leaving a message to the\n"
-       "           calling party.\n"
-       "    u    - Play the 'unavailable' greeting.\n"
-       "    U    - Mark message as Urgent.\n"
-       "    P    - Mark message as PRIORITY.\n";
-
-static char *synopsis_vmain = "Check Voicemail messages";
-
-static char *descrip_vmain =
-       "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
-       "calling party to check voicemail messages. A specific mailbox, and optional\n"
-       "corresponding context, may be specified. If a mailbox is not provided, the\n"
-       "calling party will be prompted to enter one. If a context is not specified,\n"
-       "the 'default' context will be used.\n\n"
-       "  Options:\n"
-       "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
-       "           is entered by the caller.\n"
-       "    g(#) - Use the specified amount of gain when recording a voicemail\n"
-       "           message. The units are whole-number decibels (dB).\n"
-       "    s    - Skip checking the passcode for the mailbox.\n"
-       "    a(#) - Skip folder prompt and go directly to folder specified.\n"
-       "           Defaults to INBOX\n";
-
-static char *synopsis_vm_box_exists =
-"Check to see if Voicemail mailbox exists";
-
-static char *descrip_vm_box_exists =
-       "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
-       "mailbox exists. If no voicemail context is specified, the 'default' context\n"
-       "will be used.\n"
-       "  This application will set the following channel variable upon completion:\n"
-       "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
-       "                        MailboxExists application. Possible values include:\n"
-       "                        SUCCESS | FAILED\n\n"
-       "  Options: (none)\n";
-
-static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
-
-static char *descrip_vmauthenticate =
-       "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
-       "same way as the Authenticate application, but the passwords are taken from\n"
-       "voicemail.conf.\n"
-       "  If the mailbox is specified, only that mailbox's password will be considered\n"
-       "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
-       "be set with the authenticated mailbox.\n\n"
-       "  Options:\n"
-       "    s - Skip playing the initial prompts.\n";
-
 /* Leave a message */
 static char *app = "VoiceMail";
 
@@ -580,6 +688,7 @@ static char *app4 = "VMAuthenticate";
 
 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
 static AST_LIST_HEAD_STATIC(zones, vm_zone);
+static char zonetag[80];
 static int maxsilence;
 static int maxmsg;
 static int maxdeletedmsg;
@@ -737,6 +846,7 @@ static void populate_defaults(struct ast_vm_user *vmu)
        ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
        ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
        ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
+       ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
        if (vmmaxsecs)
                vmu->maxsecs = vmmaxsecs;
        if (maxmsg)
@@ -770,7 +880,7 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
 #ifdef IMAP_STORAGE
        } else if (!strcasecmp(var, "imapuser")) {
                ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
-       } else if (!strcasecmp(var, "imappassword")) {
+       } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
                ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
 #endif
        } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
@@ -869,7 +979,9 @@ static char *vm_check_password_shell(char *command, char *buf, size_t len)
                } else if (pid) {
                        /* parent */
                        close(fds[1]);
-                       read(fds[0], buf, len);
+                       if (read(fds[0], buf, len) < 0) {
+                               ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
+                       }
                        close(fds[0]);
                } else {
                        /*  child */
@@ -945,7 +1057,7 @@ static int change_password_realtime(struct ast_vm_user *vmu, const char *passwor
                if (strlen(password) > 10) {
                        ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
                }
-               res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, SENTINEL);
+               res = ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL);
                if (res > 0) {
                        ast_copy_string(vmu->password, password, sizeof(vmu->password));
                        res = 0;
@@ -981,34 +1093,31 @@ static void apply_options(struct ast_vm_user *vmu, const char *options)
  */
 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
 {
-       struct ast_variable *tmp;
-       tmp = var;
-       while (tmp) {
-               if (!strcasecmp(tmp->name, "vmsecret")) {
-                       ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
-               } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
+       for (; var; var = var->next) {
+               if (!strcasecmp(var->name, "vmsecret")) {
+                       ast_copy_string(retval->password, var->value, sizeof(retval->password));
+               } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
                        if (ast_strlen_zero(retval->password))
-                               ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
-               } else if (!strcasecmp(tmp->name, "uniqueid")) {
-                       ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
-               } else if (!strcasecmp(tmp->name, "pager")) {
-                       ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
-               } else if (!strcasecmp(tmp->name, "email")) {
-                       ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
-               } else if (!strcasecmp(tmp->name, "fullname")) {
-                       ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
-               } else if (!strcasecmp(tmp->name, "context")) {
-                       ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
+                               ast_copy_string(retval->password, var->value, sizeof(retval->password));
+               } else if (!strcasecmp(var->name, "uniqueid")) {
+                       ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
+               } 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));
+               } else if (!strcasecmp(var->name, "fullname")) {
+                       ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
+               } else if (!strcasecmp(var->name, "context")) {
+                       ast_copy_string(retval->context, var->value, sizeof(retval->context));
 #ifdef IMAP_STORAGE
-               } else if (!strcasecmp(tmp->name, "imapuser")) {
-                       ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
-               } else if (!strcasecmp(tmp->name, "imappassword")) {
-                       ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
+               } else if (!strcasecmp(var->name, "imapuser")) {
+                       ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
+               } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
+                       ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
 #endif
                } else
-                       apply_option(retval, tmp->name, tmp->value);
-               tmp = tmp->next;
-       } 
+                       apply_option(retval, var->name, var->value);
+       }
 }
 
 /*!
@@ -1156,7 +1265,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                return;
 
        /* check voicemail.conf */
-       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
+       if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
                while ((category = ast_category_browse(cfg, category))) {
                        if (!strcasecmp(category, vmu->context)) {
                                if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
@@ -1180,13 +1289,13 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                /* save the results */
                reset_user_pw(vmu->context, vmu->mailbox, newpassword);
                ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
-               config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
+               ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
        }
        category = NULL;
        var = NULL;
        /* check users.conf and update the password stored for the mailbox*/
        /* if no vmsecret entry exists create one. */
-       if ((cfg = ast_config_load("users.conf", config_flags))) {
+       if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
                ast_debug(4, "we are looking for %s\n", vmu->mailbox);
                while ((category = ast_category_browse(cfg, category))) {
                        ast_debug(4, "users.conf: %s\n", category);
@@ -1210,7 +1319,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                /* save the results and clean things up */
                reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
                ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
-               config_text_file_save("users.conf", cfg, "AppVoicemail");
+               ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
        }
 }
 
@@ -1312,7 +1421,7 @@ static const char *mbox(int id)
                "Deleted",
                "Urgent"
        };
-       return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
+       return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
 }
 
 static void free_user(struct ast_vm_user *vmu)
@@ -1335,7 +1444,7 @@ static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
                return;
        }
 
-       if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
+       if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
                ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
                return;
        }
@@ -1351,7 +1460,9 @@ static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
                ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
        /* delete message */
        snprintf (arg, sizeof(arg), "%lu",messageNum);
+       ast_mutex_lock(&vms->lock);
        mail_setflag (vms->mailstream,arg,"\\DELETED");
+       ast_mutex_unlock(&vms->lock);
 }
 
 static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
@@ -1378,17 +1489,19 @@ 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, 1)) ||!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
+       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))) {
                ast_log(AST_LOG_ERROR, "Voicemail state not found!\n");
                return -1;
        }
        
        /* Greetings will never have a prepended message */
        *vms_p->introfn = '\0';
-       
+
+       ast_mutex_lock(&vms_p->lock);
        ret = init_mailstream(vms_p, GREETINGS_FOLDER);
        if (!vms_p->mailstream) {
                ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
+               ast_mutex_unlock(&vms_p->lock);
                return -1;
        }
 
@@ -1400,6 +1513,7 @@ 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;
                }
                filename = strsep(&attachment, ".");
@@ -1407,9 +1521,11 @@ static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast
                        ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
                        vms_p->msgArray[vms_p->curmsg] = i + 1;
                        save_body(body, vms_p, "2", attachment, 0);
+                       ast_mutex_unlock(&vms_p->lock);
                        return 0;
                }
        }
+       ast_mutex_unlock(&vms_p->lock);
 
        return -1;
 }
@@ -1444,7 +1560,7 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
        /* Before anything can happen, we need a vm_state so that we can
         * actually access the imap server through the vms->mailstream
         */
-       if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
+       if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
                /* This should not happen. If it does, then I guess we'd
                 * need to create the vm_state, extract which mailbox to
                 * open, and then set up the msgArray so that the correct
@@ -1475,7 +1591,9 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
        }
 
        /* This will only work for new messages... */
+       ast_mutex_lock(&vms->lock);
        header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
+       ast_mutex_unlock(&vms->lock);
        /* empty string means no valid header */
        if (ast_strlen_zero(header_content)) {
                ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
@@ -1483,12 +1601,13 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
                goto exit;
        }
 
+       ast_mutex_lock(&vms->lock);
        mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
-       
+       ast_mutex_unlock(&vms->lock);
+
        /* We have the body, now we extract the file name of the first attachment. */
        if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
                attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
-               ast_log(LOG_NOTICE, "%s is the attached file\n", attachedfilefmt);
        } else {
                ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
                res = -1;
@@ -1506,7 +1625,6 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
        
        save_body(body, vms, "2", attachedfilefmt, 0);
        if (save_body(body, vms, "3", attachedfilefmt, 1)) {
-               ast_log(LOG_NOTICE, "Nulling the introfn cuz ain't nothing in part 3\n");
                *vms->introfn = '\0';
        }
 
@@ -1519,9 +1637,9 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
 
        fprintf(text_file_ptr, "%s\n", "[message]");
 
-       get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
-       fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
        get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
+       fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
+       get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
        fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
        get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
        fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
@@ -1531,6 +1649,8 @@ static int imap_retrieve_file(const char *dir, const int msgnum, const char *mai
        fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
        get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
        fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
+       get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
+       fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
        fclose(text_file_ptr);
 
 exit:
@@ -1591,6 +1711,12 @@ static int messagecount(const char *context, const char *mailbox, const char *fo
        int fold = folder_int(folder);
        int urgent = 0;
        
+       /* If URGENT, then look at INBOX */
+       if (fold == 11) {
+               fold = NEW_FOLDER;
+               urgent = 1;
+       }
+
        if (ast_strlen_zero(mailbox))
                return 0;
 
@@ -1617,7 +1743,7 @@ static int messagecount(const char *context, const char *mailbox, const char *fo
        /* check if someone is accessing this box right now... */
        vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
        if (!vms_p) {
-               vms_p = get_vm_state_by_mailbox(mailbox,1);
+               vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
        }
        if (vms_p) {
                ast_debug(3, "Returning before search - user is logged in\n");
@@ -1635,28 +1761,11 @@ static int messagecount(const char *context, const char *mailbox, const char *fo
        /* add one if not there... */
        vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
        if (!vms_p) {
-               vms_p = get_vm_state_by_mailbox(mailbox,0);
-       }
-
-       /* If URGENT, then look at INBOX */
-       if (fold == 11) {
-               fold = NEW_FOLDER;
-               urgent = 1;
+               vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
        }
 
        if (!vms_p) {
-               ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
-               if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
-                       return -1;
-               }
-               ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
-               ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
-               vms_p->mailstream = NIL; /* save for access from interactive entry point */
-               ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
-               vms_p->updated = 1;
-               ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
-               init_vm_state(vms_p);
-               vmstate_insert(vms_p);
+               vms_p = create_vm_state_from_user(vmu);
        }
        ret = init_mailstream(vms_p, fold);
        if (!vms_p->mailstream) {
@@ -1664,6 +1773,7 @@ static int messagecount(const char *context, const char *mailbox, const char *fo
                return -1;
        }
        if (ret == 0) {
+               ast_mutex_lock(&vms_p->lock);
                pgm = mail_newsearchpgm ();
                hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
                pgm->header = hdr;
@@ -1696,10 +1806,13 @@ static int messagecount(const char *context, const char *mailbox, const char *fo
                        vms_p->urgentmessages = vms_p->vmArrayIndex;
                /*Freeing the searchpgm also frees the searchhdr*/
                mail_free_searchpgm(&pgm);
+               ast_mutex_unlock(&vms_p->lock);
                vms_p->updated = 0;
                return vms_p->vmArrayIndex;
-       } else {  
+       } else {
+               ast_mutex_lock(&vms_p->lock);
                mail_ping(vms_p->mailstream);
+               ast_mutex_unlock(&vms_p->lock);
        }
        return 0;
 }
@@ -1718,12 +1831,12 @@ static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, i
        int tempcopy = 0;
        STRING str;
        int ret; /* for better error checking */
-       char *imapflags = NIL;
+       char *imap_flags = NIL;
 
        /* Set urgent flag for IMAP message */
        if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
                ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
-               imapflags="\\FLAGGED";
+               imap_flags="\\FLAGGED";
        }
        
        /* Attach only the first format */
@@ -1786,14 +1899,21 @@ static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, i
                        *(vmu->email) = '\0';
                return -1;
        }
-       fread(buf, len, 1, p);
+       if (fread(buf, len, 1, p) < len) {
+               if (ferror(p)) {
+                       ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
+                       return -1;
+               }
+       }
        ((char *)buf)[len] = '\0';
        INIT(&str, mail_string, buf, len);
        ret = init_mailstream(vms, NEW_FOLDER);
        if (ret == 0) {
                imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
-               if(!mail_append_full(vms->mailstream, mailbox, imapflags, NIL, &str))
+               ast_mutex_lock(&vms->lock);
+               if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
                        ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
+               ast_mutex_unlock(&vms->lock);
                fclose(p);
                unlink(tmp);
                ast_free(buf);
@@ -1907,13 +2027,13 @@ static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
 
 static int has_voicemail(const char *mailbox, const char *folder)
 {
-       char tmp[256], *tmp2, *mbox, *context;
+       char tmp[256], *tmp2, *box, *context;
        ast_copy_string(tmp, mailbox, sizeof(tmp));
        tmp2 = tmp;
        if (strchr(tmp2, ',')) {
-               while ((mbox = strsep(&tmp2, ","))) {
-                       if (!ast_strlen_zero(mbox)) {
-                               if (has_voicemail(mbox, folder))
+               while ((box = strsep(&tmp2, ","))) {
+                       if (!ast_strlen_zero(box)) {
+                               if (has_voicemail(box, folder))
                                        return 1;
                        }
                }
@@ -1957,8 +2077,12 @@ static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int i
                return -1;
        }
        snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
-       if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
+       ast_mutex_lock(&sendvms->lock);
+       if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
+               ast_mutex_unlock(&sendvms->lock);
                return 0;
+       }
+       ast_mutex_unlock(&sendvms->lock);
        ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
        return -1;
 }
@@ -2094,6 +2218,7 @@ static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
                check_quota(vms,(char *)mbox(box));
        }
 
+       ast_mutex_lock(&vms->lock);
        pgm = mail_newsearchpgm();
 
        /* Check IMAP folder for Asterisk messages only... */
@@ -2125,16 +2250,20 @@ static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
        vms->lastmsg = vms->vmArrayIndex - 1;
        mail_free_searchpgm(&pgm);
 
+       ast_mutex_unlock(&vms->lock);
        return 0;
 }
 
 static void write_file(char *filename, char *buffer, unsigned long len)
 {
        FILE *output;
-       ast_log(LOG_NOTICE, "The name of the file I'm writing is %s\n", filename);
 
        output = fopen (filename, "w");
-       fwrite (buffer, len, 1, output);
+       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));
+               }
+       }
        fclose (output);
 }
 
@@ -2244,9 +2373,9 @@ void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
 }
 
 
-void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
+void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
 {
-       ast_debug(5, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
+       ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
        if (attributes & LATT_NOINFERIORS)
                ast_debug(5, "no inferiors\n");
        if (attributes & LATT_NOSELECT)
@@ -2431,6 +2560,7 @@ static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
                return NULL;
        ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
        ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
+       ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
        vms_p->mailstream = NIL; /* save for access from interactive entry point */
        if (option_debug > 4)
                ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
@@ -2469,10 +2599,11 @@ static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
        return NULL;
 }
 
-static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
+static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
 {
 
        struct vmstate *vlist = NULL;
+       const char *local_context = S_OR(context, "default");
 
        AST_LIST_LOCK(&vmstates);
        AST_LIST_TRAVERSE(&vmstates, vlist, list) {
@@ -2480,14 +2611,14 @@ static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interac
                        ast_debug(3, "error: vms is NULL for %s\n", mailbox);
                        continue;
                }
-               if (!vlist->vms->username) {
+               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 (i=%d) to vmstate mailbox %s (i=%d)\n", mailbox, interactive, vlist->vms->username, vlist->vms->interactive);
+               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);
                
-               if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
+               if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
                        ast_debug(3, "Found it!\n");
                        AST_LIST_UNLOCK(&vmstates);
                        return vlist->vms;
@@ -2509,7 +2640,7 @@ static void vmstate_insert(struct vm_state *vms)
           use the one we already have since it is more up to date.
           We can compare the username to find the duplicate */
        if (vms->interactive == 1) {
-               altvms = get_vm_state_by_mailbox(vms->username,0);
+               altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
                if (altvms) {   
                        ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
                        vms->newmessages = altvms->newmessages;
@@ -2610,7 +2741,9 @@ static int save_body(BODY *body, struct vm_state *vms, char *section, char *form
        if (!body || body == NIL)
                return -1;
 
+       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) {
                snprintf(filename, sizeof(filename), "%s.%s", fn, format);
                /* ast_debug(1,body_content); */
@@ -2633,6 +2766,7 @@ static int save_body(BODY *body, struct vm_state *vms, char *section, char *form
  *
  * Determines the delimiter character that is used by the underlying IMAP based mail store.
  */
+/* MUTEX should already be held */
 static void get_mailbox_delimiter(MAILSTREAM *stream) {
        char tmp[50];
        snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
@@ -2647,6 +2781,7 @@ static void get_mailbox_delimiter(MAILSTREAM *stream) {
  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
  */
 static void check_quota(struct vm_state *vms, char *mailbox) {
+       ast_mutex_lock(&vms->lock);
        mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
        ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
        if (vms && vms->mailstream != NULL) {
@@ -2654,6 +2789,7 @@ static void check_quota(struct vm_state *vms, char *mailbox) {
        } else {
                ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
        }
+       ast_mutex_unlock(&vms->lock);
 }
 
 #endif /* IMAP_STORAGE */
@@ -2844,7 +2980,9 @@ static int retrieve_file(char *dir, int msgnum)
                                                        }
                                                }
                                        }
-                                       truncate(full_fn, fdlen);
+                                       if (truncate(full_fn, fdlen) < 0) {
+                                               ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
+                                       }
                                }
                        } else {
                                res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
@@ -3181,7 +3319,7 @@ static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int ms
                        res = -1;
                        break;
                }
-               if (cfg) {
+               if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
                        if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
                                idata.context = "";
                        }
@@ -3280,6 +3418,33 @@ static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxco
        return; 
 }
 
+/*!
+ * \brief Removes a voicemail message file.
+ * \param dir the path to the message file.
+ * \param msgnum the unique number for the message within the mailbox.
+ *
+ * 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. 
+ * 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.
+ */
+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);       
+       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+       unlink(full_fn);
+       return 0;
+}
 #else
 #ifndef IMAP_STORAGE
 /*!
@@ -3371,37 +3536,10 @@ static int last_message_index(struct ast_vm_user *vmu, char *dir)
 
        return x - 1;
 }
-#if (defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
-/*!
- * \brief Removes a voicemail message file.
- * \param dir the path to the message file.
- * \param msgnum the unique number for the message within the mailbox.
- *
- * 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. 
- * 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.
- */
-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);       
-       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-       unlink(full_fn);
-       return 0;
-}
-#endif
-
 
+#endif /* #ifndef IMAP_STORAGE */
+#endif /* #else of #ifdef ODBC_STORAGE */
+#ifndef IMAP_STORAGE
 /*!
  * \brief Utility function to copy a file.
  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
@@ -3512,10 +3650,8 @@ static void copy_plain_file(char *frompath, char *topath)
        copy(frompath2, topath2);
        ast_variables_destroy(var);
 }
+#endif
 
-#endif /* #ifndef IMAP_STORAGE */
-#endif /* #else of #ifdef ODBC_STORAGE */
-#if (!defined(ODBC_STORAGE) && !defined(IMAP_STORAGE))
 /*! 
  * \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.
@@ -3541,7 +3677,6 @@ static int vm_delete(char *file)
        unlink(txt);
        return ast_filedelete(file, NULL);
 }
-#endif
 
 /*!
  * \brief utility used by inchar(), for base_encode()
@@ -3678,9 +3813,10 @@ static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu
        pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
        pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
        pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
-       pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
-       pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
-       pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
+       pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
+               ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
+       pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
+       pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
        pbx_builtin_setvar_helper(ast, "VM_DATE", date);
        pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
        pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
@@ -3733,6 +3869,70 @@ static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm
        return tm;
 }
 
+/*!\brief Check if the string would need encoding within the MIME standard, to
+ * avoid confusing certain mail software that expects messages to be 7-bit
+ * clean.
+ */
+static int check_mime(const char *str)
+{
+       for (; *str; str++) {
+               if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+/*!\brief Encode a string according to the MIME rules for encoding strings
+ * that are not 7-bit clean or contain control characters.
+ *
+ * Additionally, if the encoded string would exceed the MIME limit of 76
+ * characters per line, then the encoding will be broken up into multiple
+ * sections, separated by a space character, in order to facilitate
+ * breaking up the associated header across multiple lines.
+ *
+ * \param start A string to be encoded
+ * \param end An expandable buffer for holding the result
+ * \param preamble The length of the first line already used for this string,
+ * to ensure that each line maintains a maximum length of 76 chars.
+ * \param postamble the length of any additional characters appended to the
+ * line, used to ensure proper field wrapping.
+ * \retval The encoded string.
+ */
+static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
+{
+       char tmp[80];
+       int first_section = 1;
+       size_t endlen = 0, tmplen = 0;
+       *end = '\0';
+
+       tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
+       for (; *start; start++) {
+               int need_encoding = 0;
+               if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
+                       need_encoding = 1;
+               }
+               if ((first_section && need_encoding && preamble + tmplen > 70) ||
+                       (first_section && !need_encoding && preamble + tmplen > 72) ||
+                       (!first_section && need_encoding && tmplen > 70) ||
+                       (!first_section && !need_encoding && tmplen > 72)) {
+                       /* Start new line */
+                       endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
+                       tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
+                       first_section = 0;
+               }
+               if (need_encoding && *start == ' ') {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
+               } else if (need_encoding) {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
+               } else {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
+               }
+       }
+       snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
+       return end;
+}
+
 /*!
  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
  * \param p The output file to generate the email contents into.
@@ -3762,8 +3962,8 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
        char dur[256];
        struct ast_tm tm;
        char enc_cidnum[256] = "", enc_cidname[256] = "";
-       char *passdata2;
-       size_t len_passdata;
+       char *passdata = NULL, *passdata2;
+       size_t len_passdata = 0, len_passdata2, tmplen;
        char *greeting_attachment; 
        char filename[256];
 
@@ -3773,6 +3973,17 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
 #define ENDL "\n"
 #endif
 
+       /* One alloca for multiple fields */
+       len_passdata2 = strlen(vmu->fullname);
+       if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
+               len_passdata2 = tmplen;
+       }
+       if ((tmplen = strlen(fromstring)) > len_passdata2) {
+               len_passdata2 = tmplen;
+       }
+       len_passdata2 = len_passdata2 * 3 + 200;
+       passdata2 = alloca(len_passdata2);
+
        if (cidnum) {
                strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
        }
@@ -3799,16 +4010,26 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
 
        if (!ast_strlen_zero(fromstring)) {
                struct ast_channel *ast;
-               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
-                       char *passdata;
-                       int vmlen = strlen(fromstring)*3 + 200;
-                       passdata = alloca(vmlen);
-                       memset(passdata, 0, vmlen);
-                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category, flag);
-                       pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
-                       len_passdata = strlen(passdata) * 2 + 3;
-                       passdata2 = alloca(len_passdata);
-                       fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
+                       char *ptr;
+                       memset(passdata2, 0, len_passdata2);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
+                       pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
+                       len_passdata = strlen(passdata2) * 3 + 300;
+                       passdata = alloca(len_passdata);
+                       if (check_mime(passdata2)) {
+                               int first_line = 1;
+                               encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
+                               while ((ptr = strchr(passdata, ' '))) {
+                                       *ptr = '\0';
+                                       fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
+                                       first_line = 0;
+                                       passdata = ptr + 1;
+                               }
+                               fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
+                       } else {
+                               fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
+                       }
                        ast_channel_free(ast);
                } else {
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
@@ -3816,19 +4037,48 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
        } else {
                fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
        }
-       len_passdata = strlen(vmu->fullname) * 2 + 3;
-       passdata2 = alloca(len_passdata);
-       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
+
+       if (check_mime(vmu->fullname)) {
+               int first_line = 1;
+               char *ptr;
+               encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
+               while ((ptr = strchr(passdata2, ' '))) {
+                       *ptr = '\0';
+                       fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
+                       first_line = 0;
+                       passdata2 = ptr + 1;
+               }
+               fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
+       } else {
+               fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
+       }
        if (!ast_strlen_zero(emailsubject)) {
                struct ast_channel *ast;
-               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
-                       char *passdata;
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
                        int vmlen = strlen(emailsubject) * 3 + 200;
-                       passdata = alloca(vmlen);
-                       memset(passdata, 0, vmlen);
-                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category, flag);
-                       pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
-                       fprintf(p, "Subject: %s" ENDL, passdata);
+                       /* Only allocate more space if the previous was not large enough */
+                       if (vmlen > len_passdata) {
+                               passdata = alloca(vmlen);
+                               len_passdata = vmlen;
+                       }
+
+                       memset(passdata, 0, len_passdata);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
+                       pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
+                       if (check_mime(passdata)) {
+                               int first_line = 1;
+                               char *ptr;
+                               encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
+                               while ((ptr = strchr(passdata2, ' '))) {
+                                       *ptr = '\0';
+                                       fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
+                                       first_line = 0;
+                                       passdata2 = ptr + 1;
+                               }
+                               fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
+                       } else {
+                               fprintf(p, "Subject: %s" ENDL, passdata);
+                       }
                        ast_channel_free(ast);
                } else {
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
@@ -3889,7 +4139,7 @@ static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, in
        fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
        if (emailbody) {
                struct ast_channel *ast;
-               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
                        char *passdata;
                        int vmlen = strlen(emailbody)*3 + 200;
                        passdata = alloca(vmlen);
@@ -4033,7 +4283,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
 
        if (*pagerfromstring) {
                struct ast_channel *ast;
-               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
                        char *passdata;
                        int vmlen = strlen(fromstring)*3 + 200;
                        passdata = alloca(vmlen);
@@ -4049,7 +4299,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
        fprintf(p, "To: %s\n", pager);
        if (pagersubject) {
                struct ast_channel *ast;
-               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
                        char *passdata;
                        int vmlen = strlen(pagersubject) * 3 + 200;
                        passdata = alloca(vmlen);
@@ -4071,7 +4321,7 @@ static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char
        ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
        if (pagerbody) {
                struct ast_channel *ast;
-               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
                        char *passdata;
                        int vmlen = strlen(pagerbody) * 3 + 200;
                        passdata = alloca(vmlen);
@@ -4343,13 +4593,13 @@ yuck:
  */
 static int has_voicemail(const char *mailbox, const char *folder)
 {
-       char tmp[256], *tmp2 = tmp, *mbox, *context;
+       char tmp[256], *tmp2 = tmp, *box, *context;
        ast_copy_string(tmp, mailbox, sizeof(tmp));
-       while ((context = mbox = strsep(&tmp2, ","))) {
+       while ((context = box = strsep(&tmp2, ","))) {
                strsep(&context, "@");
                if (ast_strlen_zero(context))
                        context = "default";
-               if (messagecount(context, mbox, folder))
+               if (messagecount(context, box, folder))
                        return 1;
        }
        return 0;
@@ -4399,7 +4649,18 @@ static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int i
        recipmsgnum = last_message_index(recip, todir) + 1;
        if (recipmsgnum < recip->maxmsg) {
                make_file(topath, sizeof(topath), todir, recipmsgnum);
-               COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
+               if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
+                       COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
+               } else {
+                       /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
+                        * copy will fail. Instead, we need to create a local copy, store it, and delete the local
+                        * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
+                        * much worse problem happening and IMAP storage doesn't call this function
+                        */
+                       copy_plain_file(frompath, topath);
+                       STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
+                       vm_delete(topath);
+               }
        } else {
                ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
        }
@@ -4470,14 +4731,14 @@ static int __has_voicemail(const char *context, const char *mailbox, const char
  */
 static int has_voicemail(const char *mailbox, const char *folder)
 {
-       char tmp[256], *tmp2 = tmp, *mbox, *context;
+       char tmp[256], *tmp2 = tmp, *box, *context;
        ast_copy_string(tmp, mailbox, sizeof(tmp));
-       while ((mbox = strsep(&tmp2, ","))) {
-               if ((context = strchr(mbox, '@')))
+       while ((box = strsep(&tmp2, ","))) {
+               if ((context = strchr(box, '@')))
                        *context++ = '\0';
                else
                        context = "default";
-               if (__has_voicemail(context, mbox, folder, 1))
+               if (__has_voicemail(context, box, folder, 1))
                        return 1;
        }
        return 0;
@@ -4630,8 +4891,6 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
        int ausemacro = 0;
        int ousemacro = 0;
        int ouseexten = 0;
-       int rtmsgid = 0;
-       char tmpid[16];
        char tmpdur[16];
        char priority[16];
        char origtime[16];
@@ -4857,7 +5116,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                        ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
                        return -1;
                }
-               if (!(vms = get_vm_state_by_mailbox(ext,0))) {
+               if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
                /* It is possible under certain circumstances that inboxcount did not
                 * create a vm_state when it was needed. This is a catchall which will
                 * rarely be used.
@@ -4924,7 +5183,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                        snprintf(priority, sizeof(priority), "%d", chan->priority);
                        snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
                        get_date(date, sizeof(date));
-                       rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), SENTINEL);
+                       ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), "filename", tmptxtfile, SENTINEL);
                }
 
                /* Store information */
@@ -4968,8 +5227,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                ast_filedelete(tmptxtfile, NULL);
                                unlink(tmptxtfile);
                                if (ast_check_realtime("voicemail_data")) {
-                                       snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
-                                       ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
+                                       ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
                                }
                        } else {
                                fprintf(txt, "duration=%d\n", duration);
@@ -4984,8 +5242,7 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                        unlink(tmptxtfile);
                                        ast_unlock_path(dir);
                                        if (ast_check_realtime("voicemail_data")) {
-                                               snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
-                                               ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
+                                               ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
                                        }
                                } else {
 #ifndef IMAP_STORAGE
@@ -5011,9 +5268,8 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
 
                                        ast_unlock_path(dir);
                                        if (ast_check_realtime("voicemail_data")) {
-                                               snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
                                                snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
-                                               ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, SENTINEL);
+                                               ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
                                        }
                                        /* We must store the file first, before copying the message, because
                                         * ODBC storage does the entire copy with SQL.
@@ -5025,15 +5281,15 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                                        /* Are there to be more recipients of this message? */
                                        while (tmpptr) {
                                                struct ast_vm_user recipu, *recip;
-                                               char *exten, *context;
+                                               char *exten, *cntx;
                                        
                                                exten = strsep(&tmpptr, "&");
-                                               context = strchr(exten, '@');
-                                               if (context) {
-                                                       *context = '\0';
-                                                       context++;
+                                               cntx = strchr(exten, '@');
+                                               if (cntx) {
+                                                       *cntx = '\0';
+                                                       cntx++;
                                                }
-                                               if ((recip = find_user(&recipu, context, exten))) {
+                                               if ((recip = find_user(&recipu, cntx, exten))) {
                                                        copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
                                                        free_user(recip);
                                                }
@@ -5083,6 +5339,21 @@ static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_
                ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
 leave_vm_out:
        free_user(vmu);
+
+#ifdef IMAP_STORAGE
+       /* expunge message - use UID Expunge if supported on IMAP server*/
+       ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
+       if (expungeonhangup == 1) {
+               ast_mutex_lock(&vms->lock);
+#ifdef HAVE_IMAP_TK2006
+               if (LEVELUIDPLUS (vms->mailstream)) {
+                       mail_expunge_full(vms->mailstream,NIL,EX_UID);
+               } else 
+#endif
+                       mail_expunge(vms->mailstream);
+               ast_mutex_unlock(&vms->lock);
+       }
+#endif
        
        return res;
 }
@@ -5101,17 +5372,22 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
        /* simple. huh? */
        char sequence[10];
        char mailbox[256];
+       int res;
+
        /* get the real IMAP message number for this message */
        snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
        
        ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
+       ast_mutex_lock(&vms->lock);
        if (box == OLD_FOLDER) {
                mail_setflag(vms->mailstream, sequence, "\\Seen");
        } else if (box == NEW_FOLDER) {
                mail_clearflag(vms->mailstream, sequence, "\\Seen");
        }
-       if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER))
+       if (!strcasecmp(mbox(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 */
        imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
        ast_debug(5, "Checking if folder exists: %s\n",mailbox);
@@ -5119,7 +5395,9 @@ static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg
                ast_debug(5, "Folder exists.\n");
        else
                ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
-       return !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
+       res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
+       ast_mutex_unlock(&vms->lock);
+       return res;
 #else
        char *dir = vms->curdir;
        char *username = vms->username;
@@ -5419,7 +5697,9 @@ static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
        f = fopen(fn2, "r");
        if (f) {
                while (!feof(f)) {      
-                       fgets((char *)buf, sizeof(buf), f);
+                       if (!fgets((char *)buf, sizeof(buf), f)) {
+                               continue;
+                       }
                        if (!feof(f)) {
                                char *stringp=NULL;
                                stringp = (char *)buf;
@@ -5724,7 +6004,7 @@ static int get_folder2(struct ast_channel *chan, char *fn, int start)
  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
  * \return zero on success, -1 on error.
  */
-static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
+static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
                        char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
 {
 #ifdef IMAP_STORAGE
@@ -5748,7 +6028,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
        strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
        strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
 
-       if ((msg_cfg = ast_config_load(textfile, config_flags)) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
+       if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
                *duration = atoi(duration_str);
        } else {
                *duration = 0;
@@ -5766,7 +6046,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                        strncat(vms->introfn, "intro", sizeof(vms->introfn));
                        res = ast_play_and_wait(chan, INTRO);
                        res = ast_play_and_wait(chan, "beep");
-                       res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vmfmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
+                       res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
                        cmd = 't';
 #else
 
@@ -5793,7 +6073,7 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                        if (record_gain)
                                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
 
-                       cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
+                       cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
                        if (record_gain)
                                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
 
@@ -5804,14 +6084,13 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                        if (prepend_duration) {
                                struct ast_category *msg_cat;
                                /* need enough space for a maximum-length message duration */
-                               char duration_str[12];
+                               char duration_buf[12];
 
                                *duration += prepend_duration;
                                msg_cat = ast_category_get(msg_cfg, "message");
-                               snprintf(duration_str, 11, "%ld", *duration);
-                               if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
-                                       config_text_file_save(textfile, msg_cfg, "app_voicemail");
-                                       STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, prepend_duration, vms, NULL);
+                               snprintf(duration_buf, 11, "%ld", *duration);
+                               if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
+                                       ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
                                }
                        }
 
@@ -5819,7 +6098,9 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
                        break;
                case '2': 
                        /* NULL out introfile so we know there is no intro! */
+#ifdef IMAP_STORAGE
                        *vms->introfn = '\0';
+#endif
                        cmd = 't';
                        break;
                case '*':
@@ -5852,13 +6133,13 @@ static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu,
        return cmd;
 }
 
-static void queue_mwi_event(const char *mbox, int urgent, int new, int old)
+static void queue_mwi_event(const char *box, int urgent, int new, int old)
 {
        struct ast_event *event;
        char *mailbox, *context;
 
        /* Strip off @default */
-       context = mailbox = ast_strdupa(mbox);
+       context = mailbox = ast_strdupa(box);
        strsep(&context, "@");
        if (ast_strlen_zero(context))
                context = "default";
@@ -5953,6 +6234,14 @@ static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu,
        manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
        run_externnotify(vmu->context, vmu->mailbox, flag);
 
+#ifdef IMAP_STORAGE
+       vm_delete(fn);  /* Delete the file, but not the IMAP message */
+       if (ast_test_flag(vmu, VM_DELETE))  { /* Delete the IMAP message if delete = yes */
+               vm_imap_delete(vms->curmsg, vmu);
+               vms->newmessages--;  /* Fix new message count */
+       }
+#endif
+
        return 0;
 }
 
@@ -6003,7 +6292,9 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
        char urgent_str[7] = "";
        char tmptxtfile[PATH_MAX];
 
-       ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
+       if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
+               ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
+       }
 
        if (vms == NULL) return -1;
        dir = vms->curdir;
@@ -6056,11 +6347,10 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                        char old_context[sizeof(chan->context)];
                        char old_exten[sizeof(chan->exten)];
                        int old_priority;
-                       struct ast_app* app;
+                       struct ast_app* directory_app;
 
-                       
-                       app = pbx_findapp("Directory");
-                       if (app) {
+                       directory_app = pbx_findapp("Directory");
+                       if (directory_app) {
                                char vmcontext[256];
                                /* make backup copies */
                                memcpy(old_context, chan->context, sizeof(chan->context));
@@ -6068,8 +6358,8 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                old_priority = chan->priority;
                                
                                /* call the the Directory, changes the channel */
-                               snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
-                               res = pbx_exec(chan, app, vmcontext);
+                               snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
+                               res = pbx_exec(chan, directory_app, vmcontext);
                                
                                ast_copy_string(username, chan->exten, sizeof(username));
                                
@@ -6077,7 +6367,6 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                memcpy(chan->context, old_context, sizeof(chan->context));
                                memcpy(chan->exten, old_exten, sizeof(chan->exten));
                                chan->priority = old_priority;
-                               
                        } else {
                                ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
                                ast_clear_flag((&globalflags), VM_DIRECFORWARD);
@@ -6103,6 +6392,14 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                AST_LIST_INSERT_HEAD(&extensions, receiver, list);
                                found++;
                        } else {
+                               /* XXX Optimization for the future.  When we encounter a single bad extension,
+                                * bailing out on all of the extensions may not be the way to go.  We should
+                                * probably just bail on that single extension, then allow the user to enter
+                                * several more. XXX
+                                */
+                               while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
+                                       free_user(receiver);
+                               }
                                valid_extensions = 0;
                                break;
                        }
@@ -6117,10 +6414,9 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                        return res;
                                }
                        } else {
-                               /* Dispose just in case */
-                               DISPOSE(fn, -1);
                                res = ast_say_digit_str(chan, s, ecodes, chan->language);
                        }
+                       DISPOSE(fn, -1);
 
                        s = strsep(&stringp, "*");
                }
@@ -6158,7 +6454,7 @@ static int forward_message(struct ast_channel *chan, char *context, struct vm_st
                                char *myserveremail = serveremail;
                                
                                /* get destination mailbox */
-                               dstvms = get_vm_state_by_mailbox(vmtmp->mailbox,0);
+                               dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
                                if (!dstvms) {
                                        dstvms = create_vm_state_from_user(vmtmp);
                                }
@@ -6446,9 +6742,8 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc
 
        snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
        RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
-       ast_log(LOG_NOTICE, "I just retrieved %s and %s\n", vms->fn, S_OR(vms->introfn, "No intro"));
        msg_cfg = ast_config_load(filename, config_flags);
-       if (!msg_cfg) {
+       if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
                ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
                return 0;
        }
@@ -6561,7 +6856,6 @@ static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struc
                }
        }
        DISPOSE(vms->curdir, vms->curmsg);
-       ast_log(LOG_NOTICE, "I just disposed of %s and %s\n", vms->fn, S_OR(vms->introfn, "No intro"));
        return res;
 }
 
@@ -6607,7 +6901,8 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
                ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
                return -1;
        }
-       
+
+       ast_mutex_lock(&vms->lock);
        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. */
@@ -6615,6 +6910,7 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
                        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->lock);
                        return -1;
                }
                filename = strsep(&attachment, ".");
@@ -6624,6 +6920,7 @@ static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
                }
        }
        mail_expunge(vms->mailstream);
+       ast_mutex_unlock(&vms->lock);
        return 0;
 }
 
@@ -6724,7 +7021,7 @@ static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
                        cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
                        make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
                        if (EXISTS(vms->curdir, x, vms->fn, NULL))
-                               DELETE(vms->curdir, x, vms->fn);
+                               DELETE(vms->curdir, x, vms->fn, vmu);
                }
        } 
 
@@ -6762,68 +7059,68 @@ done:
  * syntax for the above three categories which is more elegant. 
  */
 
-static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
+static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
 {
        int cmd;
        char *buf;
 
-       buf = alloca(strlen(mbox)+2); 
-       strcpy(buf, mbox);
+       buf = alloca(strlen(box)+2); 
+       strcpy(buf, box);
        strcat(buf,"s");
 
-       if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
+       if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
                cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
                return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
        } else {
                cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
-               return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
+               return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
        }
 }
 
-static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
+static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
 {
        int cmd;
 
-       if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
-               if (!strcasecmp(mbox, "vm-INBOX"))
+       if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
+               if (!strcasecmp(box, "vm-INBOX"))
                        cmd = ast_play_and_wait(chan, "vm-new-e");
                else
                        cmd = ast_play_and_wait(chan, "vm-old-e");
                return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
        } else {
                cmd = ast_play_and_wait(chan, "vm-messages");
-               return cmd ? cmd : ast_play_and_wait(chan, mbox);
+               return cmd ? cmd : ast_play_and_wait(chan, box);
        }
 }
 
-static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
+static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
 {
        int cmd;
 
-       if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
+       if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
                cmd = ast_play_and_wait(chan, "vm-messages");
-               return cmd ? cmd : ast_play_and_wait(chan, mbox);
+               return cmd ? cmd : ast_play_and_wait(chan, box);
        } else {
-               cmd = ast_play_and_wait(chan, mbox);
+               cmd = ast_play_and_wait(chan, box);
                return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
        }
 }
 
-static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
+static int vm_play_folder_name(struct ast_channel *chan, char *box)
 {
        int cmd;
 
        if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
                cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
-               return cmd ? cmd : ast_play_and_wait(chan, mbox);
+               return cmd ? cmd : ast_play_and_wait(chan, box);
        } else if (!strcasecmp(chan->language, "gr")){
-               return vm_play_folder_name_gr(chan, mbox);
+               return vm_play_folder_name_gr(chan, box);
        } else if (!strcasecmp(chan->language, "pl")){
-               return vm_play_folder_name_pl(chan, mbox);
+               return vm_play_folder_name_pl(chan, box);
        } else if (!strcasecmp(chan->language, "ua")){  /* Ukrainian syntax */
-               return vm_play_folder_name_ua(chan, mbox);
+               return vm_play_folder_name_ua(chan, box);
        } else {  /* Default English */
-               cmd = ast_play_and_wait(chan, mbox);
+               cmd = ast_play_and_wait(chan, box);
                return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
        }
 }
@@ -7033,7 +7330,7 @@ static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
                        ast_play_and_wait(chan, "vm-vecchi") ||
                        ast_play_and_wait(chan, "vm-messages");
        }
-       return res ? -1 : 0;
+       return res;
 }
 
 /* POLISH syntax */
@@ -7749,8 +8046,11 @@ static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm
        /* 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)) {
-               if (ast_fileexists(prefile, NULL, NULL) > 0)
+               RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
+               if (ast_fileexists(prefile, NULL, NULL) > 0) {
                        ast_play_and_wait(chan, "vm-tempgreetactive");
+               }
+               DISPOSE(prefile, -1);
        }
 
        /* Play voicemail intro - syntax is different for different languages */
@@ -8065,7 +8365,7 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct
                                if (cmd < 0)
                                        break;
 
-                               if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
+                               if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
                                        break;
                                }
                        }
@@ -8088,9 +8388,11 @@ static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct
                default: 
                        cmd = 0;
                        snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
+                       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
                        if (ast_fileexists(prefile, NULL, NULL)) {
                                cmd = ast_play_and_wait(chan, "vm-tmpexists");
                        }
+                       DISPOSE(prefile, -1);
                        if (!cmd) {
                                cmd = ast_play_and_wait(chan, "vm-options");
                        }
@@ -8402,7 +8704,7 @@ static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, st
 
 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
                        struct ast_vm_user *res_vmu, const char *context, const char *prefix,
-                       int skipuser, int maxlogins, int silent)
+                       int skipuser, int max_logins, int silent)
 {
        int useadsi=0, valid=0, logretries=0;
        char password[AST_MAX_EXTENSION]="", *passptr;
@@ -8419,7 +8721,7 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
        
        /* Authenticate them and get their mailbox/password */
        
-       while (!valid && (logretries < maxlogins)) {
+       while (!valid && (logretries < max_logins)) {
                /* Prompt for, and read in the username */
                if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
                        ast_log(AST_LOG_WARNING, "Couldn't read username\n");
@@ -8472,7 +8774,7 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                }
                logretries++;
                if (!valid) {
-                       if (skipuser || logretries >= maxlogins) {
+                       if (skipuser || logretries >= max_logins) {
                                if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
                                        ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
                                        return -1;
@@ -8489,7 +8791,7 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_
                                return -1;
                }
        }
-       if (!valid && (logretries >= maxlogins)) {
+       if (!valid && (logretries >= max_logins)) {
                ast_stopstream(chan);
                ast_play_and_wait(chan, "vm-goodbye");
                return -1;
@@ -8628,6 +8930,7 @@ static int vm_execmain(struct ast_channel *chan, void *data)
 #ifdef IMAP_STORAGE
        vms.interactive = 1;
        vms.updated = 1;
+       ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
        vmstate_insert(&vms);
        init_vm_state(&vms);
 #endif
@@ -9173,13 +9476,15 @@ out:
 #ifdef IMAP_STORAGE
        /* expunge message - use UID Expunge if supported on IMAP server*/
        ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
-       if (vmu && deleted == 1 && expungeonhangup == 1) {
+       if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
+               ast_mutex_lock(&vms.lock);
 #ifdef HAVE_IMAP_TK2006
                if (LEVELUIDPLUS (vms.mailstream)) {
                        mail_expunge_full(vms.mailstream,NIL,EX_UID);
                } else 
 #endif
                        mail_expunge(vms.mailstream);
+               ast_mutex_unlock(&vms.lock);
        }
        /*  before we delete the state, we should copy pertinent info
         *  back to the persistent model */
@@ -9235,13 +9540,13 @@ static int vm_exec(struct ast_channel *chan, void *data)
                        }
                }
        } else {
-               char tmp[256];
-               res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
+               char temp[256];
+               res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
                if (res < 0)
                        return res;
-               if (ast_strlen_zero(tmp))
+               if (ast_strlen_zero(temp))
                        return 0;
-               args.argv0 = ast_strdupa(tmp);
+               args.argv0 = ast_strdupa(temp);
        }
 
        res = leave_voicemail(chan, args.argv0, &leave_options);
@@ -9255,14 +9560,14 @@ static int vm_exec(struct ast_channel *chan, void *data)
        return res;
 }
 
-static struct ast_vm_user *find_or_create(const char *context, const char *mbox)
+static struct ast_vm_user *find_or_create(const char *context, const char *box)
 {
        struct ast_vm_user *vmu;
 
        AST_LIST_TRAVERSE(&users, vmu, list) {
-               if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
+               if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox))
                        break;
-               if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
+               if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(box, vmu->mailbox)))
                        break;
        }
 
@@ -9273,14 +9578,14 @@ static struct ast_vm_user *find_or_create(const char *context, const char *mbox)
                return NULL;
        
        ast_copy_string(vmu->context, context, sizeof(vmu->context));
-       ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
+       ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
 
        AST_LIST_INSERT_TAIL(&users, vmu, list);
        
        return vmu;
 }
 
-static int append_mailbox(const char *context, const char *mbox, const char *data)
+static int append_mailbox(const char *context, const char *box, const char *data)
 {
        /* Assumes lock is already held */
        char *tmp;
@@ -9292,7 +9597,7 @@ static int append_mailbox(const char *context, const char *mbox, const char *dat
 
        tmp = ast_strdupa(data);
 
-       if (!(vmu = find_or_create(context, mbox)))
+       if (!(vmu = find_or_create(context, box)))
                return -1;
        
        populate_defaults(vmu);
@@ -9309,8 +9614,8 @@ static int append_mailbox(const char *context, const char *mbox, const char *dat
        if (stringp && (s = strsep(&stringp, ","))) 
                apply_options(vmu, s);
 
-       mailbox_full = alloca(strlen(mbox) + strlen(context) + 1);
-       strcpy(mailbox_full, mbox);
+       mailbox_full = alloca(strlen(box) + strlen(context) + 1);
+       strcpy(mailbox_full, box);
        strcat(mailbox_full, "@");
        strcat(mailbox_full, context);
 
@@ -9376,11 +9681,6 @@ static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *a
 
 static struct ast_custom_function mailbox_exists_acf = {
        .name = "MAILBOX_EXISTS",
-       .synopsis = "Tell if a mailbox is configured",
-       .desc =
-"Returns a boolean of whether the corresponding mailbox exists.  If context\n"
-"is not specified, defaults to the \"default\" context.\n",
-       .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
        .read = acf_mailbox_exists,
 };
 
@@ -9454,6 +9754,8 @@ static char *show_users_realtime(int fd, const char *context)
                "=============================================================\n"
                "\n");
 
+       ast_config_destroy(cfg);
+
        return CLI_SUCCESS;
 }
 
@@ -9647,11 +9949,11 @@ static void *mb_poll_thread(void *data)
 {
        while (poll_thread_run) {
                struct timespec ts = { 0, };
-               struct timeval tv;
+               struct timeval wait;
 
-               tv = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
-               ts.tv_sec = tv.tv_sec;
-               ts.tv_nsec = tv.tv_usec * 1000;
+               wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
+               ts.tv_sec = wait.tv_sec;
+               ts.tv_nsec = wait.tv_usec * 1000;
 
                ast_mutex_lock(&poll_lock);
                ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
@@ -9911,11 +10213,11 @@ static int manager_list_voicemail_users(struct mansession *s, const struct messa
 /*! \brief Free the users structure. */
 static void free_vm_users(void) 
 {
-       struct ast_vm_user *cur;
+       struct ast_vm_user *current;
        AST_LIST_LOCK(&users);
-       while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
-               ast_set_flag(cur, VM_ALLOCED);
-               free_user(cur);
+       while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
+               ast_set_flag(current, VM_ALLOCED);
+               free_user(current);
        }
        AST_LIST_UNLOCK(&users);
 }
@@ -9930,9 +10232,51 @@ static void free_vm_zones(void)
        AST_LIST_UNLOCK(&zones);
 }
 
+static const char *substitute_escapes(const char *value)
+{
+       char *current;
+
+       /* Add 16 for fudge factor */
+       struct ast_str *str = ast_str_thread_get(&global_app_buf, strlen(value) + 16);
+
+       /* Substitute strings \r, \n, and \t into the appropriate characters */
+       for (current = (char *) value; *current; current++) {
+               if (*current == '\\') {
+                       current++;
+                       if (!*current) {
+                               ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
+                               break;
+                       }
+                       switch (*current) {
+                       case 'r':
+                               ast_str_append(&str, 0, "\r");
+                               break;
+                       case 'n':
+#ifdef IMAP_STORAGE
+                               if (!str->used || str->str[str->used - 1] != '\r') {
+                                       ast_str_append(&str, 0, "\r");
+                               }
+#endif
+                               ast_str_append(&str, 0, "\n");
+                               break;
+                       case 't':
+                               ast_str_append(&str, 0, "\t");
+                               break;
+                       default:
+                               ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
+                               break;
+                       }
+               } else {
+                       ast_str_append(&str, 0, "%c", *current);
+               }
+       }
+
+       return ast_str_buffer(str);
+}
+
 static int load_config(int reload)
 {
-       struct ast_vm_user *cur;
+       struct ast_vm_user *current;
        struct ast_config *cfg, *ucfg;
        char *cat;
        struct ast_variable *var;
@@ -9946,13 +10290,27 @@ static int load_config(int reload)
        ast_unload_realtime("voicemail_data");
 
        if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
-               if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
+               if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
                        return 0;
+               } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
+                       ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
+                       ucfg = NULL;
+               }
                ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
-               cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
+               if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
+                       ast_config_destroy(ucfg);
+                       ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
+                       return 0;
+               }
+       } else if (cfg == CONFIG_STATUS_FILEINVALID) {
+               ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
+               return 0;
        } else {
                ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
-               ucfg = ast_config_load("users.conf", config_flags);
+               if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
+                       ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
+                       ucfg = NULL;
+               }
        }
 #ifdef IMAP_STORAGE
        ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
@@ -10341,6 +10699,12 @@ static int load_config(int reload)
                }
                ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);     
 
+               if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
+                       ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
+                       val = "no";
+               }
+               ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);    
+
                if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
                        ast_debug(1,"Duration info before msg enabled globally\n");
                        val = "yes";
@@ -10428,10 +10792,10 @@ static int load_config(int reload)
                        for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
                                if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
                                        continue;
-                               if ((cur = find_or_create(userscontext, cat))) {
-                                       populate_defaults(cur);
-                                       apply_options_full(cur, ast_variable_browse(ucfg, cat));
-                                       ast_copy_string(cur->context, userscontext, sizeof(cur->context));
+                               if ((current = find_or_create(userscontext, cat))) {
+                                       populate_defaults(current);
+                                       apply_options_full(current, ast_variable_browse(ucfg, cat));
+                                       ast_copy_string(current->context, userscontext, sizeof(current->context));
                                }
                        }
                        ast_config_destroy(ucfg);
@@ -10451,12 +10815,12 @@ static int load_config(int reload)
                                        while (var) {
                                                struct vm_zone *z;
                                                if ((z = ast_malloc(sizeof(*z)))) {
-                                                       char *msg_format, *timezone;
+                                                       char *msg_format, *tzone;
                                                        msg_format = ast_strdupa(var->value);
-                                                       timezone = strsep(&msg_format, "|");
+                                                       tzone = strsep(&msg_format, "|");
                                                        if (msg_format) {
                                                                ast_copy_string(z->name, var->name, sizeof(z->name));
-                                                               ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
+                                                               ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
                                                                ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
                                                                AST_LIST_LOCK(&zones);
                                                                AST_LIST_INSERT_HEAD(&zones, z, list);
@@ -10520,61 +10884,20 @@ static int load_config(int reload)
                                adsiver = atoi(val);
                        }
                }
-               if ((val = ast_variable_retrieve(cfg, "general", "emailsubject")))
+               if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
+                       ast_copy_string(zonetag, val, sizeof(zonetag));
+               }
+               if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
                        emailsubject = ast_strdup(val);
+               }
                if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
-                       char *tmpread, *tmpwrite;
-                       emailbody = ast_strdup(val);
-
-                       /* substitute strings \t and \n into the appropriate characters */
-                       tmpread = tmpwrite = emailbody;
-                       while ((tmpwrite = strchr(tmpread,'\\'))) {
-                               switch (tmpwrite[1]) {
-                               case 'r':
-                                       memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
-                                       *tmpwrite = '\r';
-                                       break;
-                               case 'n':
-                                       memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
-                                       *tmpwrite = '\n';
-                                       break;
-                               case 't':
-                                       memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
-                                       *tmpwrite = '\t';
-                                       break;
-                               default:
-                                       ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
-                               }
-                               tmpread = tmpwrite + 1;
-                       }
+                       emailbody = ast_strdup(substitute_escapes(val));
                }
-               if ((val = ast_variable_retrieve(cfg, "general", "pagersubject")))
+               if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
                        pagersubject = ast_strdup(val);
+               }
                if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
-                       char *tmpread, *tmpwrite;
-                       pagerbody = ast_strdup(val);
-
-                       /* substitute strings \t and \n into the appropriate characters */
-                       tmpread = tmpwrite = pagerbody;
-                       while ((tmpwrite = strchr(tmpread, '\\'))) {
-                               switch (tmpwrite[1]) {
-                               case 'r':
-                                       memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
-                                       *tmpwrite = '\r';
-                                       break;
-                               case 'n':
-                                       memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
-                                       *tmpwrite = '\n';
-                                       break;
-                               case 't':
-                                       memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
-                                       *tmpwrite = '\t';
-                                       break;
-                               default:
-                                       ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
-                               }
-                               tmpread = tmpwrite + 1;
-                       }
+                       pagerbody = ast_strdup(substitute_escapes(val));
                }
                AST_LIST_UNLOCK(&users);
                ast_config_destroy(cfg);
@@ -10623,7 +10946,7 @@ static int unload_module(void)
        res |= ast_unregister_application(app4);
        res |= ast_custom_function_unregister(&mailbox_exists_acf);
        res |= ast_manager_unregister("VoicemailUsersList");
-       ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
+       ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
        ast_uninstall_vm_functions();
 
        if (poll_thread != AST_PTHREADT_NULL)
@@ -10654,16 +10977,16 @@ static int load_module(void)
        if ((res = load_config(0)))
                return res;
 
-       res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
-       res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
-       res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
-       res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
+       res = ast_register_application_xml(app, vm_exec);
+       res |= ast_register_application_xml(app2, vm_execmain);
+       res |= ast_register_application_xml(app3, vm_box_exists);
+       res |= ast_register_application_xml(app4, vmauthenticate);
        res |= ast_custom_function_register(&mailbox_exists_acf);
        res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
        if (res)
                return res;
 
-       ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
+       ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
 
        ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
        ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
@@ -10764,7 +11087,7 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
        RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
        msg_cfg = ast_config_load(filename, config_flags);
        DISPOSE(vms->curdir, vms->curmsg);
-       if (!msg_cfg) {
+       if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
                ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
                return 0;
        }
@@ -10942,7 +11265,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
        int max_attempts = 3;
        int attempts = 0;
        int recorded = 0;
-       int message_exists = 0;
+       int msg_exists = 0;
        signed char zero_gain = 0;
        char tempfile[PATH_MAX];
        char *acceptdtmf = "#";
@@ -10966,7 +11289,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
        while ((cmd >= 0) && (cmd != 't')) {
                switch (cmd) {
                case '1':
-                       if (!message_exists) {
+                       if (!msg_exists) {
                                /* In this case, 1 is to record a message */
                                cmd = '3';
                                break;
@@ -10992,7 +11315,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                        cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
                        break;
                case '3':
-                       message_exists = 0;
+                       msg_exists = 0;
                        /* Record */
                        if (recorded == 1) 
                                ast_verb(3, "Re-recording the message\n");
@@ -11042,7 +11365,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
 #endif
                        } else {
                                /* If all is well, a message exists */
-                               message_exists = 1;
+                               msg_exists = 1;
                                cmd = 0;
                        }
                        break;
@@ -11094,7 +11417,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                                cmd = ast_play_and_wait(chan, "vm-sorry");
                                break;
                        }
-                       if (message_exists || recorded) {
+                       if (msg_exists || recorded) {
                                cmd = ast_play_and_wait(chan, "vm-saveoper");
                                if (!cmd)
                                        cmd = ast_waitfordigit(chan, 3000);
@@ -11121,7 +11444,7 @@ static int play_record_review(struct ast_channel *chan, char *playfile, char *re
                           their OGM's */
                        if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
                                return cmd;
-                       if (message_exists) {
+                       if (msg_exists) {
                                cmd = ast_play_and_wait(chan, "vm-review");
                                if (!cmd && outsidecaller) {
                                        if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {