remove the PBX_ODBC logic from the configure script, and add GENERIC_ODCB logic that...
[asterisk/asterisk.git] / apps / app_voicemail.c
index b90351b..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,11 +115,193 @@ 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];
 static char imapflags[128];
 static char imapfolder[64];
+static char imapparentfolder[64] = "\0";
 static char greetingfolder[64];
 static char authuser[32];
 static char authpassword[42];
@@ -140,26 +317,25 @@ struct ast_vm_user;
 static int init_mailstream(struct vm_state *vms, int box);
 static void write_file(char *filename, char *buffer, unsigned long len);
 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
-static void vm_imap_delete(int msgnum, struct vm_state *vms);
+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);
 static void set_update(MAILSTREAM * stream);
 static void init_vm_state(struct vm_state *vms);
-static void check_msgArray(struct vm_state *vms);
-static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
-static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, char *altfile);
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix);
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
 static void get_mailbox_delimiter(MAILSTREAM *stream);
 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
 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, char *introfile, const char *flag);
+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 (char *dir, int msgnum, const char *mailbox, const char *context);
+static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
 static void check_quota(struct vm_state *vms, char *mailbox);
 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
@@ -229,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
 
 
@@ -416,10 +593,10 @@ 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];
-       char fn2[PATH_MAX];
        char intro[PATH_MAX];
        int *deleted;
        int *heard;
@@ -438,6 +615,7 @@ struct vm_state {
        int vmArrayIndex;
        char imapuser[80];                   /*!< IMAP server login */
        int interactive;
+       char introfn[PATH_MAX];              /*!< Name of prepended file */
        unsigned int quota_limit;
        unsigned int quota_usage;
        struct vm_state *persist_vms;
@@ -449,29 +627,28 @@ static char odbc_database[80];
 static char odbc_table[80];
 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
 #define DISPOSE(a,b) remove_file(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
+#define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
 #define EXISTS(a,b,c,d) (message_exists(a,b))
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
-#define DELETE(a,b,c) (delete_file(a,b))
+#define DELETE(a,b,c,d) (delete_file(a,b))
 #else
 #ifdef IMAP_STORAGE
-#define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
 #define DISPOSE(a,b) (imap_remove_file(a,b))
-#define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
+#define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
+#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
-#define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
-#define DELETE(a,b,c) (vm_delete(c))
+#define DELETE(a,b,c,d) (vm_imap_delete(b,d))
 #else
 #define RETRIEVE(a,b,c,d)
 #define DISPOSE(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i,j,k)
+#define STORE(a,b,c,d,e,f,g,h,i,j)
 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
-#define DELETE(a,b,c) (vm_delete(c))
+#define DELETE(a,b,c,d) (vm_delete(c))
 #endif
 #endif
 
@@ -500,76 +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"
-       "    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";
 
@@ -581,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;
@@ -701,6 +809,22 @@ static int is_valid_dtmf(const char *key);
 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
 #endif
 
+static char *strip_control(const char *input, char *buf, size_t buflen)
+{
+       char *bufptr = buf;
+       for (; *input; input++) {
+               if (*input < 32) {
+                       continue;
+               }
+               *bufptr++ = *input;
+               if (bufptr == buf + buflen - 1) {
+                       break;
+               }
+       }
+       *bufptr = '\0';
+       return buf;
+}
+
 
 /*!
  * \brief Sets default voicemail system options to a voicemail user.
@@ -722,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)
@@ -755,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")) {
@@ -854,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 */
@@ -893,7 +1020,7 @@ static int check_password(struct ast_vm_user *vmu, char *password)
        if (!ast_strlen_zero(ext_pass_check_cmd)) {
                char cmd[255], buf[255];
 
-               ast_log(LOG_DEBUG, "Verify password policies for %s\n", password);
+               ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
 
                snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
                if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
@@ -902,10 +1029,10 @@ static int check_password(struct ast_vm_user *vmu, char *password)
                                ast_debug(3, "Passed password check: '%s'\n", buf);
                                return 0;
                        } else if (!strncasecmp(buf, "FAILURE", 7)) {
-                               ast_log(LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
+                               ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
                                return 0;
                        } else {
-                               ast_log(LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
+                               ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
                                return 1;
                        }
                }
@@ -927,7 +1054,10 @@ static int change_password_realtime(struct ast_vm_user *vmu, const char *passwor
 {
        int res;
        if (!ast_strlen_zero(vmu->uniqueid)) {
-               res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
+               if (strlen(password) > 10) {
+                       ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(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;
@@ -963,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);
+       }
 }
 
 /*!
@@ -1039,9 +1166,9 @@ static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const cha
                        ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
                populate_defaults(retval);
                if (!context && ast_test_flag((&globalflags), VM_SEARCH))
-                       var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
+                       var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
                else
-                       var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
+                       var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
                if (var) {
                        apply_options_full(retval, var);
                        ast_variables_destroy(var);
@@ -1138,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))) {
@@ -1162,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);
@@ -1192,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");
        }
 }
 
@@ -1222,40 +1349,35 @@ static int make_dir(char *dest, int len, const char *context, const char *ext, c
        return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
 }
 
-#ifdef IMAP_STORAGE
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix)
+/*! 
+ * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
+ * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
+ * \param len The length of the path string that was written out.
+ * 
+ * The path is constructed as 
+ *     VM_SPOOL_DIRcontext/ext/folder
+ *
+ * \return zero on success, -1 on error.
+ */
+static int make_file(char *dest, const int len, const char *dir, const int num)
 {
-       int res;
-       if ((res = ast_mkdir(dir, 01777))) {
-               ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
-               return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
-       }
-       return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
+       return snprintf(dest, len, "%s/msg%04d", dir, num);
 }
 
-static void vm_imap_delete(int msgnum, struct vm_state *vms)
+/* same as mkstemp, but return a FILE * */
+static FILE *vm_mkftemp(char *template)
 {
-       unsigned long messageNum = 0;
-       char arg[10];
-
-       /* find real message number based on msgnum */
-       /* this may be an index into vms->msgArray based on the msgnum. */
-
-       messageNum = vms->msgArray[msgnum];
-       if (messageNum == 0) {
-               ast_log(AST_LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
-               return;
+       FILE *p = NULL;
+       int pfd = mkstemp(template);
+       chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
+       if (pfd > -1) {
+               p = fdopen(pfd, "w+");
+               if (!p) {
+                       close(pfd);
+                       pfd = -1;
+               }
        }
-       ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
-       /* delete message */
-       snprintf (arg, sizeof(arg), "%lu",messageNum);
-       mail_setflag (vms->mailstream,arg,"\\DELETED");
-}
-
-#endif
-static int make_file(char *dest, int len, char *dir, int num)
-{
-       return snprintf(dest, len, "%s/msg%04d", dir, num);
+       return p;
 }
 
 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
@@ -1279,1642 +1401,1510 @@ static int create_dirpath(char *dest, int len, const char *context, const char *
        return 0;
 }
 
-/*! \brief Lock file path
-    only return failure if ast_lock_path returns 'timeout',
-   not if the path does not exist or any other reason
-*/
-static int vm_lock_path(const char *path)
+static const char *mbox(int id)
 {
-       switch (ast_lock_path(path)) {
-       case AST_LOCK_TIMEOUT:
-               return -1;
-       default:
-               return 0;
-       }
+       static const char *msgs[] = {
+#ifdef IMAP_STORAGE
+               imapfolder,
+#else
+               "INBOX",
+#endif
+               "Old",
+               "Work",
+               "Family",
+               "Friends",
+               "Cust1",
+               "Cust2",
+               "Cust3",
+               "Cust4",
+               "Cust5",
+               "Deleted",
+               "Urgent"
+       };
+       return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
 }
 
+static void free_user(struct ast_vm_user *vmu)
+{
+       if (ast_test_flag(vmu, VM_ALLOCED))
+               ast_free(vmu);
+}
 
-#ifdef ODBC_STORAGE
-struct generic_prepare_struct {
-       char *sql;
-       int argc;
-       char **argv;
-};
-
-static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
+/* All IMAP-specific functions should go in this block. This
+ * keeps them from being spread out all over the code */
+#ifdef IMAP_STORAGE
+static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
 {
-       struct generic_prepare_struct *gps = data;
-       int res, i;
-       SQLHSTMT stmt;
+       char arg[10];
+       struct vm_state *vms;
+       unsigned long messageNum;
 
-       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
-               return NULL;
+       /* Greetings aren't stored in IMAP, so we can't delete them there */
+       if (msgnum < 0) {
+               return;
        }
-       res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               return NULL;
+
+       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;
        }
-       for (i = 0; i < gps->argc; i++)
-               SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
 
-       return stmt;
+       /* find real message number based on msgnum */
+       /* this may be an index into vms->msgArray based on the msgnum. */
+       messageNum = vms->msgArray[msgnum];
+       if (messageNum == 0) {
+               ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
+               return;
+       }
+       if (option_debug > 2)
+               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);
 }
 
-/*!
- * \brief Retrieves a file from an ODBC data store.
- * \param dir the path to the file to be retreived.
- * \param msgnum the message number, such as within a mailbox folder.
- * 
- * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
- * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
- *
- * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
- * The output is the message information file with the name msgnum and the extension .txt
- * and the message file with the extension of its format, in the directory with base file name of the msgnum.
- * 
- * \return 0 on success, -1 on error.
- */
-static int retrieve_file(char *dir, int msgnum)
+static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
 {
-       int x = 0;
-       int res;
-       int fd=-1;
-       size_t fdlen = 0;
-       void *fdm = MAP_FAILED;
-       SQLSMALLINT colcount=0;
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char fmt[80]="";
-       char *c;
-       char coltitle[256];
-       SQLSMALLINT collen;
-       SQLSMALLINT datatype;
-       SQLSMALLINT decimaldigits;
-       SQLSMALLINT nullable;
-       SQLULEN colsize;
-       SQLLEN colsize2;
-       FILE *f=NULL;
-       char rowdata[80];
-       char fn[PATH_MAX];
-       char full_fn[PATH_MAX];
-       char msgnums[80];
-       char *argv[] = { dir, msgnums };
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+       struct vm_state *vms_p;
+       char *file, *filename;
+       char *attachment;
+       int ret = 0, i;
+       BODY *body;
 
-       struct odbc_obj *obj;
-       obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               ast_copy_string(fmt, vmfmts, sizeof(fmt));
-               c = strchr(fmt, '|');
-               if (c)
-                       *c = '\0';
-               if (!strcasecmp(fmt, "wav49"))
-                       strcpy(fmt, "WAV");
-               snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
-               if (msgnum > -1)
-                       make_file(fn, sizeof(fn), dir, msgnum);
-               else
-                       ast_copy_string(fn, dir, sizeof(fn));
+       /* This function is only used for retrieval of IMAP greetings
+        * regular messages are not retrieved this way, nor are greetings
+        * if they are stored locally*/
+       if (msgnum > -1 || !imapgreetings) {
+               return 0;
+       } else {
+               file = strrchr(ast_strdupa(dir), '/');
+               if (file)
+                       *file++ = '\0';
+               else {
+                       ast_debug (1, "Failed to procure file name from directory passed.\n");
+                       return -1;
+               }
+       }
 
-               /* Create the information file */
-               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-               
-               if (!(f = fopen(full_fn, "w+"))) {
-                       ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
-                       goto yuck;
+       /* check if someone is accessing this box right now... */
+       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;
+       }
+
+       /*XXX Yuck, this could probably be done a lot better */
+       for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
+               mail_fetchstructure(vms_p->mailstream, i + 1, &body);
+               /* We have the body, now we extract the file name of the first attachment. */
+               if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
+                       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;
                }
-               
-               snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
-               snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+               filename = strsep(&attachment, ".");
+               if (!strcmp(filename, file)) {
+                       ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
+                       vms_p->msgArray[vms_p->curmsg] = i + 1;
+                       save_body(body, vms_p, "2", attachment, 0);
+                       ast_mutex_unlock(&vms_p->lock);
+                       return 0;
                }
-               res = SQLFetch(stmt);
-               if (res == SQL_NO_DATA) {
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
-               if (fd < 0) {
-                       ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLNumResultCols(stmt, &colcount);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
-                       ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               if (f) 
-                       fprintf(f, "[message]\n");
-               for (x=0;x<colcount;x++) {
-                       rowdata[0] = '\0';
-                       collen = sizeof(coltitle);
-                       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
-                                               &datatype, &colsize, &decimaldigits, &nullable);
-                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                               ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
-                               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                               ast_odbc_release_obj(obj);
-                               goto yuck;
-                       }
-                       if (!strcasecmp(coltitle, "recording")) {
-                               off_t offset;
-                               res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
-                               fdlen = colsize2;
-                               if (fd > -1) {
-                                       char tmp[1]="";
-                                       lseek(fd, fdlen - 1, SEEK_SET);
-                                       if (write(fd, tmp, 1) != 1) {
-                                               close(fd);
-                                               fd = -1;
-                                               continue;
-                                       }
-                                       /* Read out in small chunks */
-                                       for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
-                                               if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
-                                                       ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
-                                                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-                                                       ast_odbc_release_obj(obj);
-                                                       goto yuck;
-                                               } else {
-                                                       res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
-                                                       munmap(fdm, CHUNKSIZE);
-                                                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                                               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                                                               unlink(full_fn);
-                                                               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-                                                               ast_odbc_release_obj(obj);
-                                                               goto yuck;
-                                                       }
-                                               }
-                                       }
-                                       truncate(full_fn, fdlen);
-                               }
-                       } else {
-                               res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                                       ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
-                                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                                       ast_odbc_release_obj(obj);
-                                       goto yuck;
-                               }
-                               if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
-                                       fprintf(f, "%s=%s\n", coltitle, rowdata);
-                       }
-               }
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:  
-       if (f)
-               fclose(f);
-       if (fd > -1)
-               close(fd);
-       return x - 1;
+       }
+       ast_mutex_unlock(&vms_p->lock);
+
+       return -1;
 }
 
-/*!
- * \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)
+static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
 {
-       char fn[PATH_MAX];
-       char full_fn[PATH_MAX];
-       char msgnums[80];
+       BODY *body;
+       char *header_content;
+       char *attachedfilefmt;
+       char buf[80];
+       struct vm_state *vms;
+       char text_file[PATH_MAX];
+       FILE *text_file_ptr;
+       int res = 0;
+       struct ast_vm_user *vmu;
+
+       if (!(vmu = find_user(NULL, context, mailbox))) {
+               ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
+               return -1;
+       }
        
-       if (msgnum > -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);       
-       if (ast_check_realtime("voicemail_data")) {
-               ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
+       if (msgnum < 0) {
+               if (imapgreetings) {
+                       res = imap_retrieve_greeting(dir, msgnum, vmu);
+                       goto exit;
+               } else {
+                       res = 0;
+                       goto exit;
+               }
        }
-       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-       unlink(full_fn);
-       return 0;
+
+       /* 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, 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
+                * IMAP message could be accessed. If I have seen correctly
+                * though, the vms should be obtainable from the vmstates list
+                * and should have its msgArray properly set up.
+                */
+               ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
+               res = -1;
+               goto exit;
+       }
+       
+       make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
+       snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
+
+       /* Don't try to retrieve a message from IMAP if it already is on the file system */
+       if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
+               res = 0;
+               goto exit;
+       }
+
+       if (option_debug > 2)
+               ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
+       if (vms->msgArray[msgnum] == 0) {
+               ast_log (LOG_WARNING,"Trying to access unknown message\n");
+               res = -1;
+               goto exit;
+       }
+
+       /* 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]);
+               res = -1;
+               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);
+       } else {
+               ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
+               res = -1;
+               goto exit;
+       }
+       
+       /* Find the format of the attached file */
+
+       strsep(&attachedfilefmt, ".");
+       if (!attachedfilefmt) {
+               ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
+               res = -1;
+               goto exit;
+       }
+       
+       save_body(body, vms, "2", attachedfilefmt, 0);
+       if (save_body(body, vms, "3", attachedfilefmt, 1)) {
+               *vms->introfn = '\0';
+       }
+
+       /* Get info from headers!! */
+       snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
+
+       if (!(text_file_ptr = fopen(text_file, "w"))) {
+               ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+       }
+
+       fprintf(text_file_ptr, "%s\n", "[message]");
+
+       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, ""));
+       get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
+       fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
+       get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
+       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:
+       free_user(vmu);
+       return res;
+}
+
+static int folder_int(const char *folder)
+{
+       /*assume a NULL folder means INBOX*/
+       if (!folder)
+               return 0;
+#ifdef IMAP_STORAGE
+       if (!strcasecmp(folder, imapfolder))
+#else
+       if (!strcasecmp(folder, "INBOX"))
+#endif
+               return 0;
+       else if (!strcasecmp(folder, "Old"))
+               return 1;
+       else if (!strcasecmp(folder, "Work"))
+               return 2;
+       else if (!strcasecmp(folder, "Family"))
+               return 3;
+       else if (!strcasecmp(folder, "Friends"))
+               return 4;
+       else if (!strcasecmp(folder, "Cust1"))
+               return 5;
+       else if (!strcasecmp(folder, "Cust2"))
+               return 6;
+       else if (!strcasecmp(folder, "Cust3"))
+               return 7;
+       else if (!strcasecmp(folder, "Cust4"))
+               return 8;
+       else if (!strcasecmp(folder, "Cust5"))
+               return 9;
+       else /*assume they meant INBOX if folder is not found otherwise*/
+               return 0;
 }
 
 /*!
- * \brief Determines the highest message number in use for a given user and mailbox folder.
- * \param vmu 
- * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
- *
- * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
+ * \brief Gets the number of messages that exist in a mailbox folder.
+ * \param context
+ * \param mailbox
+ * \param folder
+ * 
+ * This method is used when IMAP backend is used.
+ * \return The number of messages in this mailbox folder (zero or more).
  */
-static int last_message_index(struct ast_vm_user *vmu, char *dir)
+static int messagecount(const char *context, const char *mailbox, const char *folder)
 {
-       int x = 0;
-       int res;
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char rowdata[20];
-       char *argv[] = { dir };
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
+       SEARCHPGM *pgm;
+       SEARCHHEADER *hdr;
 
-       struct odbc_obj *obj;
-       obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+       struct ast_vm_user *vmu, vmus;
+       struct vm_state *vms_p;
+       int ret = 0;
+       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;
+
+       /* We have to get the user before we can open the stream! */
+       vmu = find_user(&vmus, context, mailbox);
+       if (!vmu) {
+               ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
+               return -1;
+       } else {
+               /* No IMAP account available */
+               if (vmu->imapuser[0] == '\0') {
+                       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
+                       return -1;
                }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+       }
+       
+       /* No IMAP account available */
+       if (vmu->imapuser[0] == '\0') {
+               ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
+               free_user(vmu);
+               return -1;
+       }
+
+       /* 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, context, 1);
+       }
+       if (vms_p) {
+               ast_debug(3, "Returning before search - user is logged in\n");
+               if (fold == 0) { /* INBOX */
+                       return vms_p->newmessages;
                }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+               if (fold == 1) { /* Old messages */
+                       return vms_p->oldmessages;
                }
-               if (sscanf(rowdata, "%d", &x) != 1)
-                       ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:  
-       return x - 1;
-}
+               if (fold == 11) {/*Urgent messages*/
+                       return vms_p->urgentmessages;
+               }
+       }
 
-/*!
- * \brief Determines if the specified message exists.
- * \param dir the folder the mailbox folder to look for messages. 
- * \param msgnum the message index to query for.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- *
- * \return greater than zero if the message exists, zero when the message does not exist or on error.
- */
-static int message_exists(char *dir, int msgnum)
-{
-       int x = 0;
-       int res;
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char rowdata[20];
-       char msgnums[20];
-       char *argv[] = { dir, msgnums };
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+       /* 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, context, 0);
+       }
 
-       struct odbc_obj *obj;
-       obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+       if (!vms_p) {
+               vms_p = create_vm_state_from_user(vmu);
+       }
+       ret = init_mailstream(vms_p, fold);
+       if (!vms_p->mailstream) {
+               ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
+               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;
+               if (fold != 1) {
+                       pgm->unseen = 1;
+                       pgm->seen = 0;
                }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+               /* In the special case where fold is 1 (old messages) we have to do things a bit
+                * differently. Old messages are stored in the INBOX but are marked as "seen"
+                */
+               else {
+                       pgm->unseen = 0;
+                       pgm->seen = 1;
                }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+               /* look for urgent messages */
+               if (urgent) {
+                       pgm->flagged = 1;
+                       pgm->unflagged = 0;
                }
-               if (sscanf(rowdata, "%d", &x) != 1)
-                       ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:  
-       return x;
-}
+               pgm->undeleted = 1;
+               pgm->deleted = 0;
 
-/*!
- * \brief returns the one-based count for messages.
- * \param vmu
- * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
- * one-based messages.
- * This method just calls last_message_index and returns +1 of its value.
- *
- * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
- */
-static int count_messages(struct ast_vm_user *vmu, char *dir)
-{
-       return last_message_index(vmu, dir) + 1;
+               vms_p->vmArrayIndex = 0;
+               mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
+               if (fold == 0 && urgent == 0)
+                       vms_p->newmessages = vms_p->vmArrayIndex;
+               if (fold == 1)
+                       vms_p->oldmessages = vms_p->vmArrayIndex;
+               if (fold == 0 && urgent == 1)
+                       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 {
+               ast_mutex_lock(&vms_p->lock);
+               mail_ping(vms_p->mailstream);
+               ast_mutex_unlock(&vms_p->lock);
+       }
+       return 0;
 }
 
-/*!
- * \brief Deletes a message from the mailbox folder.
- * \param sdir The mailbox folder to work in.
- * \param smsg The message index to be deleted.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * The specified message is directly deleted from the database 'voicemessages' table.
- * 
- * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
- */
-static void delete_file(char *sdir, int smsg)
+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)
 {
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char msgnums[20];
-       char *argv[] = { sdir, msgnums };
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+       char *myserveremail = serveremail;
+       char fn[PATH_MAX];
+       char introfn[PATH_MAX];
+       char mailbox[256];
+       char *stringp;
+       FILE *p=NULL;
+       char tmp[80] = "/tmp/astmail-XXXXXX";
+       long len;
+       void *buf;
+       int tempcopy = 0;
+       STRING str;
+       int ret; /* for better error checking */
+       char *imap_flags = NIL;
 
-       struct odbc_obj *obj;
-       obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-               snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt)
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-               else
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-       return; 
-}
+       /* Set urgent flag for IMAP message */
+       if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
+               ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
+               imap_flags="\\FLAGGED";
+       }
+       
+       /* Attach only the first format */
+       fmt = ast_strdupa(fmt);
+       stringp = fmt;
+       strsep(&stringp, "|");
 
-/*!
- * \brief Copies a voicemail from one mailbox to another.
- * \param sdir the folder for which to look for the message to be copied.
- * \param smsg the index of the message to be copied.
- * \param ddir the destination folder to copy the message into.
- * \param dmsg the index to be used for the copied message.
- * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
- * \param dmailboxcontext The context for the destination user.
- *
- * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
- */
-static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
-{
-       SQLHSTMT stmt;
-       char sql[512];
-       char msgnums[20];
-       char msgnumd[20];
-       struct odbc_obj *obj;
-       char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
+       if (!ast_strlen_zero(vmu->serveremail))
+               myserveremail = vmu->serveremail;
 
-       delete_file(ddir, dmsg);
-       obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
-               snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext, flag) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt)
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
-               else
-                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-       return; 
-}
+       if (msgnum > -1)
+               make_file(fn, sizeof(fn), dir, msgnum);
+       else
+               ast_copy_string (fn, dir, sizeof(fn));
 
-struct insert_data {
-       char *sql;
-       char *dir;
-       char *msgnums;
-       void *data;
-       SQLLEN datalen;
-       const char *context;
-       const char *macrocontext;
-       const char *callerid;
-       const char *origtime;
-       const char *duration;
-       char *mailboxuser;
-       char *mailboxcontext;
-       const char *category;
-       const char *flag;
-};
+       snprintf(introfn, sizeof(introfn), "%sintro", fn);
+       if (ast_fileexists(introfn, NULL, NULL) <= 0) {
+               *introfn = '\0';
+       }
+       
+       if (ast_strlen_zero(vmu->email)) {
+               /* We need the vmu->email to be set when we call make_email_file, but
+                * if we keep it set, a duplicate e-mail will be created. So at the end
+                * of this function, we will revert back to an empty string if tempcopy
+                * is 1.
+                */
+               ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
+               tempcopy = 1;
+       }
 
-static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
-{
-       struct insert_data *data = vdata;
-       int res;
-       SQLHSTMT stmt;
-       SQLLEN len = data->datalen;
+       if (!strcmp(fmt, "wav49"))
+               fmt = "WAV";
+       ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
 
-       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               return NULL;
+       /* Make a temporary file instead of piping directly to sendmail, in case the mail
+          command hangs. */
+       if (!(p = vm_mkftemp(tmp))) {
+               ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
+               if (tempcopy)
+                       *(vmu->email) = '\0';
+               return -1;
        }
 
-       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
-       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
-       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &len);
-       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
-       SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
-       SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
-       SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
-       SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
-       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
-       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
-       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
-       if (!ast_strlen_zero(data->category)) {
-               SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
+       if (msgnum < 0 && imapgreetings) {
+               if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
+                       ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
+                       return -1;
+               }
+               imap_delete_old_greeting(fn, vms);
        }
-       res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               return NULL;
+       
+       make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
+       /* read mail file to memory */          
+       len = ftell(p);
+       rewind(p);
+       if (!(buf = ast_malloc(len + 1))) {
+               ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
+               fclose(p);
+               if (tempcopy)
+                       *(vmu->email) = '\0';
+               return -1;
+       }
+       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);
+               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);
+       } else {
+               ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
+               fclose(p);
+               unlink(tmp);
+               ast_free(buf);
+               return -1;
        }
+       ast_debug(3, "%s stored\n", fn);
+       
+       if (tempcopy)
+               *(vmu->email) = '\0';
+       
+       return 0;
 
-       return stmt;
 }
 
 /*!
- * \brief Stores a voicemail into the database.
- * \param dir the folder the mailbox folder to store the message.
- * \param mailboxuser the user owning the mailbox folder.
- * \param mailboxcontext
- * \param msgnum the message index for the message to be stored.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * The message sound file and information file is looked up on the file system. 
- * A SQL query is invoked to store the message into the (MySQL) database.
+ * \brief Gets the number of messages that exist in the inbox folder.
+ * \param mailbox_context
+ * \param newmsgs The variable that is updated with the count of new messages within this inbox.
+ * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
+ * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
+ * 
+ * This method is used when IMAP backend is used.
+ * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
  *
- * \return the zero on success -1 on error.
+ * \return zero on success, -1 on error.
  */
-static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
-{
-       int res = 0;
-       int fd = -1;
-       void *fdm = MAP_FAILED;
-       size_t fdlen = -1;
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char msgnums[20];
-       char fn[PATH_MAX];
-       char full_fn[PATH_MAX];
-       char fmt[80]="";
-       char *c;
-       struct ast_config *cfg=NULL;
-       struct odbc_obj *obj;
-       struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
-       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
 
-       delete_file(dir, msgnum);
-       if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-               return -1;
-       }
+static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+{
+       char tmp[PATH_MAX] = "";
+       char *mailboxnc;
+       char *context;
+       char *mb;
+       char *cur;
+       if (newmsgs)
+               *newmsgs = 0;
+       if (oldmsgs)
+               *oldmsgs = 0;
+       if (urgentmsgs)
+               *urgentmsgs = 0;
 
-       do {
-               ast_copy_string(fmt, vmfmts, sizeof(fmt));
-               c = strchr(fmt, '|');
-               if (c)
-                       *c = '\0';
-               if (!strcasecmp(fmt, "wav49"))
-                       strcpy(fmt, "WAV");
-               snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
-               if (msgnum > -1)
-                       make_file(fn, sizeof(fn), dir, msgnum);
-               else
-                       ast_copy_string(fn, dir, sizeof(fn));
-               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-               cfg = ast_config_load(full_fn, config_flags);
-               snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
-               fd = open(full_fn, O_RDWR);
-               if (fd < 0) {
-                       ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
-                       res = -1;
-                       break;
-               }
-               if (cfg) {
-                       if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
-                               idata.context = "";
-                       }
-                       if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
-                               idata.macrocontext = "";
-                       }
-                       if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
-                               idata.callerid = "";
-                       }
-                       if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
-                               idata.origtime = "";
-                       }
-                       if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
-                               idata.duration = "";
-                       }
-                       if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
-                               idata.category = "";
-                       }
-                       if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
-                               idata.flag = "";
+       ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox_context))
+               return 0;
+       
+       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+       context = strchr(tmp, '@');
+       if (strchr(mailbox_context, ',')) {
+               int tmpnew, tmpold, tmpurgent;
+               ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+               mb = tmp;
+               while ((cur = strsep(&mb, ", "))) {
+                       if (!ast_strlen_zero(cur)) {
+                               if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+                                       return -1;
+                               else {
+                                       if (newmsgs)
+                                               *newmsgs += tmpnew; 
+                                       if (oldmsgs)
+                                               *oldmsgs += tmpold;
+                                       if (urgentmsgs)
+                                               *urgentmsgs += tmpurgent;
+                               }
                        }
                }
-               fdlen = lseek(fd, 0, SEEK_END);
-               lseek(fd, 0, SEEK_SET);
-               printf("Length is %zd\n", fdlen);
-               fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
-               if (fdm == MAP_FAILED) {
-                       ast_log(AST_LOG_WARNING, "Memory map failed!\n");
-                       res = -1;
-                       break;
-               } 
-               idata.data = fdm;
-               idata.datalen = fdlen;
+               return 0;
+       }
+       if (context) {
+               *context = '\0';
+               mailboxnc = tmp;
+               context++;
+       } else {
+               context = "default";
+               mailboxnc = (char *)mailbox_context;
+       }
+       if (newmsgs) {
+               if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
+                       return -1;
+       }
+       if (oldmsgs) {
+               if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
+                       return -1;
+       }
+       if (urgentmsgs) {
+               if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
+                       return -1;
+       }
+       return 0;
+}
 
-               if (!ast_strlen_zero(idata.category)) 
-                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
-               else
-                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
+static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
+{
+       return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
+}
 
-               if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               } else {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       res = -1;
-               }
-       } while (0);
-       if (obj) {
-               ast_odbc_release_obj(obj);
-       }
-       if (cfg)
-               ast_config_destroy(cfg);
-       if (fdm != MAP_FAILED)
-               munmap(fdm, fdlen);
-       if (fd > -1)
-               close(fd);
-       return res;
-}
-
-/*!
- * \brief Renames a message in a mailbox folder.
- * \param sdir The folder of the message to be renamed.
- * \param smsg The index of the message to be renamed.
- * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
- * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
- * \param ddir The destination folder for the message to be renamed into
- * \param dmsg The destination message for the message to be renamed.
+/** 
+ * \brief Determines if the given folder has messages.
+ * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
+ * \param folder the folder to look in
  *
- * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
- * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
- * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
+ * This function is used when the mailbox is stored in an IMAP back end.
+ * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
+ * \return 1 if the folder has one or more messages. zero otherwise.
  */
-static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
-{
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char msgnums[20];
-       char msgnumd[20];
-       struct odbc_obj *obj;
-       char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
 
-       delete_file(ddir, dmsg);
-       obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
-               snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt)
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-               else
-                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-       return; 
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+       char tmp[256], *tmp2, *box, *context;
+       ast_copy_string(tmp, mailbox, sizeof(tmp));
+       tmp2 = tmp;
+       if (strchr(tmp2, ',')) {
+               while ((box = strsep(&tmp2, ","))) {
+                       if (!ast_strlen_zero(box)) {
+                               if (has_voicemail(box, folder))
+                                       return 1;
+                       }
+               }
+       }
+       if ((context= strchr(tmp, '@')))
+               *context++ = '\0';
+       else
+               context = "default";
+       return messagecount(context, tmp, folder) ? 1 : 0;
 }
 
-#else
-#ifndef IMAP_STORAGE
 /*!
- * \brief Find all .txt files - even if they are not in sequence from 0000.
+ * \brief Copies a message from one mailbox to another.
+ * \param chan
  * \param vmu
+ * \param imbox
+ * \param msgnum
+ * \param duration
+ * \param recip
+ * \param fmt
  * \param dir
  *
- * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ * This works with IMAP storage based mailboxes.
  *
- * \return the count of messages, zero or more.
+ * \return zero on success, -1 on error.
  */
-static int count_messages(struct ast_vm_user *vmu, char *dir)
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag)
+{
+       struct vm_state *sendvms = NULL, *destvms = NULL;
+       char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
+       if (msgnum >= recip->maxmsg) {
+               ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
+               return -1;
+       }
+       if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
+               ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
+               return -1;
+       }
+       if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
+               ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
+               return -1;
+       }
+       snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
+       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;
+}
+
+static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
 {
+       char tmp[256], *t = tmp;
+       size_t left = sizeof(tmp);
+       
+       if (box == OLD_FOLDER) {
+               ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
+       } else {
+               ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
+       }
 
-       int vmcount = 0;
-       DIR *vmdir = NULL;
-       struct dirent *vment = NULL;
+       if (box == NEW_FOLDER) {
+               ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
+       } else {
+               snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
+       }
 
-       if (vm_lock_path(dir))
-               return ERROR_LOCK_PATH;
+       /* Build up server information */
+       ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
 
-       if ((vmdir = opendir(dir))) {
-               while ((vment = readdir(vmdir))) {
-                       if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
-                               vmcount++;
-                       }
+       /* Add authentication user if present */
+       if (!ast_strlen_zero(authuser))
+               ast_build_string(&t, &left, "/authuser=%s", authuser);
+
+       /* Add flags if present */
+       if (!ast_strlen_zero(imapflags))
+               ast_build_string(&t, &left, "/%s", imapflags);
+
+       /* End with username */
+       ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
+       if (box == NEW_FOLDER || box == OLD_FOLDER)
+               snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
+       else if (box == GREETINGS_FOLDER)
+               snprintf(spec, len, "%s%s", tmp, greetingfolder);
+       else {  /* Other folders such as Friends, Family, etc... */
+               if (!ast_strlen_zero(imapparentfolder)) {
+                       /* imapparentfolder would typically be set to INBOX */
+                       snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
+               } else {
+                       snprintf(spec, len, "%s%s", tmp, mbox(box));
                }
-               closedir(vmdir);
        }
-       ast_unlock_path(dir);
-       
-       return vmcount;
 }
 
-/*!
- * \brief Renames a message in a mailbox folder.
- * \param sfn The path to the mailbox information and data file to be renamed.
- * \param dfn The path for where the message data and information files will be renamed to.
- *
- * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
- */
-static void rename_file(char *sfn, char *dfn)
+static int init_mailstream(struct vm_state *vms, int box)
 {
-       char stxt[PATH_MAX];
-       char dtxt[PATH_MAX];
-       ast_filerename(sfn,dfn,NULL);
-       snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
-       snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
-       if (ast_check_realtime("voicemail_data")) {
-               ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
+       MAILSTREAM *stream = NIL;
+       long debug;
+       char tmp[256];
+       
+       if (!vms) {
+               ast_log (LOG_ERROR,"vm_state is NULL!\n");
+               return -1;
+       }
+       if (option_debug > 2)
+               ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
+       if (vms->mailstream == NIL || !vms->mailstream) {
+               if (option_debug)
+                       ast_log (LOG_DEBUG,"mailstream not set.\n");
+       } else {
+               stream = vms->mailstream;
+       }
+       /* debug = T;  user wants protocol telemetry? */
+       debug = NIL;  /* NO protocol telemetry? */
+
+       if (delimiter == '\0') {                /* did not probe the server yet */
+               char *cp;
+#ifdef USE_SYSTEM_IMAP
+#include <imap/linkage.c>
+#elif defined(USE_SYSTEM_CCLIENT)
+#include <c-client/linkage.c>
+#else
+#include "linkage.c"
+#endif
+               /* Connect to INBOX first to get folders delimiter */
+               imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
+               ast_mutex_lock(&vms->lock);
+               stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+               ast_mutex_unlock(&vms->lock);
+               if (stream == NIL) {
+                       ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
+                       return -1;
+               }
+               get_mailbox_delimiter(stream);
+               /* update delimiter in imapfolder */
+               for (cp = imapfolder; *cp; cp++)
+                       if (*cp == '/')
+                               *cp = delimiter;
+       }
+       /* Now connect to the target folder */
+       imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
+       if (option_debug > 2)
+               ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
+       ast_mutex_lock(&vms->lock);
+       vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+       ast_mutex_unlock(&vms->lock);
+       if (vms->mailstream == NIL) {
+               return -1;
+       } else {
+               return 0;
        }
-       rename(stxt, dtxt);
 }
 
-/*! 
- * \brief Determines the highest message number in use for a given user and mailbox folder.
- * \param vmu 
- * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
- *
- * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
- * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
- *
- * \note Should always be called with a lock already set on dir.
- * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
- */
-static int last_message_index(struct ast_vm_user *vmu, char *dir)
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
 {
-       int x;
-       unsigned char map[MAXMSGLIMIT] = "";
-       DIR *msgdir;
-       struct dirent *msgdirent;
-       int msgdirint;
+       SEARCHPGM *pgm;
+       SEARCHHEADER *hdr;
+       int ret, urgent = 0;
 
-       /* Reading the entire directory into a file map scales better than
-        * doing a stat repeatedly on a predicted sequence.  I suspect this
-        * is partially due to stat(2) internally doing a readdir(2) itself to
-        * find each file. */
-       msgdir = opendir(dir);
-       while ((msgdirent = readdir(msgdir))) {
-               if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
-                       map[msgdirint] = 1;
+       /* If Urgent, then look at INBOX */
+       if (box == 11) {
+               box = NEW_FOLDER;
+               urgent = 1;
        }
-       closedir(msgdir);
 
-       for (x = 0; x < vmu->maxmsg; x++) {
-               if (map[x] == 0)
-                       break;
-       }
+       ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
+       ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
 
-       return x - 1;
-}
+       if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
+               ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
+               return -1;
+       }
+       
+       create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
+       
+       /* Check Quota */
+       if  (box == 0)  {
+               ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
+               check_quota(vms,(char *)mbox(box));
+       }
 
+       ast_mutex_lock(&vms->lock);
+       pgm = mail_newsearchpgm();
 
-/*!
- * \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.
- * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
- *
- * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
- * The copy operation copies up to 4096 bytes at once.
- *
- * \return zero on success, -1 on error.
- */
-static int copy(char *infile, char *outfile)
-{
-       int ifd;
-       int ofd;
-       int res;
-       int len;
-       char buf[4096];
+       /* Check IMAP folder for Asterisk messages only... */
+       hdr = mail_newsearchheader("X-Asterisk-VM-Extension", vmu->mailbox);
+       pgm->header = hdr;
+       pgm->deleted = 0;
+       pgm->undeleted = 1;
 
-#ifdef HARDLINK_WHEN_POSSIBLE
-       /* Hard link if possible; saves disk space & is faster */
-       if (link(infile, outfile)) {
-#endif
-               if ((ifd = open(infile, O_RDONLY)) < 0) {
-                       ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
-                       return -1;
-               }
-               if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
-                       ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
-                       close(ifd);
-                       return -1;
-               }
-               do {
-                       len = read(ifd, buf, sizeof(buf));
-                       if (len < 0) {
-                               ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
-                               close(ifd);
-                               close(ofd);
-                               unlink(outfile);
-                       }
-                       if (len) {
-                               res = write(ofd, buf, len);
-                               if (errno == ENOMEM || errno == ENOSPC || res != len) {
-                                       ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
-                                       close(ifd);
-                                       close(ofd);
-                                       unlink(outfile);
-                               }
-                       }
-               } while (len);
-               close(ifd);
-               close(ofd);
-               return 0;
-#ifdef HARDLINK_WHEN_POSSIBLE
-       } else {
-               /* Hard link succeeded */
-               return 0;
+       /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
+       if (box == NEW_FOLDER && urgent == 1) {
+               pgm->unseen = 1;
+               pgm->seen = 0;
+               pgm->flagged = 1;
+               pgm->unflagged = 0;
+       } else if (box == NEW_FOLDER && urgent == 0) {
+               pgm->unseen = 1;
+               pgm->seen = 0;
+               pgm->flagged = 0;
+               pgm->unflagged = 1;
+       } else if (box == OLD_FOLDER) {
+               pgm->seen = 1;
+               pgm->unseen = 0;
        }
-#endif
+
+       ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
+
+       vms->vmArrayIndex = 0;
+       mail_search_full (vms->mailstream, NULL, pgm, NIL);
+       vms->lastmsg = vms->vmArrayIndex - 1;
+       mail_free_searchpgm(&pgm);
+
+       ast_mutex_unlock(&vms->lock);
+       return 0;
 }
 
-/*!
- * \brief Copies a voicemail information (envelope) file.
- * \param frompath
- * \param topath 
- *
- * Every voicemail has the data (.wav) file, and the information file.
- * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
- * This is used by the COPY macro when not using IMAP storage.
- */
-static void copy_plain_file(char *frompath, char *topath)
+static void write_file(char *filename, char *buffer, unsigned long len)
 {
-       char frompath2[PATH_MAX], topath2[PATH_MAX];
-       struct ast_variable *tmp,*var = NULL;
-       const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
-       ast_filecopy(frompath, topath, NULL);
-       snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
-       snprintf(topath2, sizeof(topath2), "%s.txt", topath);
-       if (ast_check_realtime("voicemail_data")) {
-               var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
-               /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
-               for (tmp = var; tmp; tmp = tmp->next) {
-                       if (!strcasecmp(tmp->name, "origmailbox")) {
-                               origmailbox = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "context")) {
-                               context = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "macrocontext")) {
-                               macrocontext = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "exten")) {
-                               exten = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "priority")) {
-                               priority = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "callerchan")) {
-                               callerchan = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "callerid")) {
-                               callerid = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "origdate")) {
-                               origdate = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "origtime")) {
-                               origtime = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "category")) {
-                               category = tmp->value;
-                       } else if (!strcasecmp(tmp->name, "duration")) {
-                               duration = tmp->value;
-                       }
+       FILE *output;
+
+       output = fopen (filename, "w");
+       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));
                }
-               ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
        }
-       copy(frompath2, topath2);
-       ast_variables_destroy(var);
+       fclose (output);
 }
 
-#endif /* #ifndef IMAP_STORAGE */
-#endif /* #else of #ifdef ODBC_STORAGE */
-#ifndef ODBC_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.
- *
- * This is used by the DELETE macro when voicemails are stored on the file system.
- *
- * \return zero on success, -1 on error.
- */
-static int vm_delete(char *file)
+static void update_messages_by_imapuser(const char *user, unsigned long number)
 {
-       char *txt;
-       int txtsize = 0;
+       struct vmstate *vlist = NULL;
 
-       txtsize = (strlen(file) + 5)*sizeof(char);
-       txt = alloca(txtsize);
-       /* Sprintf here would safe because we alloca'd exactly the right length,
-        * but trying to eliminate all sprintf's anyhow
-        */
-       if (ast_check_realtime("voicemail_data")) {
-               ast_destroy_realtime("voicemail_data", "filename", file, NULL);
+       AST_LIST_LOCK(&vmstates);
+       AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+               if (!vlist->vms) {
+                       ast_debug(3, "error: vms is NULL for %s\n", user);
+                       continue;
+               }
+               if (!vlist->vms->imapuser) {
+                       ast_debug(3, "error: imapuser is NULL for %s\n", user);
+                       continue;
+               }
+               ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vlist->vms->vmArrayIndex, vlist->vms->interactive);
+               vlist->vms->msgArray[vlist->vms->vmArrayIndex++] = number;
        }
-       snprintf(txt, txtsize, "%s.txt", file);
-       unlink(txt);
-       return ast_filedelete(file, NULL);
+       AST_LIST_UNLOCK(&vmstates);
 }
-#endif
 
-/*!
- * \brief utility used by inchar(), for base_encode()
- */
-static int inbuf(struct baseio *bio, FILE *fi)
+void mm_searched(MAILSTREAM *stream, unsigned long number)
 {
-       int l;
-
-       if (bio->ateof)
-               return 0;
-
-       if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
-               if (ferror(fi))
-                       return -1;
-
-               bio->ateof = 1;
-               return 0;
-       }
+       char *mailbox = stream->mailbox, buf[1024] = "", *user;
 
-       bio->iolen= l;
-       bio->iocp= 0;
+       if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
+               return;
 
-       return 1;
+       update_messages_by_imapuser(user, number);
 }
 
-/*!
- * \brief utility used by base_encode()
- */
-static int inchar(struct baseio *bio, FILE *fi)
+static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
 {
-       if (bio->iocp>=bio->iolen) {
-               if (!inbuf(bio, fi))
-                       return EOF;
-       }
+       struct ast_variable *var;
+       struct ast_vm_user *vmu;
 
-       return bio->iobuf[bio->iocp++];
+       vmu = ast_calloc(1, sizeof *vmu);
+       if (!vmu)
+               return NULL;
+       ast_set_flag(vmu, VM_ALLOCED);
+       populate_defaults(vmu);
+
+       var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
+       if (var) {
+               apply_options_full(vmu, var);
+               ast_variables_destroy(var);
+               return vmu;
+       } else {
+               free(vmu);
+               return NULL;
+       }
 }
 
-/*!
- * \brief utility used by base_encode()
- */
-static int ochar(struct baseio *bio, int c, FILE *so)
+/* Interfaces to C-client */
+
+void mm_exists(MAILSTREAM * stream, unsigned long number)
 {
-       if (bio->linelength >= BASELINELEN) {
-               if (fputs(eol,so) == EOF)
-                       return -1;
+       /* mail_ping will callback here if new mail! */
+       ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
+       if (number == 0) return;
+       set_update(stream);
+}
 
-               bio->linelength= 0;
-       }
 
-       if (putc(((unsigned char)c),so) == EOF)
-               return -1;
+void mm_expunged(MAILSTREAM * stream, unsigned long number)
+{
+       /* mail_ping will callback here if expunged mail! */
+       ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
+       if (number == 0) return;
+       set_update(stream);
+}
 
-       bio->linelength++;
 
-       return 1;
+void mm_flags(MAILSTREAM * stream, unsigned long number)
+{
+       /* mail_ping will callback here if read mail! */
+       ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
+       if (number == 0) return;
+       set_update(stream);
 }
 
-/*!
- * \brief Performs a base 64 encode algorithm on the contents of a File
- * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
- * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
- *
- * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
- *
- * \return zero on success, -1 on error.
- */
-static int base_encode(char *filename, FILE *so)
+
+void mm_notify(MAILSTREAM * stream, char *string, long errflg)
 {
-       static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
-               'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
-               'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
-               '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
-       int i,hiteof= 0;
-       FILE *fi;
-       struct baseio bio;
+       ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
+       mm_log (string, errflg);
+}
 
-       memset(&bio, 0, sizeof(bio));
-       bio.iocp = BASEMAXINLINE;
 
-       if (!(fi = fopen(filename, "rb"))) {
-               ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
-               return -1;
+void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
+{
+       if (delimiter == '\0') {
+               delimiter = delim;
        }
 
-       while (!hiteof){
-               unsigned char igroup[3], ogroup[4];
-               int c,n;
-
-               igroup[0]= igroup[1]= igroup[2]= 0;
+       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)
+               ast_debug(5, "no select\n");
+       if (attributes & LATT_MARKED)
+               ast_debug(5, "marked\n");
+       if (attributes & LATT_UNMARKED)
+               ast_debug(5, "unmarked\n");
+}
 
-               for (n= 0;n<3;n++) {
-                       if ((c = inchar(&bio, fi)) == EOF) {
-                               hiteof= 1;
-                               break;
-                       }
 
-                       igroup[n]= (unsigned char)c;
-               }
+void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
+{
+       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)
+               ast_debug(5, "no select\n");
+       if (attributes & LATT_MARKED)
+               ast_debug(5, "marked\n");
+       if (attributes & LATT_UNMARKED)
+               ast_debug(5, "unmarked\n");
+}
 
-               if (n> 0) {
-                       ogroup[0]= dtable[igroup[0]>>2];
-                       ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
-                       ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
-                       ogroup[3]= dtable[igroup[2]&0x3F];
 
-                       if (n<3) {
-                               ogroup[3]= '=';
+void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
+{
+       ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
+       if (status->flags & SA_MESSAGES)
+               ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
+       if (status->flags & SA_RECENT)
+               ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
+       if (status->flags & SA_UNSEEN)
+               ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
+       if (status->flags & SA_UIDVALIDITY)
+               ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
+       if (status->flags & SA_UIDNEXT)
+               ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
+       ast_log(AST_LOG_NOTICE, "\n");
+}
 
-                               if (n<2)
-                                       ogroup[2]= '=';
-                       }
 
-                       for (i= 0;i<4;i++)
-                               ochar(&bio, ogroup[i], so);
-               }
+void mm_log(char *string, long errflg)
+{
+       switch ((short) errflg) {
+               case NIL:
+                       ast_debug(1,"IMAP Info: %s\n", string);
+                       break;
+               case PARSE:
+               case WARN:
+                       ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
+                       break;
+               case ERROR:
+                       ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
+                       break;
        }
-
-       fclose(fi);
-       
-       if (fputs(eol,so)==EOF)
-               return 0;
-
-       return 1;
 }
 
-static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
-{
-       char callerid[256];
-       /* Prepare variables for substitution in email body and subject */
-       pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
-       pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
-       snprintf(passdata, passdatasize, "%d", msgnum);
-       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_DATE", date);
-       pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
-       pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
-}
 
-/*!
- * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
- * \param from The string to work with.
- * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
- * 
- * \return The destination string with quotes wrapped on it (the to field).
- */
-static char *quote(const char *from, char *to, size_t len)
+void mm_dlog(char *string)
 {
-       char *ptr = to;
-       *ptr++ = '"';
-       for (; ptr < to + len - 1; from++) {
-               if (*from == '"')
-                       *ptr++ = '\\';
-               else if (*from == '\0')
-                       break;
-               *ptr++ = *from;
-       }
-       if (ptr < to + len - 1)
-               *ptr++ = '"';
-       *ptr = '\0';
-       return to;
+       ast_log(AST_LOG_NOTICE, "%s\n", string);
 }
 
-/*! \brief
- * fill in *tm for current time according to the proper timezone, if any.
- * Return tm so it can be used as a function argument.
- */
-static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
+
+void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
 {
-       const struct vm_zone *z = NULL;
-       struct timeval t = ast_tvnow();
+       struct ast_vm_user *vmu;
 
-       /* Does this user have a timezone specified? */
-       if (!ast_strlen_zero(vmu->zonetag)) {
-               /* Find the zone in the list */
-               AST_LIST_LOCK(&zones);
-               AST_LIST_TRAVERSE(&zones, z, list) {
-                       if (!strcmp(z->name, vmu->zonetag))
+       ast_debug(4, "Entering callback mm_login\n");
+
+       ast_copy_string(user, mb->user, MAILTMPLEN);
+
+       /* We should only do this when necessary */
+       if (!ast_strlen_zero(authpassword)) {
+               ast_copy_string(pwd, authpassword, MAILTMPLEN);
+       } else {
+               AST_LIST_TRAVERSE(&users, vmu, list) {
+                       if (!strcasecmp(mb->user, vmu->imapuser)) {
+                               ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
                                break;
+                       }
+               }
+               if (!vmu) {
+                       if ((vmu = find_user_realtime_imapuser(mb->user))) {
+                               ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
+                               free_user(vmu);
+                       }
                }
-               AST_LIST_UNLOCK(&zones);
        }
-       ast_localtime(&t, tm, z ? z->timezone : NULL);
-       return tm;
 }
 
-/*! \brief same as mkstemp, but return a FILE * */
-static FILE *vm_mkftemp(char *template)
+
+void mm_critical(MAILSTREAM * stream)
 {
-       FILE *p = NULL;
-       int pfd = mkstemp(template);
-       chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
-       if (pfd > -1) {
-               p = fdopen(pfd, "w+");
-               if (!p) {
-                       close(pfd);
-                       pfd = -1;
-               }
-       }
-       return p;
 }
 
-/*!
- * \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.
- * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
- * \param vmu The voicemail user who is sending the voicemail.
- * \param msgnum The message index in the mailbox folder.
- * \param context 
- * \param mailbox The voicemail box to read the voicemail to be notified in this email.
- * \param cidnum The caller ID number.
- * \param cidname The caller ID name.
- * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
- * \param format The message sound file format. i.e. .wav
- * \param duration The time of the message content, in seconds.
- * \param attach_user_voicemail if 1, the sound file is attached to the email.
- * \param chan
- * \param category
- * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
- *
- * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email.  That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
- */
-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag)
+
+void mm_nocritical(MAILSTREAM * stream)
 {
-       char date[256];
-       char host[MAXHOSTNAMELEN] = "";
-       char who[256];
-       char bound[256];
-       char dur[256];
-       struct ast_tm tm;
-       char *passdata2;
-       size_t len_passdata;
-       char *greeting_attachment; 
-       char filename[256];
+}
 
-#ifdef IMAP_STORAGE
-#define ENDL "\r\n"
-#else
-#define ENDL "\n"
-#endif
 
-       gethostname(host, sizeof(host)-1);
+long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
+{
+       kill (getpid (), SIGSTOP);
+       return NIL;
+}
 
-       if (strchr(srcemail, '@'))
-               ast_copy_string(who, srcemail, sizeof(who));
-       else 
-               snprintf(who, sizeof(who), "%s@%s", srcemail, host);
-       
-       greeting_attachment = strrchr(ast_strdupa(attach), '/');
-       if (greeting_attachment)
-               *greeting_attachment++ = '\0';
 
-       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
-       ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
-       fprintf(p, "Date: %s" ENDL, date);
+void mm_fatal(char *string)
+{
+       ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
+}
 
-       /* Set date format for voicemail mail */
-       ast_strftime(date, sizeof(date), emaildateformat, &tm);
-
-       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, cidnum, 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);
-                       ast_channel_free(ast);
-               } else
-                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
-       } 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 (!ast_strlen_zero(emailsubject)) {
-               struct ast_channel *ast;
-               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
-                       char *passdata;
-                       int vmlen = strlen(emailsubject) * 3 + 200;
-                       passdata = alloca(vmlen);
-                       memset(passdata, 0, vmlen);
-                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
-                       pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
-                       fprintf(p, "Subject: %s" ENDL, passdata);
-                       ast_channel_free(ast);
-               } else
-                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
-       } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
-               if (ast_strlen_zero(flag)) {
-                       fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
-               } else {
-                       fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
-               }
-       } else {
-               if (ast_strlen_zero(flag)) {
-                       fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
-               } else {
-                       fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
-               }
+/* C-client callback to handle quota */
+static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
+{
+       struct vm_state *vms;
+       char *mailbox = stream->mailbox, *user;
+       char buf[1024] = "";
+       unsigned long usage = 0, limit = 0;
+       
+       while (pquota) {
+               usage = pquota->usage;
+               limit = pquota->limit;
+               pquota = pquota->next;
        }
-
-       fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
-       if (imap) {
-               /* additional information needed for IMAP searching */
-               fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
-               /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
-               fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
-               fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
-               fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
-                /* flag added for Urgent */
-               fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
-               fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
-               fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
-               fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
-               fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
-               fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
-               if (!ast_strlen_zero(category)) 
-                       fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
-               fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
-               fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
-               fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
+       
+       if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 2))) {
+               ast_log(AST_LOG_ERROR, "No state found.\n");
+               return;
        }
-       if (!ast_strlen_zero(cidnum))
-               fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
-       if (!ast_strlen_zero(cidname))
-               fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
-       fprintf(p, "MIME-Version: 1.0" ENDL);
-       if (attach_user_voicemail) {
-               /* Something unique. */
-               snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
 
-               fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
-               fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
-               fprintf(p, "--%s" ENDL, bound);
-       }
-       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))) {
-                       char *passdata;
-                       int vmlen = strlen(emailbody)*3 + 200;
-                       passdata = alloca(vmlen);
-                       memset(passdata, 0, vmlen);
-                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
-                       pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
-                       fprintf(p, "%s" ENDL, passdata);
-                       ast_channel_free(ast);
-               } else
-                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
-       } else if (msgnum > -1){
-               fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long %s message (number %d)" ENDL
-               "in mailbox %s from %s, on %s so you might" ENDL
-               "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
-               dur, flag, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
-       } else {
-               fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
-                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
-       }
+       ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
 
-       if (attach_user_voicemail) {
-               if (!ast_strlen_zero(attach2)) {
-                       snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
-                       ast_debug(5, "creating attachment filename %s\n", filename);
-                       add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 0, msgnum);
-                       snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
-                       ast_debug(5, "creating second attachment filename %s\n", filename);
-                       add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
-               } else {
-                       snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
-                       ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
-                       add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
-               }
-       }
+       vms->quota_usage = usage;
+       vms->quota_limit = limit;
 }
 
-static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
+static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
 {
-       char tmpdir[256], newtmp[256];
-       char fname[256];
-       char tmpcmd[256];
-       int tmpfd = -1;
+       char *start, *eol_pnt;
+       int taglen;
 
-       /* Eww. We want formats to tell us their own MIME type */
-       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+       if (ast_strlen_zero(header) || ast_strlen_zero(tag))
+               return NULL;
 
-       if (vmu->volgain < -.001 || vmu->volgain > .001) {
-               create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
-               snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
-               tmpfd = mkstemp(newtmp);
-               chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
-               ast_debug(3, "newtmp: %s\n", newtmp);
-               if (tmpfd > -1) {
-                       snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
-                       ast_safe_system(tmpcmd);
-                       attach = newtmp;
-                       ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
-               }
-       }
-       fprintf(p, "--%s" ENDL, bound);
-       if (msgnum > -1)
-               fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
-       else
-               fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
-       fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
-       fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
-       if (msgnum > -1)
-               fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
-       else
-               fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
-       snprintf(fname, sizeof(fname), "%s.%s", attach, format);
-       base_encode(fname, p);
-       if (last)
-               fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
-       if (tmpfd > -1) {
-               unlink(fname);
-               close(tmpfd);
-               unlink(newtmp);
-       }
-       return 0;
+       taglen = strlen(tag) + 1;
+       if (taglen < 1)
+               return NULL;
+
+       if (!(start = strstr(header, tag)))
+               return NULL;
+
+       /* Since we can be called multiple times we should clear our buffer */
+       memset(buf, 0, len);
+
+       ast_copy_string(buf, start+taglen, len);
+       if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
+               *eol_pnt = '\0';
+       return buf;
 }
-#undef ENDL
 
-static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
+static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
 {
-       FILE *p=NULL;
-       char tmp[80] = "/tmp/astmail-XXXXXX";
-       char tmp2[256];
+       char *start, *quote, *eol_pnt;
 
-       if (vmu && ast_strlen_zero(vmu->email)) {
-               ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
-               return(0);
-       }
-       if (!strcmp(format, "wav49"))
-               format = "WAV";
-       ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
-       /* Make a temporary file instead of piping directly to sendmail, in case the mail
-          command hangs */
-       if ((p = vm_mkftemp(tmp)) == NULL) {
-               ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
-               return -1;
+       if (ast_strlen_zero(mailbox))
+               return NULL;
+
+       if (!(start = strstr(mailbox, "/user=")))
+               return NULL;
+
+       ast_copy_string(buf, start+6, len);
+
+       if (!(quote = strchr(buf, '\"'))) {
+               if (!(eol_pnt = strchr(buf, '/')))
+                       eol_pnt = strchr(buf,'}');
+               *eol_pnt = '\0';
+               return buf;
        } else {
-               make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
-               fclose(p);
-               snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
-               ast_safe_system(tmp2);
-               ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
+               eol_pnt = strchr(buf+1,'\"');
+               *eol_pnt = '\0';
+               return buf+1;
        }
-       return 0;
 }
 
-static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
+static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
 {
-       char date[256];
-       char host[MAXHOSTNAMELEN] = "";
-       char who[256];
-       char dur[PATH_MAX];
-       char tmp[80] = "/tmp/astmail-XXXXXX";
-       char tmp2[PATH_MAX];
-       struct ast_tm tm;
-       FILE *p;
+       struct vm_state *vms_p;
 
-       if ((p = vm_mkftemp(tmp)) == NULL) {
-               ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
-               return -1;
-       }
-       gethostname(host, sizeof(host)-1);
-       if (strchr(srcemail, '@'))
-               ast_copy_string(who, srcemail, sizeof(who));
-       else 
-               snprintf(who, sizeof(who), "%s@%s", srcemail, host);
-       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
-       ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
-       fprintf(p, "Date: %s\n", date);
+       if (option_debug > 4)
+               ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
+       if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
+               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);
+       vms_p->updated = 1;
+       /* set mailbox to INBOX! */
+       ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
+       init_vm_state(vms_p);
+       vmstate_insert(vms_p);
+       return vms_p;
+}
 
-       if (*pagerfromstring) {
-               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, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
-                       pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
-                       fprintf(p, "From: %s <%s>\n", passdata, who);
-                       ast_channel_free(ast);
-               } else 
-                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
-       } else
-               fprintf(p, "From: Asterisk PBX <%s>\n", who);
-       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))) {
-                       char *passdata;
-                       int vmlen = strlen(pagersubject) * 3 + 200;
-                       passdata = alloca(vmlen);
-                       memset(passdata, 0, vmlen);
-                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
-                       pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
-                       fprintf(p, "Subject: %s\n\n", passdata);
-                       ast_channel_free(ast);
-               } else
-                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
-       } else {
-               if (ast_strlen_zero(flag)) {
-                       fprintf(p, "Subject: New VM\n\n");
-               } else {
-                       fprintf(p, "Subject: New %s VM\n\n", flag);
+static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
+{
+       struct vmstate *vlist = NULL;
+
+       AST_LIST_LOCK(&vmstates);
+       AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+               if (!vlist->vms) {
+                       ast_debug(3, "error: vms is NULL for %s\n", user);
+                       continue;
+               }
+               if (!vlist->vms->imapuser) {
+                       ast_debug(3, "error: imapuser is NULL for %s\n", user);
+                       continue;
+               }
+
+               if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
+                       AST_LIST_UNLOCK(&vmstates);
+                       return vlist->vms;
                }
        }
+       AST_LIST_UNLOCK(&vmstates);
 
-       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))) {
-                       char *passdata;
-                       int vmlen = strlen(pagerbody) * 3 + 200;
-                       passdata = alloca(vmlen);
-                       memset(passdata, 0, vmlen);
-                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
-                       pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
-                       fprintf(p, "%s\n", passdata);
-                       ast_channel_free(ast);
-               } else
-                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
-       } else {
-               fprintf(p, "New %s long %s msg in box %s\n"
-                               "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
+       ast_debug(3, "%s not found in vmstates\n", user);
+
+       return NULL;
+}
+
+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) {
+               if (!vlist->vms) {
+                       ast_debug(3, "error: vms is NULL for %s\n", mailbox);
+                       continue;
+               }
+               if (!vlist->vms->username || !vlist->vms->context) {
+                       ast_debug(3, "error: username is NULL for %s\n", mailbox);
+                       continue;
+               }
+
+               ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
+               
+               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;
+               }
        }
-       fclose(p);
-       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
-       ast_safe_system(tmp2);
-       ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
-       return 0;
+       AST_LIST_UNLOCK(&vmstates);
+
+       ast_debug(3, "%s not found in vmstates\n", mailbox);
+
+       return NULL;
 }
 
-/*!
- * \brief Gets the current date and time, as formatted string.
- * \param s The buffer to hold the output formatted date.
- * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
- * 
- * The date format string used is "%a %b %e %r UTC %Y".
- * 
- * \return zero on success, -1 on error.
- */
-static int get_date(char *s, int len)
+static void vmstate_insert(struct vm_state *vms) 
 {
-       struct ast_tm tm;
-       struct timeval t = ast_tvnow();
+       struct vmstate *v;
+       struct vm_state *altvms;
+
+       /* If interactive, it probably already exists, and we should
+          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, vms->context, 0);
+               if (altvms) {   
+                       ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
+                       vms->newmessages = altvms->newmessages;
+                       vms->oldmessages = altvms->oldmessages;
+                       vms->vmArrayIndex = altvms->vmArrayIndex;
+                       vms->lastmsg = altvms->lastmsg;
+                       vms->curmsg = altvms->curmsg;
+                       /* get a pointer to the persistent store */
+                       vms->persist_vms = altvms;
+                       /* Reuse the mailstream? */
+                       vms->mailstream = altvms->mailstream;
+                       /* vms->mailstream = NIL; */
+               }
+       }
+
+       if (!(v = ast_calloc(1, sizeof(*v))))
+               return;
        
-       ast_localtime(&t, &tm, "UTC");
+       v->vms = vms;
 
-       return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
+       ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
+
+       AST_LIST_LOCK(&vmstates);
+       AST_LIST_INSERT_TAIL(&vmstates, v, list);
+       AST_LIST_UNLOCK(&vmstates);
 }
 
-static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
+static void vmstate_delete(struct vm_state *vms) 
 {
-       int res;
-       char fn[PATH_MAX];
-       char dest[PATH_MAX];
-
-       snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
+       struct vmstate *vc = NULL;
+       struct vm_state *altvms = NULL;
 
-       if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
-               ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
-               return -1;
+       /* If interactive, we should copy pertinent info
+          back to the persistent state (to make update immediate) */
+       if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
+               ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
+               altvms->newmessages = vms->newmessages;
+               altvms->oldmessages = vms->oldmessages;
+               altvms->updated = 1;
        }
-
-       RETRIEVE(fn, -1, ext, context);
-       if (ast_fileexists(fn, NULL, NULL) > 0) {
-               res = ast_stream_and_wait(chan, fn, ecodes);
-               if (res) {
-                       DISPOSE(fn, -1);
-                       return res;
+       
+       ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
+       
+       AST_LIST_LOCK(&vmstates);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
+               if (vc->vms == vms) {
+                       AST_LIST_REMOVE_CURRENT(list);
+                       break;
                }
-       } else {
-               /* Dispose just in case */
-               DISPOSE(fn, -1);
-               res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
-               if (res)
-                       return res;
-               res = ast_say_digit_str(chan, ext, ecodes, chan->language);
-               if (res)
-                       return res;
        }
-       res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
-       return res;
+       AST_LIST_TRAVERSE_SAFE_END
+       AST_LIST_UNLOCK(&vmstates);
+       
+       if (vc) {
+               ast_mutex_destroy(&vc->vms->lock);
+               ast_free(vc);
+       }
+       else
+               ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
 }
 
-static void free_user(struct ast_vm_user *vmu)
+static void set_update(MAILSTREAM * stream) 
 {
-       if (!ast_test_flag(vmu, VM_ALLOCED))
+       struct vm_state *vms;
+       char *mailbox = stream->mailbox, *user;
+       char buf[1024] = "";
+
+       if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
+               if (user && option_debug > 2)
+                       ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
                return;
+       }
+
+       ast_debug(3, "User %s mailbox set for update.\n", user);
 
-       ast_free(vmu);
+       vms->updated = 1; /* Set updated flag since mailbox changed */
 }
 
-static void free_zone(struct vm_zone *z)
+static void init_vm_state(struct vm_state *vms) 
 {
-       ast_free(z);
+       int x;
+       vms->vmArrayIndex = 0;
+       for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
+               vms->msgArray[x] = 0;
+       }
+       ast_mutex_init(&vms->lock);
 }
 
-/*!
- * \brief Gets the name of the mailbox folder from the numeric index.
- * \param id The numerical index for the folder name.
- * 
- * When an invalid number is entered, or one that exceeds the pre-configured list of folder names, the name "tmp" is returned.
- *
- * \return the String name that coresponds to this folder index.
- */
-static const char *mbox(int id)
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
 {
-       static const char *msgs[] = {
-#ifdef IMAP_STORAGE
-               imapfolder,
-#else
-               "INBOX",
-#endif
-               "Old",
-               "Work",
-               "Family",
-               "Friends",
-               "Cust1",
-               "Cust2",
-               "Cust3",
-               "Cust4",
-               "Cust5",
-               "Deleted",
-               "Urgent"
-       };
-       return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
+       char *body_content;
+       char *body_decoded;
+       char *fn = is_intro ? vms->introfn : vms->fn;
+       unsigned long len;
+       unsigned long newlen;
+       char filename[256];
+       
+       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); */
+               body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
+               /* If the body of the file is empty, return an error */
+               if (!newlen) {
+                       return -1;
+               }
+               write_file(filename, (char *) body_decoded, newlen);
+       } else {
+               ast_debug(5, "Body of message is NULL.\n");
+               return -1;
+       }
+       return 0;
 }
-#ifdef IMAP_STORAGE
-/*!
- * \brief Converts a string folder name into the numerical identifier.
- * \param folder the string folder name to be converted to an id.
+
+/*! 
+ * \brief Get delimiter via mm_list callback 
+ * \param stream
  *
- * This is the opposite of the mbox() function.
+ * 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);
+       mail_list(stream, tmp, "*");
+}
+
+/*! 
+ * \brief Check Quota for user 
+ * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
+ * \param mailbox the mailbox to check the quota for.
  *
- * \return the id that coresponds to the folder name
+ * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
  */
-static int folder_int(const char *folder)
+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) {
+               imap_getquotaroot(vms->mailstream, mailbox);
+       } else {
+               ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
+       }
+       ast_mutex_unlock(&vms->lock);
+}
+
+#endif /* IMAP_STORAGE */
+
+/*! \brief Lock file path
+    only return failure if ast_lock_path returns 'timeout',
+   not if the path does not exist or any other reason
+*/
+static int vm_lock_path(const char *path)
 {
-       /* assume a NULL folder means INBOX */
-       if (!folder)
-               return 0;
-#ifdef IMAP_STORAGE
-       if (!strcasecmp(folder, imapfolder))
-#else
-       if (!strcasecmp(folder, "INBOX"))
-#endif
-               return 0;
-       else if (!strcasecmp(folder, "Old"))
-               return 1;
-       else if (!strcasecmp(folder, "Work"))
-               return 2;
-       else if (!strcasecmp(folder, "Family"))
-               return 3;
-       else if (!strcasecmp(folder, "Friends"))
-               return 4;
-       else if (!strcasecmp(folder, "Cust1"))
-               return 5;
-       else if (!strcasecmp(folder, "Cust2"))
-               return 6;
-       else if (!strcasecmp(folder, "Cust3"))
-               return 7;
-       else if (!strcasecmp(folder, "Cust4"))
-               return 8;
-       else if (!strcasecmp(folder, "Cust5"))
-               return 9;
-       else if (!strcasecmp(folder, "Deleted"))
-               return 10;
-       else if (!strcasecmp(folder, "Urgent"))
-               return 11;
-       else /*assume they meant INBOX if folder is not found otherwise*/
+       switch (ast_lock_path(path)) {
+       case AST_LOCK_TIMEOUT:
+               return -1;
+       default:
                return 0;
+       }
 }
-#endif
+
 
 #ifdef ODBC_STORAGE
-/*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
-static int inboxcount(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+struct generic_prepare_struct {
+       char *sql;
+       int argc;
+       char **argv;
+};
+
+static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
 {
-       int x = -1;
-       int res;
+       struct generic_prepare_struct *gps = data;
+       int res, i;
        SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char rowdata[20];
-       char tmp[PATH_MAX] = "";
-       struct odbc_obj *obj;
-       char *context;
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
 
-       if (newmsgs)
-               *newmsgs = 0;
-       if (oldmsgs)
-               *oldmsgs = 0;
-       if (urgentmsgs)
-               *urgentmsgs = 0;
+       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
+               return NULL;
+       }
+       res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               return NULL;
+       }
+       for (i = 0; i < gps->argc; i++)
+               SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
 
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
-               return 0;
+       return stmt;
+}
 
-       ast_copy_string(tmp, mailbox, sizeof(tmp));
-       
-       context = strchr(tmp, '@');
-       if (context) {
-               *context = '\0';
-               context++;
-       } else
-               context = "default";
-       
+/*!
+ * \brief Retrieves a file from an ODBC data store.
+ * \param dir the path to the file to be retreived.
+ * \param msgnum the message number, such as within a mailbox folder.
+ * 
+ * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
+ * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
+ *
+ * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
+ * The output is the message information file with the name msgnum and the extension .txt
+ * and the message file with the extension of its format, in the directory with base file name of the msgnum.
+ * 
+ * \return 0 on success, -1 on error.
+ */
+static int retrieve_file(char *dir, int msgnum)
+{
+       int x = 0;
+       int res;
+       int fd=-1;
+       size_t fdlen = 0;
+       void *fdm = MAP_FAILED;
+       SQLSMALLINT colcount=0;
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char fmt[80]="";
+       char *c;
+       char coltitle[256];
+       SQLSMALLINT collen;
+       SQLSMALLINT datatype;
+       SQLSMALLINT decimaldigits;
+       SQLSMALLINT nullable;
+       SQLULEN colsize;
+       SQLLEN colsize2;
+       FILE *f=NULL;
+       char rowdata[80];
+       char fn[PATH_MAX];
+       char full_fn[PATH_MAX];
+       char msgnums[80];
+       char *argv[] = { dir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+
+       struct odbc_obj *obj;
        obj = ast_odbc_request_obj(odbc_database, 0);
        if (obj) {
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
+               ast_copy_string(fmt, vmfmts, sizeof(fmt));
+               c = strchr(fmt, '|');
+               if (c)
+                       *c = '\0';
+               if (!strcasecmp(fmt, "wav49"))
+                       strcpy(fmt, "WAV");
+               snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+               if (msgnum > -1)
+                       make_file(fn, sizeof(fn), dir, msgnum);
+               else
+                       ast_copy_string(fn, dir, sizeof(fn));
+
+               /* Create the information file */
+               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+               
+               if (!(f = fopen(full_fn, "w+"))) {
+                       ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
                        goto yuck;
                }
-               *newmsgs = atoi(rowdata);
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
+               
+               snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+               snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
                stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
                if (!stmt) {
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
@@ -2922,3914 +2912,4360 @@ static int inboxcount(const char *mailbox, int *urgentmsgs, int *newmsgs, int *o
                        goto yuck;
                }
                res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+               if (res == SQL_NO_DATA) {
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                        ast_odbc_release_obj(obj);
                        goto yuck;
-               }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+               } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                        ast_odbc_release_obj(obj);
                        goto yuck;
                }
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               *oldmsgs = atoi(rowdata);
-               
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+               fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
+               if (fd < 0) {
+                       ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                        ast_odbc_release_obj(obj);
                        goto yuck;
                }
-               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+               res = SQLNumResultCols(stmt, &colcount);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
+                       ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                        ast_odbc_release_obj(obj);
                        goto yuck;
                }
-               *urgentmsgs = atoi(rowdata);
-               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-               x = 0;
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-               
-yuck:  
-       return x;
-}
-
-/*!
- * \brief Gets the number of messages that exist in a mailbox folder.
- * \param context
- * \param mailbox
- * \param folder
- * 
- * This method is used when ODBC backend is used.
- * \return The number of messages in this mailbox folder (zero or more).
+               if (f) 
+                       fprintf(f, "[message]\n");
+               for (x=0;x<colcount;x++) {
+                       rowdata[0] = '\0';
+                       collen = sizeof(coltitle);
+                       res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
+                                               &datatype, &colsize, &decimaldigits, &nullable);
+                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                               ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+                               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                               ast_odbc_release_obj(obj);
+                               goto yuck;
+                       }
+                       if (!strcasecmp(coltitle, "recording")) {
+                               off_t offset;
+                               res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
+                               fdlen = colsize2;
+                               if (fd > -1) {
+                                       char tmp[1]="";
+                                       lseek(fd, fdlen - 1, SEEK_SET);
+                                       if (write(fd, tmp, 1) != 1) {
+                                               close(fd);
+                                               fd = -1;
+                                               continue;
+                                       }
+                                       /* Read out in small chunks */
+                                       for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
+                                               if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
+                                                       ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
+                                                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+                                                       ast_odbc_release_obj(obj);
+                                                       goto yuck;
+                                               } else {
+                                                       res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
+                                                       munmap(fdm, CHUNKSIZE);
+                                                       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                                                               ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                                                               unlink(full_fn);
+                                                               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+                                                               ast_odbc_release_obj(obj);
+                                                               goto yuck;
+                                                       }
+                                               }
+                                       }
+                                       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);
+                               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                                       ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
+                                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                                       ast_odbc_release_obj(obj);
+                                       goto yuck;
+                               }
+                               if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
+                                       fprintf(f, "%s=%s\n", coltitle, rowdata);
+                       }
+               }
+               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:  
+       if (f)
+               fclose(f);
+       if (fd > -1)
+               close(fd);
+       return x - 1;
+}
+
+/*!
+ * \brief Determines the highest message number in use for a given user and mailbox folder.
+ * \param vmu 
+ * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
+ *
+ * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
  */
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
 {
-       struct odbc_obj *obj = NULL;
-       int nummsgs = 0;
+       int x = 0;
        int res;
-       SQLHSTMT stmt = NULL;
+       SQLHSTMT stmt;
        char sql[PATH_MAX];
        char rowdata[20];
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
-       if (!folder)
-               folder = "INBOX";
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
-               return 0;
+       char *argv[] = { dir };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
 
+       struct odbc_obj *obj;
        obj = ast_odbc_request_obj(odbc_database, 0);
        if (obj) {
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
                stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
                if (!stmt) {
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       ast_odbc_release_obj(obj);
                        goto yuck;
                }
                res = SQLFetch(stmt);
                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                        ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
                        goto yuck;
                }
                res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
                if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
                        ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
                        goto yuck;
                }
-               nummsgs = atoi(rowdata);
+               if (sscanf(rowdata, "%d", &x) != 1)
+                       ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
        } else
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:  
+       return x - 1;
+}
 
-yuck:
-       if (obj)
+/*!
+ * \brief Determines if the specified message exists.
+ * \param dir the folder the mailbox folder to look for messages. 
+ * \param msgnum the message index to query for.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ *
+ * \return greater than zero if the message exists, zero when the message does not exist or on error.
+ */
+static int message_exists(char *dir, int msgnum)
+{
+       int x = 0;
+       int res;
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char rowdata[20];
+       char msgnums[20];
+       char *argv[] = { dir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+
+       struct odbc_obj *obj;
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt) {
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLFetch(stmt);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               if (sscanf(rowdata, "%d", &x) != 1)
+                       ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
+               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
                ast_odbc_release_obj(obj);
-       return nummsgs;
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:  
+       return x;
 }
 
-/** 
- * \brief Determines if the given folder has messages.
- * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- * 
- * This function is used when the mailbox is stored in an ODBC back end.
- * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
- * \return 1 if the folder has one or more messages. zero otherwise.
+/*!
+ * \brief returns the one-based count for messages.
+ * \param vmu
+ * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
+ * one-based messages.
+ * This method just calls last_message_index and returns +1 of its value.
+ *
+ * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
  */
-static int has_voicemail(const char *mailbox, const char *folder)
+static int count_messages(struct ast_vm_user *vmu, char *dir)
 {
-       char tmp[256], *tmp2 = tmp, *mbox, *context;
-       ast_copy_string(tmp, mailbox, sizeof(tmp));
-       while ((context = mbox = strsep(&tmp2, ","))) {
-               strsep(&context, "@");
-               if (ast_strlen_zero(context))
-                       context = "default";
-               if (messagecount(context, mbox, folder))
-                       return 1;
-       }
-       return 0;
+       return last_message_index(vmu, dir) + 1;
 }
 
-#elif defined(IMAP_STORAGE)
-
-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, char *introfile, const char *flag)
+/*!
+ * \brief Deletes a message from the mailbox folder.
+ * \param sdir The mailbox folder to work in.
+ * \param smsg The message index to be deleted.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * The specified message is directly deleted from the database 'voicemessages' table.
+ * 
+ * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
+ */
+static void delete_file(char *sdir, int smsg)
 {
-       char *myserveremail = serveremail;
-       char fn[PATH_MAX];
-       char intro[PATH_MAX];
-       char mailbox[256];
-       char *stringp;
-       FILE *p=NULL;
-       char tmp[80] = "/tmp/astmail-XXXXXX";
-       long len;
-       void *buf;
-       int tempcopy = 0;
-       STRING str;
-       int ret; /* for better error checking */
-       char *imapflags = 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";
-       }
-       
-       /* Attach only the first format */
-       fmt = ast_strdupa(fmt);
-       stringp = fmt;
-       strsep(&stringp, "|");
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char msgnums[20];
+       char *argv[] = { sdir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
 
-       if (!ast_strlen_zero(vmu->serveremail))
-               myserveremail = vmu->serveremail;
+       struct odbc_obj *obj;
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+               snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt)
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               else
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+       return; 
+}
 
-       if (msgnum > -1)
-               make_file(fn, sizeof(fn), dir, msgnum);
-       else
-               ast_copy_string (fn, dir, sizeof(fn));
-       
-       if (ast_strlen_zero(vmu->email)) {
-               /* We need the vmu->email to be set when we call make_email_file, but
-                * if we keep it set, a duplicate e-mail will be created. So at the end
-                * of this function, we will revert back to an empty string if tempcopy
-                * is 1.
-                */
-               ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
-               tempcopy = 1;
-       }
+/*!
+ * \brief Copies a voicemail from one mailbox to another.
+ * \param sdir the folder for which to look for the message to be copied.
+ * \param smsg the index of the message to be copied.
+ * \param ddir the destination folder to copy the message into.
+ * \param dmsg the index to be used for the copied message.
+ * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
+ * \param dmailboxcontext The context for the destination user.
+ *
+ * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
+ */
+static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
+{
+       SQLHSTMT stmt;
+       char sql[512];
+       char msgnums[20];
+       char msgnumd[20];
+       struct odbc_obj *obj;
+       char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
 
-       if (!ast_strlen_zero(introfile)) {
-               snprintf(intro, sizeof(intro), "%s/msgintro%04d", dir, msgnum);
-       } else {
-               intro[0] = '\0';
-       }
+       delete_file(ddir, dmsg);
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+               snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext, flag) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt)
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+               else
+                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+       return; 
+}
 
-       if (!strcmp(fmt, "wav49"))
-               fmt = "WAV";
-       ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
+struct insert_data {
+       char *sql;
+       char *dir;
+       char *msgnums;
+       void *data;
+       SQLLEN datalen;
+       SQLLEN indlen;
+       const char *context;
+       const char *macrocontext;
+       const char *callerid;
+       const char *origtime;
+       const char *duration;
+       char *mailboxuser;
+       char *mailboxcontext;
+       const char *category;
+       const char *flag;
+};
 
-       /* Make a temporary file instead of piping directly to sendmail, in case the mail
-          command hangs. */
-       if (!(p = vm_mkftemp(tmp))) {
-               ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
-               if (tempcopy)
-                       *(vmu->email) = '\0';
-               return -1;
-       }
+static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
+{
+       struct insert_data *data = vdata;
+       int res;
+       SQLHSTMT stmt;
 
-       if (msgnum < 0 && imapgreetings) {
-               if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
-                       ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
-                       return -1;
-               }
-               imap_delete_old_greeting(fn, vms);
+       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               return NULL;
        }
-       
-       make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, intro, fmt, duration, 1, chan, NULL, 1, flag);
-       /* read mail file to memory */          
-       len = ftell(p);
-       rewind(p);
-       if (!(buf = ast_malloc(len + 1))) {
-               ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
-               fclose(p);
-               if (tempcopy)
-                       *(vmu->email) = '\0';
-               return -1;
+
+       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
+       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
+       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
+       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
+       SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
+       SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
+       SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
+       SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
+       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
+       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
+       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
+       if (!ast_strlen_zero(data->category)) {
+               SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
        }
-       fread(buf, len, 1, p);
-       ((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_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
-               fclose(p);
-               unlink(tmp);
-               ast_free(buf);
-       } else {
-               ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
-               fclose(p);
-               unlink(tmp);
-               ast_free(buf);
-               return -1;
+       res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               return NULL;
        }
-       ast_debug(3, "%s stored\n", fn);
-       
-       if (tempcopy)
-               *(vmu->email) = '\0';
-       
-       return 0;
 
+       return stmt;
 }
 
 /*!
- * \brief Gets the number of messages that exist in a mailbox folder.
- * \param context
- * \param mailbox
- * \param folder
- * 
- * This method is used when IMAP backend is used.
- * \return The number of messages in this mailbox folder (zero or more).
+ * \brief Stores a voicemail into the database.
+ * \param dir the folder the mailbox folder to store the message.
+ * \param mailboxuser the user owning the mailbox folder.
+ * \param mailboxcontext
+ * \param msgnum the message index for the message to be stored.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * The message sound file and information file is looked up on the file system. 
+ * A SQL query is invoked to store the message into the (MySQL) database.
+ *
+ * \return the zero on success -1 on error.
  */
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
 {
-       SEARCHPGM *pgm;
-       SEARCHHEADER *hdr;
-
-       struct ast_vm_user *vmu, vmus;
-       struct vm_state *vms_p;
-       int ret = 0;
-       int fold = folder_int(folder);
-       
-       if (ast_strlen_zero(mailbox))
-               return 0;
+       int res = 0;
+       int fd = -1;
+       void *fdm = MAP_FAILED;
+       size_t fdlen = -1;
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char msgnums[20];
+       char fn[PATH_MAX];
+       char full_fn[PATH_MAX];
+       char fmt[80]="";
+       char *c;
+       struct ast_config *cfg=NULL;
+       struct odbc_obj *obj;
+       struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
+       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
 
-       /* We have to get the user before we can open the stream! */
-       vmu = find_user(&vmus, context, mailbox);
-       if (!vmu) {
-               ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
-               return -1;
-       } else {
-               /* No IMAP account available */
-               if (vmu->imapuser[0] == '\0') {
-                       ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
-                       return -1;
-               }
-       }
-       
-       /* No IMAP account available */
-       if (vmu->imapuser[0] == '\0') {
-               ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
-               free_user(vmu);
+       delete_file(dir, msgnum);
+       if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
                return -1;
        }
 
-       /* 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);
-       }
-       if (vms_p) {
-               ast_debug(3, "Returning before search - user is logged in\n");
-               if (fold == 0) { /* INBOX */
-                       return vms_p->newmessages;
-               }
-               if (fold == 1) { /* Old messages */
-                       return vms_p->oldmessages;
+       do {
+               ast_copy_string(fmt, vmfmts, sizeof(fmt));
+               c = strchr(fmt, '|');
+               if (c)
+                       *c = '\0';
+               if (!strcasecmp(fmt, "wav49"))
+                       strcpy(fmt, "WAV");
+               snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+               if (msgnum > -1)
+                       make_file(fn, sizeof(fn), dir, msgnum);
+               else
+                       ast_copy_string(fn, dir, sizeof(fn));
+               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+               cfg = ast_config_load(full_fn, config_flags);
+               snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+               fd = open(full_fn, O_RDWR);
+               if (fd < 0) {
+                       ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
+                       res = -1;
+                       break;
                }
-               if (fold == 11) {/*Urgent messages*/
-                       return vms_p->urgentmessages;
+               if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
+                       if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
+                               idata.context = "";
+                       }
+                       if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
+                               idata.macrocontext = "";
+                       }
+                       if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
+                               idata.callerid = "";
+                       }
+                       if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
+                               idata.origtime = "";
+                       }
+                       if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
+                               idata.duration = "";
+                       }
+                       if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
+                               idata.category = "";
+                       }
+                       if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
+                               idata.flag = "";
+                       }
                }
-       }
+               fdlen = lseek(fd, 0, SEEK_END);
+               lseek(fd, 0, SEEK_SET);
+               printf("Length is %zd\n", fdlen);
+               fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
+               if (fdm == MAP_FAILED) {
+                       ast_log(AST_LOG_WARNING, "Memory map failed!\n");
+                       res = -1;
+                       break;
+               } 
+               idata.data = fdm;
+               idata.datalen = idata.indlen = fdlen;
 
-       /* 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 (!ast_strlen_zero(idata.category)) 
+                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
+               else
+                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
 
-       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;
+               if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               } else {
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       res = -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;
-               /* set mailbox to INBOX! */
-               ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
-               init_vm_state(vms_p);
-               vmstate_insert(vms_p);
-       }
-       ret = init_mailstream(vms_p, fold);
-       if (!vms_p->mailstream) {
-               ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
-               return -1;
+       } while (0);
+       if (obj) {
+               ast_odbc_release_obj(obj);
        }
-       if (ret == 0) {
-               pgm = mail_newsearchpgm ();
-               hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
-               pgm->header = hdr;
-               if (fold != 1) {
-                       pgm->unseen = 1;
-                       pgm->seen = 0;
-               }
-               /* In the special case where fold is 1 (old messages) we have to do things a bit
-                * differently. Old messages are stored in the INBOX but are marked as "seen"
-                */
-               else {
-                       pgm->unseen = 0;
-                       pgm->seen = 1;
-               }
-               /* look for urgent messages */
-               if (fold == 11) {
-                       pgm->flagged = 1;
-                       pgm->unflagged = 0;
-               }
-               pgm->undeleted = 1;
-               pgm->deleted = 0;
+       if (cfg)
+               ast_config_destroy(cfg);
+       if (fdm != MAP_FAILED)
+               munmap(fdm, fdlen);
+       if (fd > -1)
+               close(fd);
+       return res;
+}
 
-               vms_p->vmArrayIndex = 0;
-               mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
-               if (fold == 0)
-                       vms_p->newmessages = vms_p->vmArrayIndex;
-               if (fold == 1)
-                       vms_p->oldmessages = vms_p->vmArrayIndex;
-               if(fold == 11)
-                       vms_p->urgentmessages = vms_p->vmArrayIndex;
-               /*Freeing the searchpgm also frees the searchhdr*/
-               mail_free_searchpgm(&pgm);
-               vms_p->updated = 0;
-               return vms_p->vmArrayIndex;
-       } else {  
-               mail_ping(vms_p->mailstream);
-       }
-       return 0;
+/*!
+ * \brief Renames a message in a mailbox folder.
+ * \param sdir The folder of the message to be renamed.
+ * \param smsg The index of the message to be renamed.
+ * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
+ * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
+ * \param ddir The destination folder for the message to be renamed into
+ * \param dmsg The destination message for the message to be renamed.
+ *
+ * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
+ * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
+ * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
+ */
+static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
+{
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char msgnums[20];
+       char msgnumd[20];
+       struct odbc_obj *obj;
+       char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
+
+       delete_file(ddir, dmsg);
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+               snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt)
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               else
+                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+       return; 
 }
 
 /*!
- * \brief Gets the number of messages that exist in the inbox folder.
- * \param mailbox_context
- * \param newmsgs The variable that is updated with the count of new messages within this inbox.
- * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
- * 
- * This method is used when IMAP backend is used.
- * Simultaneously determines the count of new and old messages. The total messages would then be the sum of these two.
+ * \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 inboxcount(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+static int remove_file(char *dir, int msgnum)
 {
-       char tmp[PATH_MAX] = "";
-       char *mailboxnc;
-       char *context;
-       char *mb;
-       char *cur;
-       if (newmsgs)
-               *newmsgs = 0;
-       if (oldmsgs)
-               *oldmsgs = 0;
-       if (urgentmsgs)
-               *urgentmsgs = 0;
-
-       ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox_context))
-               return 0;
+       char fn[PATH_MAX];
+       char full_fn[PATH_MAX];
+       char msgnums[80];
        
-       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
-       context = strchr(tmp, '@');
-       if (strchr(mailbox_context, ',')) {
-               int tmpnew, tmpold, tmpurgent;
-               ast_copy_string(tmp, mailbox_context, sizeof(tmp));
-               mb = tmp;
-               while ((cur = strsep(&mb, ", "))) {
-                       if (!ast_strlen_zero(cur)) {
-                               if (inboxcount(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
-                                       return -1;
-                               else {
-                                       if (newmsgs)
-                                               *newmsgs += tmpnew; 
-                                       if (oldmsgs)
-                                               *oldmsgs += tmpold;
-                                       if (urgentmsgs)
-                                               *urgentmsgs += tmpurgent;
-                               }
-                       }
-               }
-               return 0;
-       }
-       if (context) {
-               *context = '\0';
-               mailboxnc = tmp;
-               context++;
-       } else {
-               context = "default";
-               mailboxnc = (char *)mailbox_context;
-       }
-       if (newmsgs) {
-               if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
-                       return -1;
-       }
-       if (oldmsgs) {
-               if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
-                       return -1;
-       }
-       if (urgentmsgs) {
-               if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
-                       return -1;
-       }
+       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;
 }
-       
-
-/** 
- * \brief Determines if the given folder has messages.
- * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- * \param folder the folder to look in
+#else
+#ifndef IMAP_STORAGE
+/*!
+ * \brief Find all .txt files - even if they are not in sequence from 0000.
+ * \param vmu
+ * \param dir
  *
- * This function is used when the mailbox is stored in an IMAP back end.
- * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
- * \return 1 if the folder has one or more messages. zero otherwise.
+ * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ *
+ * \return the count of messages, zero or more.
  */
-static int has_voicemail(const char *mailbox, const char *folder)
+static int count_messages(struct ast_vm_user *vmu, char *dir)
 {
-       char tmp[256], *tmp2, *mbox, *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))
-                                       return 1;
+
+       int vmcount = 0;
+       DIR *vmdir = NULL;
+       struct dirent *vment = NULL;
+
+       if (vm_lock_path(dir))
+               return ERROR_LOCK_PATH;
+
+       if ((vmdir = opendir(dir))) {
+               while ((vment = readdir(vmdir))) {
+                       if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
+                               vmcount++;
                        }
                }
+               closedir(vmdir);
        }
-       if ((context= strchr(tmp, '@')))
-               *context++ = '\0';
-       else
-               context = "default";
-       return messagecount(context, tmp, folder) ? 1 : 0;
+       ast_unlock_path(dir);
+       
+       return vmcount;
 }
 
 /*!
- * \brief Copies a message from one mailbox to another.
- * \param chan
- * \param vmu
- * \param imbox
- * \param msgnum
- * \param duration
- * \param recip
- * \param fmt
- * \param dir
- *
- * This works with IMAP storage based mailboxes.
+ * \brief Renames a message in a mailbox folder.
+ * \param sfn The path to the mailbox information and data file to be renamed.
+ * \param dfn The path for where the message data and information files will be renamed to.
  *
- * \return zero on success, -1 on error.
+ * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
  */
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+static void rename_file(char *sfn, char *dfn)
 {
-       struct vm_state *sendvms = NULL, *destvms = NULL;
-       char messagestring[10]; /* I guess this could be a problem if someone has more than 999,999,999 messages... */
-       if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
-               ast_log(AST_LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
-               return -1;
-       }
-       if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
-               ast_log(AST_LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
-               return -1;
+       char stxt[PATH_MAX];
+       char dtxt[PATH_MAX];
+       ast_filerename(sfn,dfn,NULL);
+       snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
+       snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
+       if (ast_check_realtime("voicemail_data")) {
+               ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
        }
-       snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
-       if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
-               return 0;
-       ast_log(AST_LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
-       return -1;
+       rename(stxt, dtxt);
 }
 
-#endif
-#ifndef IMAP_STORAGE
 /*! 
- * \brief Copies a message from one mailbox to another.
- * \param chan
- * \param vmu
- * \param imbox
- * \param msgnum
- * \param duration
- * \param recip
- * \param fmt
- * \param dir
+ * \brief Determines the highest message number in use for a given user and mailbox folder.
+ * \param vmu 
+ * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
  *
- * This is only used by file storage based mailboxes.
+ * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
  *
- * \return zero on success, -1 on error.
+ * \note Should always be called with a lock already set on dir.
+ * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
  */
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
 {
-       char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
-       const char *frombox = mbox(imbox);
-       int recipmsgnum;
+       int x;
+       unsigned char map[MAXMSGLIMIT] = "";
+       DIR *msgdir;
+       struct dirent *msgdirent;
+       int msgdirint;
 
-       ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
+       /* Reading the entire directory into a file map scales better than
+        * doing a stat repeatedly on a predicted sequence.  I suspect this
+        * is partially due to stat(2) internally doing a readdir(2) itself to
+        * find each file. */
+       msgdir = opendir(dir);
+       while ((msgdirent = readdir(msgdir))) {
+               if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
+                       map[msgdirint] = 1;
+       }
+       closedir(msgdir);
 
-       if (!strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
-               create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
-       } else {
-               create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
+       for (x = 0; x < vmu->maxmsg; x++) {
+               if (map[x] == 0)
+                       break;
        }
-       
-       if (!dir)
-               make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
-       else
-               ast_copy_string(fromdir, dir, sizeof(fromdir));
 
-       make_file(frompath, sizeof(frompath), fromdir, msgnum);
-       make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
+       return x - 1;
+}
 
-       if (vm_lock_path(todir))
-               return ERROR_LOCK_PATH;
+#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.
+ * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
+ *
+ * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
+ * The copy operation copies up to 4096 bytes at once.
+ *
+ * \return zero on success, -1 on error.
+ */
+static int copy(char *infile, char *outfile)
+{
+       int ifd;
+       int ofd;
+       int res;
+       int len;
+       char buf[4096];
 
-       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);
+#ifdef HARDLINK_WHEN_POSSIBLE
+       /* Hard link if possible; saves disk space & is faster */
+       if (link(infile, outfile)) {
+#endif
+               if ((ifd = open(infile, O_RDONLY)) < 0) {
+                       ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
+                       return -1;
+               }
+               if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
+                       ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
+                       close(ifd);
+                       return -1;
+               }
+               do {
+                       len = read(ifd, buf, sizeof(buf));
+                       if (len < 0) {
+                               ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+                               close(ifd);
+                               close(ofd);
+                               unlink(outfile);
+                       }
+                       if (len) {
+                               res = write(ofd, buf, len);
+                               if (errno == ENOMEM || errno == ENOSPC || res != len) {
+                                       ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+                                       close(ifd);
+                                       close(ofd);
+                                       unlink(outfile);
+                               }
+                       }
+               } while (len);
+               close(ifd);
+               close(ofd);
+               return 0;
+#ifdef HARDLINK_WHEN_POSSIBLE
        } else {
-               ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
+               /* Hard link succeeded */
+               return 0;
        }
-       ast_unlock_path(todir);
-       notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
-       
-       return 0;
+#endif
+}
+
+/*!
+ * \brief Copies a voicemail information (envelope) file.
+ * \param frompath
+ * \param topath 
+ *
+ * Every voicemail has the data (.wav) file, and the information file.
+ * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
+ * This is used by the COPY macro when not using IMAP storage.
+ */
+static void copy_plain_file(char *frompath, char *topath)
+{
+       char frompath2[PATH_MAX], topath2[PATH_MAX];
+       struct ast_variable *tmp,*var = NULL;
+       const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
+       ast_filecopy(frompath, topath, NULL);
+       snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
+       snprintf(topath2, sizeof(topath2), "%s.txt", topath);
+       if (ast_check_realtime("voicemail_data")) {
+               var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
+               /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
+               for (tmp = var; tmp; tmp = tmp->next) {
+                       if (!strcasecmp(tmp->name, "origmailbox")) {
+                               origmailbox = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "context")) {
+                               context = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "macrocontext")) {
+                               macrocontext = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "exten")) {
+                               exten = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "priority")) {
+                               priority = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "callerchan")) {
+                               callerchan = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "callerid")) {
+                               callerid = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "origdate")) {
+                               origdate = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "origtime")) {
+                               origtime = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "category")) {
+                               category = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "duration")) {
+                               duration = tmp->value;
+                       }
+               }
+               ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
+       }
+       copy(frompath2, topath2);
+       ast_variables_destroy(var);
 }
 #endif
-#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
 
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+/*! 
+ * \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.
+ *
+ * This is used by the DELETE macro when voicemails are stored on the file system.
+ *
+ * \return zero on success, -1 on error.
+ */
+static int vm_delete(char *file)
 {
-       return __has_voicemail(context, mailbox, folder, 0);
+       char *txt;
+       int txtsize = 0;
+
+       txtsize = (strlen(file) + 5)*sizeof(char);
+       txt = alloca(txtsize);
+       /* Sprintf here would safe because we alloca'd exactly the right length,
+        * but trying to eliminate all sprintf's anyhow
+        */
+       if (ast_check_realtime("voicemail_data")) {
+               ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
+       }
+       snprintf(txt, txtsize, "%s.txt", file);
+       unlink(txt);
+       return ast_filedelete(file, NULL);
 }
 
-static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
+/*!
+ * \brief utility used by inchar(), for base_encode()
+ */
+static int inbuf(struct baseio *bio, FILE *fi)
 {
-       DIR *dir;
-       struct dirent *de;
-       char fn[256];
-       int ret = 0;
+       int l;
 
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
+       if (bio->ateof)
                return 0;
 
-       if (ast_strlen_zero(folder))
-               folder = "INBOX";
-       if (ast_strlen_zero(context))
-               context = "default";
-
-       snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
+       if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
+               if (ferror(fi))
+                       return -1;
 
-       if (!(dir = opendir(fn)))
+               bio->ateof = 1;
                return 0;
-
-       while ((de = readdir(dir))) {
-               if (!strncasecmp(de->d_name, "msg", 3)) {
-                       if (shortcircuit) {
-                               ret = 1;
-                               break;
-                       } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
-                               if (shortcircuit) return 1;
-                               ret++;
-                       }
-               }
        }
 
-       closedir(dir);
+       bio->iolen= l;
+       bio->iocp= 0;
 
-       /* If we are checking INBOX, we should check Urgent as well */
-       if (strcmp(folder, "INBOX") == 0) {
-               return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
-       } else {
-               return ret;
-       }
+       return 1;
 }
 
-/** 
- * \brief Determines if the given folder has messages.
- * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- * \param folder the folder to look in
- *
- * This function is used when the mailbox is stored in a filesystem back end.
- * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
- * \return 1 if the folder has one or more messages. zero otherwise.
+/*!
+ * \brief utility used by base_encode()
  */
-static int has_voicemail(const char *mailbox, const char *folder)
+static int inchar(struct baseio *bio, FILE *fi)
 {
-       char tmp[256], *tmp2 = tmp, *mbox, *context;
-       ast_copy_string(tmp, mailbox, sizeof(tmp));
-       while ((mbox = strsep(&tmp2, ","))) {
-               if ((context = strchr(mbox, '@')))
-                       *context++ = '\0';
-               else
-                       context = "default";
-               if (__has_voicemail(context, mbox, folder, 1))
-                       return 1;
+       if (bio->iocp>=bio->iolen) {
+               if (!inbuf(bio, fi))
+                       return EOF;
        }
-       return 0;
+
+       return bio->iobuf[bio->iocp++];
 }
 
+/*!
+ * \brief utility used by base_encode()
+ */
+static int ochar(struct baseio *bio, int c, FILE *so)
+{
+       if (bio->linelength >= BASELINELEN) {
+               if (fputs(eol,so) == EOF)
+                       return -1;
+
+               bio->linelength= 0;
+       }
+
+       if (putc(((unsigned char)c),so) == EOF)
+               return -1;
+
+       bio->linelength++;
+
+       return 1;
+}
 
-static int inboxcount(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+/*!
+ * \brief Performs a base 64 encode algorithm on the contents of a File
+ * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
+ * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
+ *
+ * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
+ *
+ * \return zero on success, -1 on error.
+ */
+static int base_encode(char *filename, FILE *so)
 {
-       char tmp[256];
-       char *context;
+       static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+               'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+               'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
+               '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+       int i,hiteof= 0;
+       FILE *fi;
+       struct baseio bio;
 
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
-               return 0;
+       memset(&bio, 0, sizeof(bio));
+       bio.iocp = BASEMAXINLINE;
 
-       if (newmsgs)
-               *newmsgs = 0;
-       if (oldmsgs)
-               *oldmsgs = 0;
-       if (urgentmsgs)
-               *urgentmsgs = 0;
+       if (!(fi = fopen(filename, "rb"))) {
+               ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
+               return -1;
+       }
 
-       if (strchr(mailbox, ',')) {
-               int tmpnew, tmpold, tmpurgent;
-               char *mb, *cur;
+       while (!hiteof){
+               unsigned char igroup[3], ogroup[4];
+               int c,n;
 
-               ast_copy_string(tmp, mailbox, sizeof(tmp));
-               mb = tmp;
-               while ((cur = strsep(&mb, ", "))) {
-                       if (!ast_strlen_zero(cur)) {
-                               if (inboxcount(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
-                                       return -1;
-                               else {
-                                       if (newmsgs)
-                                               *newmsgs += tmpnew; 
-                                       if (oldmsgs)
-                                               *oldmsgs += tmpold;
-                                       if (urgentmsgs)
-                                               *urgentmsgs += tmpurgent;
-                               }
+               igroup[0]= igroup[1]= igroup[2]= 0;
+
+               for (n= 0;n<3;n++) {
+                       if ((c = inchar(&bio, fi)) == EOF) {
+                               hiteof= 1;
+                               break;
                        }
+
+                       igroup[n]= (unsigned char)c;
+               }
+
+               if (n> 0) {
+                       ogroup[0]= dtable[igroup[0]>>2];
+                       ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
+                       ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
+                       ogroup[3]= dtable[igroup[2]&0x3F];
+
+                       if (n<3) {
+                               ogroup[3]= '=';
+
+                               if (n<2)
+                                       ogroup[2]= '=';
+                       }
+
+                       for (i= 0;i<4;i++)
+                               ochar(&bio, ogroup[i], so);
                }
-               return 0;
        }
 
-       ast_copy_string(tmp, mailbox, sizeof(tmp));
+       fclose(fi);
        
-       if ((context = strchr(tmp, '@')))
-               *context++ = '\0';
-       else
-               context = "default";
-
-       if (newmsgs)
-               *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
-       if (oldmsgs)
-               *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
-       if (urgentmsgs)
-               *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
+       if (fputs(eol,so)==EOF)
+               return 0;
 
-       return 0;
+       return 1;
 }
 
-#endif
-
-static void run_externnotify(char *context, char *extension, const char *flag)
+static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
 {
-       char arguments[255];
-       char ext_context[256] = "";
-       int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
-       struct ast_smdi_mwi_message *mwi_msg;
+       char callerid[256];
+       /* Prepare variables for substitution in email body and subject */
+       pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
+       pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
+       snprintf(passdata, passdatasize, "%d", msgnum);
+       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_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);
+}
 
-       if (!ast_strlen_zero(context))
-               snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
-       else
-               ast_copy_string(ext_context, extension, sizeof(ext_context));
+/*!
+ * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
+ * \param from The string to work with.
+ * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
+ * 
+ * \return The destination string with quotes wrapped on it (the to field).
+ */
+static char *quote(const char *from, char *to, size_t len)
+{
+       char *ptr = to;
+       *ptr++ = '"';
+       for (; ptr < to + len - 1; from++) {
+               if (*from == '"')
+                       *ptr++ = '\\';
+               else if (*from == '\0')
+                       break;
+               *ptr++ = *from;
+       }
+       if (ptr < to + len - 1)
+               *ptr++ = '"';
+       *ptr = '\0';
+       return to;
+}
 
-       if (smdi_iface) {
-               if (ast_app_has_voicemail(ext_context, NULL)) 
-                       ast_smdi_mwi_set(smdi_iface, extension);
-               else
-                       ast_smdi_mwi_unset(smdi_iface, extension);
+/*! \brief
+ * fill in *tm for current time according to the proper timezone, if any.
+ * Return tm so it can be used as a function argument.
+ */
+static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
+{
+       const struct vm_zone *z = NULL;
+       struct timeval t = ast_tvnow();
 
-               if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
-                       ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
-                       if (!strncmp(mwi_msg->cause, "INV", 3))
-                               ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
-                       else if (!strncmp(mwi_msg->cause, "BLK", 3))
-                               ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
-                       ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
-                       ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
-               } else {
-                       ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
+       /* Does this user have a timezone specified? */
+       if (!ast_strlen_zero(vmu->zonetag)) {
+               /* Find the zone in the list */
+               AST_LIST_LOCK(&zones);
+               AST_LIST_TRAVERSE(&zones, z, list) {
+                       if (!strcmp(z->name, vmu->zonetag))
+                               break;
                }
+               AST_LIST_UNLOCK(&zones);
        }
+       ast_localtime(&t, tm, z ? z->timezone : NULL);
+       return tm;
+}
 
-       if (!ast_strlen_zero(externnotify)) {
-               if (inboxcount(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
-                       ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
-               } else {
-                       snprintf(arguments, sizeof(arguments), "%s %s %s %d %s&", externnotify, context, extension, newvoicemails, S_OR(flag,""));
-                       ast_debug(1, "Executing %s\n", arguments);
-                       ast_safe_system(arguments);
+/*!\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 Variables used for saving a voicemail.
+/*!\brief Encode a string according to the MIME rules for encoding strings
+ * that are not 7-bit clean or contain control characters.
  *
- * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
+ * 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.
  */
-struct leave_vm_options {
-       unsigned int flags;
-       signed char record_gain;
-       char *exitcontext;
-};
+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 Prompts the user and records a voicemail to a mailbox.
+ * \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.
+ * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
+ * \param vmu The voicemail user who is sending the voicemail.
+ * \param msgnum The message index in the mailbox folder.
+ * \param context 
+ * \param mailbox The voicemail box to read the voicemail to be notified in this email.
+ * \param cidnum The caller ID number.
+ * \param cidname The caller ID name.
+ * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
+ * \param format The message sound file format. i.e. .wav
+ * \param duration The time of the message content, in seconds.
+ * \param attach_user_voicemail if 1, the sound file is attached to the email.
  * \param chan
- * \param ext
- * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
- * 
- * 
- * 
- * \return zero on success, -1 on error.
+ * \param category
+ * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
+ *
+ * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email.  That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
  */
-static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
+static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag)
 {
+       char date[256];
+       char host[MAXHOSTNAMELEN] = "";
+       char who[256];
+       char bound[256];
+       char dur[256];
+       struct ast_tm tm;
+       char enc_cidnum[256] = "", enc_cidname[256] = "";
+       char *passdata = NULL, *passdata2;
+       size_t len_passdata = 0, len_passdata2, tmplen;
+       char *greeting_attachment; 
+       char filename[256];
+
 #ifdef IMAP_STORAGE
-       int newmsgs, oldmsgs, urgentmsgs;
-       struct vm_state *vms = NULL;
+#define ENDL "\r\n"
 #else
-       char urgdir[PATH_MAX];
+#define ENDL "\n"
 #endif
-       char txtfile[PATH_MAX];
-       char tmptxtfile[PATH_MAX];
-       char callerid[256];
-       FILE *txt;
-       char date[256];
-       int txtdes;
-       int res = 0;
-       int msgnum;
-       int duration = 0;
-       int ausemacro = 0;
-       int ousemacro = 0;
-       int ouseexten = 0;
-       int rtmsgid = 0;
-       char tmpid[16];
-       char tmpdur[16];
-       char priority[16];
-       char origtime[16];
-       char dir[PATH_MAX];
-       char tmpdir[PATH_MAX];
-       char fn[PATH_MAX];
-       char prefile[PATH_MAX] = "";
-       char tempfile[PATH_MAX] = "";
-       char ext_context[256] = "";
-       char fmt[80];
-       char *context;
-       char ecodes[17] = "#";
-       char tmp[1024] = "";
-       char *tmpptr;
-       struct ast_vm_user *vmu;
-       struct ast_vm_user svm;
-       const char *category = NULL;
-       const char *code;
-       const char *alldtmf = "0123456789ABCD*#";
-       char flag[80];
-
-       ast_copy_string(tmp, ext, sizeof(tmp));
-       ext = tmp;
-       if ((context = strchr(tmp, '@'))) {
-               *context++ = '\0';
-               tmpptr = strchr(context, '&');
-       } else {
-               tmpptr = strchr(ext, '&');
-       }
-
-       if (tmpptr)
-               *tmpptr++ = '\0';
-
-       ast_channel_lock(chan);
-       if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
-               category = ast_strdupa(category);
-       }
-       ast_channel_unlock(chan);
 
-       if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
-               ast_copy_string(flag, "Urgent", sizeof(flag));
-       } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
-               ast_copy_string(flag, "PRIORITY", sizeof(flag));
-       } else {
-               flag[0] = '\0';
+       /* One alloca for multiple fields */
+       len_passdata2 = strlen(vmu->fullname);
+       if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
+               len_passdata2 = tmplen;
        }
-
-       ast_debug(3, "Before find_user\n");
-       if (!(vmu = find_user(&svm, context, ext))) {
-               ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-               return res;
+       if ((tmplen = strlen(fromstring)) > len_passdata2) {
+               len_passdata2 = tmplen;
        }
-       /* Setup pre-file if appropriate */
-       if (strcmp(vmu->context, "default"))
-               snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
-       else
-               ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
+       len_passdata2 = len_passdata2 * 3 + 200;
+       passdata2 = alloca(len_passdata2);
 
-       /* Set the path to the prefile. Will be one of 
-               VM_SPOOL_DIRcontext/ext/busy
-               VM_SPOOL_DIRcontext/ext/unavail
-          Depending on the flag set in options.
-       */
-       if (ast_test_flag(options, OPT_BUSY_GREETING)) {
-               snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
-       } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
-               snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
+       if (cidnum) {
+               strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
        }
-       /* Set the path to the tmpfile as
-               VM_SPOOL_DIR/context/ext/temp
-          and attempt to create the folder structure.
-       */
-       snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
-       if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
-               ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
-               return -1;
+       if (cidname) {
+               strip_control(cidname, enc_cidname, sizeof(enc_cidname));
        }
+       gethostname(host, sizeof(host) - 1);
 
-       RETRIEVE(tempfile, -1, ext, context);
+       if (strchr(srcemail, '@'))
+               ast_copy_string(who, srcemail, sizeof(who));
+       else 
+               snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+       
+       greeting_attachment = strrchr(ast_strdupa(attach), '/');
+       if (greeting_attachment)
+               *greeting_attachment++ = '\0';
 
-       if (ast_fileexists(tempfile, NULL, NULL) > 0)
-               ast_copy_string(prefile, tempfile, sizeof(prefile));
+       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+       ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+       fprintf(p, "Date: %s" ENDL, date);
 
-       DISPOSE(tempfile, -1);
-       /* It's easier just to try to make it than to check for its existence */
-       create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
+       /* Set date format for voicemail mail */
+       ast_strftime(date, sizeof(date), emaildateformat, &tm);
 
-       /* Check current or macro-calling context for special extensions */
-       if (ast_test_flag(vmu, VM_OPERATOR)) {
-               if (!ast_strlen_zero(vmu->exit)) {
-                       if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
-                               strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
-                               ouseexten = 1;
+       if (!ast_strlen_zero(fromstring)) {
+               struct ast_channel *ast;
+               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);
                        }
-               } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
-                       strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
-                       ouseexten = 1;
-               } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
-                       strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
-                       ousemacro = 1;
+                       ast_channel_free(ast);
+               } else {
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
                }
+       } else {
+               fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
        }
 
-       if (!ast_strlen_zero(vmu->exit)) {
-               if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
-                       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
-       } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
-               strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
-       else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
-               strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
-               ausemacro = 1;
-       }
-
-       if (ast_test_flag(options, OPT_DTMFEXIT)) {
-               for (code = alldtmf; *code; code++) {
-                       char e[2] = "";
-                       e[0] = *code;
-                       if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
-                               strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
+       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);
        }
-
-       /* Play the beginning intro if desired */
-       if (!ast_strlen_zero(prefile)) {
-#ifdef ODBC_STORAGE
-               int success = 
-#endif
-                       RETRIEVE(prefile, -1, ext, context);
-               if (ast_fileexists(prefile, NULL, NULL) > 0) {
-                       if (ast_streamfile(chan, prefile, chan->language) > -1) 
-                               res = ast_waitstream(chan, ecodes);
-#ifdef ODBC_STORAGE
-                       if (success == -1) {
-                               /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
-                               ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
-                               store_file(prefile, vmu->mailbox, vmu->context, -1);
+       if (!ast_strlen_zero(emailsubject)) {
+               struct ast_channel *ast;
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
+                       int vmlen = strlen(emailsubject) * 3 + 200;
+                       /* 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);
                        }
-#endif
+                       ast_channel_free(ast);
                } else {
-                       ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
-                       res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
                }
-               DISPOSE(prefile, -1);
-               if (res < 0) {
-                       ast_debug(1, "Hang up during prefile playback\n");
-                       free_user(vmu);
-                       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-                       return -1;
+       } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
+               if (ast_strlen_zero(flag)) {
+                       fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
+               } else {
+                       fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
                }
-       }
-       if (res == '#') {
-               /* On a '#' we skip the instructions */
-               ast_set_flag(options, OPT_SILENT);
-               res = 0;
-       }
-       if (!res && !ast_test_flag(options, OPT_SILENT)) {
-               res = ast_stream_and_wait(chan, INTRO, ecodes);
-               if (res == '#') {
-                       ast_set_flag(options, OPT_SILENT);
-                       res = 0;
+       } else {
+               if (ast_strlen_zero(flag)) {
+                       fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
+               } else {
+                       fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
                }
        }
-       if (res > 0)
-               ast_stopstream(chan);
-       /* Check for a '*' here in case the caller wants to escape from voicemail to something
-        other than the operator -- an automated attendant or mailbox login for example */
-       if (!ast_strlen_zero(vmu->exit) && (res == '*')) {
-               chan->exten[0] = 'a';
-               chan->exten[1] = '\0';
-               if (!ast_strlen_zero(vmu->exit)) {
-                       ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
-               } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
-                       ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+
+       fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
+       if (imap) {
+               /* additional information needed for IMAP searching */
+               fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
+               /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
+               fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
+               fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
+               fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
+                /* flag added for Urgent */
+               fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
+               fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
+               fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
+               fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
+               fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
+               fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
+               if (!ast_strlen_zero(category)) {
+                       fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
+               } else {
+                       fprintf(p, "X-Asterisk-VM-Category: " ENDL);
                }
-               chan->priority = 0;
-               free_user(vmu);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
-               return 0;
+               fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
+               fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
+               fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
+       }
+       if (!ast_strlen_zero(cidnum)) {
+               fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
+       }
+       if (!ast_strlen_zero(cidname)) {
+               fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
        }
+       fprintf(p, "MIME-Version: 1.0" ENDL);
+       if (attach_user_voicemail) {
+               /* Something unique. */
+               snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
 
-       /* Check for a '0' here */
-       if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
-       transfer:
-               if (ouseexten || ousemacro) {
-                       chan->exten[0] = 'o';
-                       chan->exten[1] = '\0';
-                       if (!ast_strlen_zero(vmu->exit)) {
-                               ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
-                       } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
-                               ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
-                       }
-                       ast_play_and_wait(chan, "transfer");
-                       chan->priority = 0;
-                       free_user(vmu);
-                       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
-               }
-               return 0;
+               fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
+               fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
+               fprintf(p, "--%s" ENDL, bound);
        }
-
-       /* Allow all other digits to exit Voicemail and return to the dialplan */
-       if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
-               if (!ast_strlen_zero(options->exitcontext))
-                       ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
-               free_user(vmu);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
-               return res;
+       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, "Substitution/voicemail"))) {
+                       char *passdata;
+                       int vmlen = strlen(emailbody)*3 + 200;
+                       passdata = alloca(vmlen);
+                       memset(passdata, 0, vmlen);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+                       pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
+                       fprintf(p, "%s" ENDL, passdata);
+                       ast_channel_free(ast);
+               } else
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+       } else if (msgnum > -1){
+               fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long %s message (number %d)" ENDL
+               "in mailbox %s from %s, on %s so you might" ENDL
+               "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
+               dur, flag, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
+       } else {
+               fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
+                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
        }
 
-       if (res < 0) {
-               free_user(vmu);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-               return -1;
+       if (imap || attach_user_voicemail) {
+               if (!ast_strlen_zero(attach2)) {
+                       snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
+                       ast_debug(5, "creating second attachment filename %s\n", filename);
+                       add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
+                       snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
+                       ast_debug(5, "creating attachment filename %s\n", filename);
+                       add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
+               } else {
+                       snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
+                       ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
+                       add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
+               }
        }
-       /* The meat of recording the message...  All the announcements and beeps have been played*/
-       ast_copy_string(fmt, vmfmts, sizeof(fmt));
-       if (!ast_strlen_zero(fmt)) {
-               msgnum = 0;
+}
 
-#ifdef IMAP_STORAGE
-               /* Is ext a mailbox? */
-               /* must open stream for this user to get info! */
-               res = inboxcount(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
-               if (res < 0) {
-                       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))) {
-               /* It is possible under certain circumstances that inboxcount did not
-                * create a vm_state when it was needed. This is a catchall which will
-                * rarely be used.
-                */
-                       if (!(vms = ast_calloc(1, sizeof(*vms)))) {
-                               ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
-                               return -1;
-                       }
-                       ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
-                       ast_copy_string(vms->username, ext, sizeof(vms->username));
-                       vms->mailstream = NIL;
-                       ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
-                       vms->updated=1;
-                       ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
-                       init_vm_state(vms);
-                       vmstate_insert(vms);
-                       vms = get_vm_state_by_mailbox(ext,0);
-               }
-               vms->newmessages++;
-               
-               /* here is a big difference! We add one to it later */
-               msgnum = newmsgs + oldmsgs;
-               ast_debug(3, "Messagecount set to %d\n",msgnum);
-               snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
-               /* set variable for compatibility */
-               pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
+static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
+{
+       char tmpdir[256], newtmp[256];
+       char fname[256];
+       char tmpcmd[256];
+       int tmpfd = -1;
 
-               /* Check if mailbox is full */
-               check_quota(vms, imapfolder);
-               if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
-                       ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
-                       ast_play_and_wait(chan, "vm-mailboxfull");
-                       return -1;
-               }
-               
-               /* Check if we have exceeded maxmsg */
-               if (msgnum >= vmu->maxmsg) {
-                       ast_log(AST_LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
-                       ast_play_and_wait(chan, "vm-mailboxfull");
-                       return -1;
-               }
+       /* Eww. We want formats to tell us their own MIME type */
+       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
 
-#else
-               if (count_messages(vmu, dir) >= vmu->maxmsg) {
-                       res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
-                       if (!res)
-                               res = ast_waitstream(chan, "");
-                       ast_log(AST_LOG_WARNING, "No more messages possible\n");
-                       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-                       goto leave_vm_out;
+       if (vmu->volgain < -.001 || vmu->volgain > .001) {
+               create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
+               snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
+               tmpfd = mkstemp(newtmp);
+               chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
+               ast_debug(3, "newtmp: %s\n", newtmp);
+               if (tmpfd > -1) {
+                       int soxstatus;
+                       snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
+                       if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
+                               attach = newtmp;
+                               ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+                       } else {
+                               ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
+                                       soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
+                               ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
+                       }
                }
+       }
+       fprintf(p, "--%s" ENDL, bound);
+       if (msgnum > -1)
+               fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
+       else
+               fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
+       fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
+       fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
+       if (msgnum > -1)
+               fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
+       else
+               fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
+       snprintf(fname, sizeof(fname), "%s.%s", attach, format);
+       base_encode(fname, p);
+       if (last)
+               fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
+       if (tmpfd > -1) {
+               unlink(fname);
+               close(tmpfd);
+               unlink(newtmp);
+       }
+       return 0;
+}
+#undef ENDL
 
-#endif
-               snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
-               txtdes = mkstemp(tmptxtfile);
-               chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
-               if (txtdes < 0) {
-                       res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
-                       if (!res)
-                               res = ast_waitstream(chan, "");
-                       ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
-                       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-                       goto leave_vm_out;
-               }
+static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
+{
+       FILE *p=NULL;
+       char tmp[80] = "/tmp/astmail-XXXXXX";
+       char tmp2[256];
 
-               /* Now play the beep once we have the message number for our next message. */
-               if (res >= 0) {
-                       /* Unless we're *really* silent, try to send the beep */
-                       res = ast_stream_and_wait(chan, "beep", "");
-               }
-                               
-               /* Store information in real-time storage */
-               if (ast_check_realtime("voicemail_data")) {
-                       snprintf(priority, sizeof(priority), "%d", 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,""), NULL);
-               }
+       if (vmu && ast_strlen_zero(vmu->email)) {
+               ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
+               return(0);
+       }
+       if (!strcmp(format, "wav49"))
+               format = "WAV";
+       ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
+       /* Make a temporary file instead of piping directly to sendmail, in case the mail
+          command hangs */
+       if ((p = vm_mkftemp(tmp)) == NULL) {
+               ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
+               return -1;
+       } else {
+               make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
+               fclose(p);
+               snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+               ast_safe_system(tmp2);
+               ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
+       }
+       return 0;
+}
 
-               /* Store information */
-               txt = fdopen(txtdes, "w+");
-               if (txt) {
-                       get_date(date, sizeof(date));
-                       fprintf(txt, 
-                               ";\n"
-                               "; Message Information file\n"
-                               ";\n"
-                               "[message]\n"
-                               "origmailbox=%s\n"
-                               "context=%s\n"
-                               "macrocontext=%s\n"
-                               "exten=%s\n"
-                               "priority=%d\n"
-                               "callerchan=%s\n"
-                               "callerid=%s\n"
-                               "origdate=%s\n"
-                               "origtime=%ld\n"
-                               "category=%s\n",
-                               ext,
-                               chan->context,
-                               chan->macrocontext, 
-                               chan->exten,
-                               chan->priority,
-                               chan->name,
-                               ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
-                               date, (long)time(NULL),
-                               category ? category : "");
-               } else
-                       ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
-#ifdef IMAP_STORAGE
-               res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
-#else
-               res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL, flag);
-#endif
+static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
+{
+       char date[256];
+       char host[MAXHOSTNAMELEN] = "";
+       char who[256];
+       char dur[PATH_MAX];
+       char tmp[80] = "/tmp/astmail-XXXXXX";
+       char tmp2[PATH_MAX];
+       struct ast_tm tm;
+       FILE *p;
 
-               if (txt) {
-                       fprintf(txt, "flag=%s\n", flag);
-                       if (duration < vmminsecs) {
-                               fclose(txt);
-                               if (option_verbose > 2) 
-                                       ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
-                               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, NULL);
-                               }
-                       } else {
-                               fprintf(txt, "duration=%d\n", duration);
-                               fclose(txt);
-                               if (vm_lock_path(dir)) {
-                                       ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
-                                       /* Delete files */
-                                       ast_filedelete(tmptxtfile, NULL);
-                                       unlink(tmptxtfile);
-                               } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
-                                       ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
-                                       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, NULL);
-                                       }
-                               } else {
-#ifndef IMAP_STORAGE
-                                       msgnum = last_message_index(vmu, dir) + 1;
-#endif
-                                       make_file(fn, sizeof(fn), dir, msgnum);
+       if ((p = vm_mkftemp(tmp)) == NULL) {
+               ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
+               return -1;
+       }
+       gethostname(host, sizeof(host)-1);
+       if (strchr(srcemail, '@'))
+               ast_copy_string(who, srcemail, sizeof(who));
+       else 
+               snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+       snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+       ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+       fprintf(p, "Date: %s\n", date);
 
-                                       /* assign a variable with the name of the voicemail file */ 
-#ifndef IMAP_STORAGE
-                                       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
-#else
-                                       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
-#endif
+       if (*pagerfromstring) {
+               struct ast_channel *ast;
+               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);
+                       memset(passdata, 0, vmlen);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+                       pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
+                       fprintf(p, "From: %s <%s>\n", passdata, who);
+                       ast_channel_free(ast);
+               } else 
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+       } else
+               fprintf(p, "From: Asterisk PBX <%s>\n", who);
+       fprintf(p, "To: %s\n", pager);
+       if (pagersubject) {
+               struct ast_channel *ast;
+               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);
+                       memset(passdata, 0, vmlen);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+                       pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
+                       fprintf(p, "Subject: %s\n\n", passdata);
+                       ast_channel_free(ast);
+               } else
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+       } else {
+               if (ast_strlen_zero(flag)) {
+                       fprintf(p, "Subject: New VM\n\n");
+               } else {
+                       fprintf(p, "Subject: New %s VM\n\n", flag);
+               }
+       }
 
-                                       snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
-                                       ast_filerename(tmptxtfile, fn, NULL);
-                                       rename(tmptxtfile, txtfile);
+       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, "Substitution/voicemail"))) {
+                       char *passdata;
+                       int vmlen = strlen(pagerbody) * 3 + 200;
+                       passdata = alloca(vmlen);
+                       memset(passdata, 0, vmlen);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+                       pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
+                       fprintf(p, "%s\n", passdata);
+                       ast_channel_free(ast);
+               } else
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+       } else {
+               fprintf(p, "New %s long %s msg in box %s\n"
+                               "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
+       }
+       fclose(p);
+       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+       ast_safe_system(tmp2);
+       ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
+       return 0;
+}
 
-                                       /* Properly set permissions on voicemail text descriptor file.
-                                          Unfortunately mkstemp() makes this file 0600 on most unix systems. */
-                                       if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
-                                               ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
+/*!
+ * \brief Gets the current date and time, as formatted string.
+ * \param s The buffer to hold the output formatted date.
+ * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
+ * 
+ * The date format string used is "%a %b %e %r UTC %Y".
+ * 
+ * \return zero on success, -1 on error.
+ */
+static int get_date(char *s, int len)
+{
+       struct ast_tm tm;
+       struct timeval t = ast_tvnow();
+       
+       ast_localtime(&t, &tm, "UTC");
 
-                                       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, NULL);
-                                       }
-                                       /* We must store the file first, before copying the message, because
-                                        * ODBC storage does the entire copy with SQL.
-                                        */
-                                       if (ast_fileexists(fn, NULL, NULL) > 0) {
-                                               STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, NULL, flag);
-                                       }
+       return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
+}
 
-                                       /* Are there to be more recipients of this message? */
-                                       while (tmpptr) {
-                                               struct ast_vm_user recipu, *recip;
-                                               char *exten, *context;
-                                       
-                                               exten = strsep(&tmpptr, "&");
-                                               context = strchr(exten, '@');
-                                               if (context) {
-                                                       *context = '\0';
-                                                       context++;
-                                               }
-                                               if ((recip = find_user(&recipu, context, exten))) {
-                                                       copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
-                                                       free_user(recip);
-                                               }
-                                       }
-#ifndef IMAP_STORAGE
-                                       if (!strcmp(flag, "Urgent")) { /* If this is an Urgent message */
-                                               /* Move the message from INBOX to Urgent folder if this is urgent! */
-                                               char sfn[PATH_MAX];
-                                               char dfn[PATH_MAX];
-                                               int x;
-                                               /* It's easier just to try to make it than to check for its existence */
-                                               create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
-                                               ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n",sfn,dfn);
-                                               x = last_message_index(vmu, urgdir) + 1;
-                                               make_file(sfn, sizeof(sfn), dir, msgnum);
-                                               make_file(dfn, sizeof(dfn), urgdir, x);
-                                               RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
-                                       }
-#endif
-                                       /* Notification needs to happen after the copy, though. */
-                                       if (ast_fileexists(fn, NULL, NULL)) {
-#ifdef IMAP_STORAGE
-                                               notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
-#else
-                                               notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
-#endif
-                                       }
+static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
+{
+       int res;
+       char fn[PATH_MAX];
+       char dest[PATH_MAX];
 
-                                       /* Disposal needs to happen after the optional move and copy */
-                                       if (ast_fileexists(fn, NULL, NULL)) {
-                                               DISPOSE(dir, msgnum);
-                                       }
-                               }
-                       }
-               }
-               if (res == '0') {
-                       goto transfer;
-               } else if (res > 0)
-                       res = 0;
+       snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
 
-               if (duration < vmminsecs)
-                       /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
-                       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-               else
-                       pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
-       } else
-               ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
-leave_vm_out:
-       free_user(vmu);
-       
+       if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
+               ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
+               return -1;
+       }
+
+       RETRIEVE(fn, -1, ext, context);
+       if (ast_fileexists(fn, NULL, NULL) > 0) {
+               res = ast_stream_and_wait(chan, fn, ecodes);
+               if (res) {
+                       DISPOSE(fn, -1);
+                       return res;
+               }
+       } else {
+               /* Dispose just in case */
+               DISPOSE(fn, -1);
+               res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
+               if (res)
+                       return res;
+               res = ast_say_digit_str(chan, ext, ecodes, chan->language);
+               if (res)
+                       return res;
+       }
+       res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
        return res;
 }
 
-static int say_and_wait(struct ast_channel *chan, int num, const char *language)
+static void free_zone(struct vm_zone *z)
 {
-       int d;
-       d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
-       return d;
+       ast_free(z);
 }
 
-static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
+#ifdef ODBC_STORAGE
+/*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
+static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
 {
-#ifdef IMAP_STORAGE
-       /* we must use mbox(x) folder names, and copy the message there */
-       /* simple. huh? */
-       long res;
-       char sequence[10];
-       char mailbox[256];
+       int x = -1;
+       int res;
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char rowdata[20];
+       char tmp[PATH_MAX] = "";
+       struct odbc_obj *obj;
+       char *context;
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
 
-       /* if save to Old folder, just leave in INBOX */
-       if (box == 1) return 10;
-       /* get the real IMAP message number for this message */
-       snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
-       /* 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);
-       if (mail_create(vms->mailstream, mailbox) == NIL) 
-               ast_debug(5, "Folder exists.\n");
-       else
-               ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
+       if (newmsgs)
+               *newmsgs = 0;
+       if (oldmsgs)
+               *oldmsgs = 0;
+       if (urgentmsgs)
+               *urgentmsgs = 0;
 
-       ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
-       res = mail_copy(vms->mailstream, sequence, (char *)mbox(box));
-       if (res == 1) return 0;
-       return 1;
-#else
-       char *dir = vms->curdir;
-       char *username = vms->username;
-       char *context = vmu->context;
-       char sfn[PATH_MAX];
-       char dfn[PATH_MAX];
-       char ddir[PATH_MAX];
-       const char *dbox = mbox(box);
-       int x, i;
-       create_dirpath(ddir, sizeof(ddir), context, username, dbox);
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox))
+               return 0;
 
-       if (vm_lock_path(ddir))
-               return ERROR_LOCK_PATH;
+       ast_copy_string(tmp, mailbox, sizeof(tmp));
+       
+       context = strchr(tmp, '@');
+       if (context) {
+               *context = '\0';
+               context++;
+       } else
+               context = "default";
+       
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt) {
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLFetch(stmt);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               *newmsgs = atoi(rowdata);
+               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
-       x = last_message_index(vmu, ddir) + 1;
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt) {
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLFetch(stmt);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               *oldmsgs = atoi(rowdata);
 
-       if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
-               x--;
-               for (i = 1; i <= x; i++) {
-                       /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
-                       make_file(sfn, sizeof(sfn), ddir, i);
-                       make_file(dfn, sizeof(dfn), ddir, i - 1);
-                       if (EXISTS(ddir, i, sfn, NULL)) {
-                               RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
-                       } else
-                               break;
+               if (!urgentmsgs) {
+                       x = 0;
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
                }
-       } else {
-               if (x >= vmu->maxmsg) {
-                       ast_unlock_path(ddir);
-                       return -1;
+
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt) {
+                       ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
                }
-       }
-       make_file(sfn, sizeof(sfn), dir, msg);
-       make_file(dfn, sizeof(dfn), ddir, x);
-       if (strcmp(sfn, dfn)) {
-               COPY(dir, msg, ddir, x, username, context, sfn, dfn);
-       }
-       ast_unlock_path(ddir);
-#endif
-       return 0;
+               res = SQLFetch(stmt);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               *urgentmsgs = atoi(rowdata);
+               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+               x = 0;
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+               
+yuck:  
+       return x;
 }
 
-static int adsi_logo(unsigned char *buf)
+static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 {
-       int bytes = 0;
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
-       return bytes;
+       return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
 }
 
-static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
+/*!
+ * \brief Gets the number of messages that exist in a mailbox folder.
+ * \param context
+ * \param mailbox
+ * \param folder
+ * 
+ * This method is used when ODBC backend is used.
+ * \return The number of messages in this mailbox folder (zero or more).
+ */
+static int messagecount(const char *context, const char *mailbox, const char *folder)
 {
-       unsigned char buf[256];
-       int bytes=0;
-       int x;
-       char num[5];
+       struct odbc_obj *obj = NULL;
+       int nummsgs = 0;
+       int res;
+       SQLHSTMT stmt = NULL;
+       char sql[PATH_MAX];
+       char rowdata[20];
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
+       if (!folder)
+               folder = "INBOX";
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox))
+               return 0;
 
-       *useadsi = 0;
-       bytes += ast_adsi_data_mode(buf + bytes);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt) {
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       goto yuck;
+               }
+               res = SQLFetch(stmt);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       goto yuck;
+               }
+               res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+                       goto yuck;
+               }
+               nummsgs = atoi(rowdata);
+               SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
-       bytes = 0;
-       bytes += adsi_logo(buf);
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
-#ifdef DISPLAY
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
+yuck:
+       if (obj)
+               ast_odbc_release_obj(obj);
+       return nummsgs;
+}
+
+/** 
+ * \brief Determines if the given folder has messages.
+ * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
+ * 
+ * This function is used when the mailbox is stored in an ODBC back end.
+ * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
+ * \return 1 if the folder has one or more messages. zero otherwise.
+ */
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+       char tmp[256], *tmp2 = tmp, *box, *context;
+       ast_copy_string(tmp, mailbox, sizeof(tmp));
+       while ((context = box = strsep(&tmp2, ","))) {
+               strsep(&context, "@");
+               if (ast_strlen_zero(context))
+                       context = "default";
+               if (messagecount(context, box, folder))
+                       return 1;
+       }
+       return 0;
+}
 #endif
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       bytes += ast_adsi_data_mode(buf + bytes);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+#ifndef IMAP_STORAGE
+/*! 
+ * \brief Copies a message from one mailbox to another.
+ * \param chan
+ * \param vmu
+ * \param imbox
+ * \param msgnum
+ * \param duration
+ * \param recip
+ * \param fmt
+ * \param dir
+ *
+ * This is only used by file storage based mailboxes.
+ *
+ * \return zero on success, -1 on error.
+ */
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+{
+       char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
+       const char *frombox = mbox(imbox);
+       int recipmsgnum;
 
-       if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
-               bytes = 0;
-               bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
-               bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
-               bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-               bytes += ast_adsi_voice_mode(buf + bytes, 0);
-               ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-               return 0;
+       ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
+
+       if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
+               create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
+       } else {
+               create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
        }
+       
+       if (!dir)
+               make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
+       else
+               ast_copy_string(fromdir, dir, sizeof(fromdir));
 
-#ifdef DISPLAY
-       /* Add a dot */
-       bytes = 0;
-       bytes += ast_adsi_logo(buf);
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
-       bytes = 0;
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+       make_file(frompath, sizeof(frompath), fromdir, msgnum);
+       make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
 
-#ifdef DISPLAY
-       /* Add another dot */
-       bytes = 0;
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
-       bytes += ast_adsi_voice_mode(buf + bytes, 0);
+       if (vm_lock_path(todir))
+               return ERROR_LOCK_PATH;
 
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+       recipmsgnum = last_message_index(recip, todir) + 1;
+       if (recipmsgnum < recip->maxmsg) {
+               make_file(topath, sizeof(topath), todir, recipmsgnum);
+               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);
+       }
+       ast_unlock_path(todir);
+       notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
+       
+       return 0;
+}
 #endif
+#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
 
-       bytes = 0;
-       /* These buttons we load but don't use yet */
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+static int messagecount(const char *context, const char *mailbox, const char *folder)
+{
+       return __has_voicemail(context, mailbox, folder, 0);
+}
 
-#ifdef DISPLAY
-       /* Add another dot */
-       bytes = 0;
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
+static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
+{
+       DIR *dir;
+       struct dirent *de;
+       char fn[256];
+       int ret = 0;
 
-       bytes = 0;
-       for (x=0;x<5;x++) {
-               snprintf(num, sizeof(num), "%d", x);
-               bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
-       }
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox))
+               return 0;
 
-#ifdef DISPLAY
-       /* Add another dot */
-       bytes = 0;
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
+       if (ast_strlen_zero(folder))
+               folder = "INBOX";
+       if (ast_strlen_zero(context))
+               context = "default";
 
-       if (ast_adsi_end_download(chan)) {
-               bytes = 0;
-               bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
-               bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
-               bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-               bytes += ast_adsi_voice_mode(buf + bytes, 0);
-               ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+       snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
+
+       if (!(dir = opendir(fn)))
                return 0;
-       }
-       bytes = 0;
-       bytes += ast_adsi_download_disconnect(buf + bytes);
-       bytes += ast_adsi_voice_mode(buf + bytes, 0);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
 
-       ast_debug(1, "Done downloading scripts...\n");
+       while ((de = readdir(dir))) {
+               if (!strncasecmp(de->d_name, "msg", 3)) {
+                       if (shortcircuit) {
+                               ret = 1;
+                               break;
+                       } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
+                               if (shortcircuit) return 1;
+                               ret++;
+                       }
+               }
+       }
 
-#ifdef DISPLAY
-       /* Add last dot */
-       bytes = 0;
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-#endif
-       ast_debug(1, "Restarting session...\n");
+       closedir(dir);
 
-       bytes = 0;
-       /* Load the session now */
-       if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
-               *useadsi = 1;
-               bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
-       } else
-               bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
+       /* If we are checking INBOX, we should check Urgent as well */
+       if (strcmp(folder, "INBOX") == 0) {
+               return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
+       } else {
+               return ret;
+       }
+}
 
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+/** 
+ * \brief Determines if the given folder has messages.
+ * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
+ * \param folder the folder to look in
+ *
+ * This function is used when the mailbox is stored in a filesystem back end.
+ * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
+ * \return 1 if the folder has one or more messages. zero otherwise.
+ */
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+       char tmp[256], *tmp2 = tmp, *box, *context;
+       ast_copy_string(tmp, mailbox, sizeof(tmp));
+       while ((box = strsep(&tmp2, ","))) {
+               if ((context = strchr(box, '@')))
+                       *context++ = '\0';
+               else
+                       context = "default";
+               if (__has_voicemail(context, box, folder, 1))
+                       return 1;
+       }
        return 0;
 }
 
-static void adsi_begin(struct ast_channel *chan, int *useadsi)
+
+static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
 {
-       int x;
-       if (!ast_adsi_available(chan))
-               return;
-       x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
-       if (x < 0)
-               return;
-       if (!x) {
-               if (adsi_load_vmail(chan, useadsi)) {
-                       ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
-                       return;
+       char tmp[256];
+       char *context;
+
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox))
+               return 0;
+
+       if (newmsgs)
+               *newmsgs = 0;
+       if (oldmsgs)
+               *oldmsgs = 0;
+       if (urgentmsgs)
+               *urgentmsgs = 0;
+
+       if (strchr(mailbox, ',')) {
+               int tmpnew, tmpold, tmpurgent;
+               char *mb, *cur;
+
+               ast_copy_string(tmp, mailbox, sizeof(tmp));
+               mb = tmp;
+               while ((cur = strsep(&mb, ", "))) {
+                       if (!ast_strlen_zero(cur)) {
+                               if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+                                       return -1;
+                               else {
+                                       if (newmsgs)
+                                     &nb