remove the PBX_ODBC logic from the configure script, and add GENERIC_ODCB logic that...
[asterisk/asterisk.git] / apps / app_voicemail.c
index bbb2af6..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"
@@ -114,16 +109,199 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/stringfields.h"
 #include "asterisk/smdi.h"
 #include "asterisk/event.h"
+#include "asterisk/taskprocessor.h"
 
 #ifdef ODBC_STORAGE
 #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];
@@ -139,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);
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
+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);
+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, 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);
@@ -201,6 +378,8 @@ static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 #define MAXMSG 100
 #define MAXMSGLIMIT 9999
 
+#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
+
 #define BASELINELEN 72
 #define BASEMAXINLINE 256
 #define eol "\r\n"
@@ -226,8 +405,8 @@ 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
-#define ERROR_MAILBOX_FULL     -200
 
 
 enum {
@@ -247,6 +426,8 @@ enum {
        OPT_PREPEND_MAILBOX =  (1 << 4),
        OPT_AUTOPLAY =         (1 << 6),
        OPT_DTMFEXIT =         (1 << 7),
+       OPT_MESSAGE_Urgent =   (1 << 8),
+       OPT_MESSAGE_PRIORITY = (1 << 9)
 } vm_option_flags;
 
 enum {
@@ -265,6 +446,8 @@ AST_APP_OPTIONS(vm_app_options, {
        AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
        AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
        AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
+       AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
+       AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
 });
 
 static int load_config(int reload);
@@ -410,16 +593,18 @@ 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;
        int curmsg;
        int lastmsg;
        int newmessages;
        int oldmessages;
+       int urgentmessages;
        int starting;
        int repeats;
 #ifdef IMAP_STORAGE
@@ -430,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;
@@ -441,35 +627,35 @@ 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) 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) (imap_store_file(a,b,c,d,e,f,g,h,i))
-#define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
+#define 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)
-#define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
+#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 COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h)); 
+#define DELETE(a,b,c,d) (vm_delete(c))
 #endif
 #endif
 
 static char VM_SPOOL_DIR[PATH_MAX];
 
 static char ext_pass_cmd[128];
+static char ext_pass_check_cmd[128];
 
 int my_umask;
 
@@ -491,74 +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";
-
-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";
 
@@ -570,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;
@@ -585,6 +704,7 @@ static int vmmaxsecs;
 static int maxgreet;
 static int skipms;
 static int maxlogins;
+static int minpassword;
 
 /*! Poll mailboxes for changes since there is something external to
  *  app_voicemail that may change them. */
@@ -614,12 +734,21 @@ static struct ast_event_sub *mwi_unsub_sub;
  */
 struct mwi_sub {
        AST_RWLIST_ENTRY(mwi_sub) entry;
+       int old_urgent;
        int old_new;
        int old_old;
        uint32_t uniqueid;
        char mailbox[1];
 };
 
+struct mwi_sub_task {
+       const char *mailbox;
+       const char *context;
+       uint32_t uniqueid;
+};
+
+static struct ast_taskprocessor *mwi_subscription_tps;
+
 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
 
 /* custom audio control prompts for voicemail playback */
@@ -635,6 +764,7 @@ static char vm_newpassword[80] = "vm-newpassword";
 static char vm_passchanged[80] = "vm-passchanged";
 static char vm_reenterpassword[80] = "vm-reenterpassword";
 static char vm_mismatch[80] = "vm-mismatch";
+static char vm_invalid_password[80] = "vm-invalid-password";
 
 static struct ast_flags globalflags = {0};
 
@@ -666,18 +796,35 @@ static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, s
 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
                        char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
-                       signed char record_gain, struct vm_state *vms);
+                       signed char record_gain, struct vm_state *vms, char *flag);
 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
-static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
+static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
+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);
 static void apply_options(struct ast_vm_user *vmu, const char *options);
+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 int is_valid_dtmf(const char *key);
 
 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
 #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.
@@ -699,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)
@@ -732,38 +880,38 @@ 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")) {
                ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
-       } else if (!strcasecmp(var, "saycid")) {
+       } else if (!strcasecmp(var, "saycid")){
                ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
-       } else if (!strcasecmp(var, "sendvoicemail")) {
+       } else if (!strcasecmp(var,"sendvoicemail")){
                ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
-       } else if (!strcasecmp(var, "review")) {
+       } else if (!strcasecmp(var, "review")){
                ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
-       } else if (!strcasecmp(var, "tempgreetwarn")) {
+       } else if (!strcasecmp(var, "tempgreetwarn")){
                ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
        } else if (!strcasecmp(var, "messagewrap")){
                ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);    
        } else if (!strcasecmp(var, "operator")) {
                ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
-       } else if (!strcasecmp(var, "envelope")) {
+       } else if (!strcasecmp(var, "envelope")){
                ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
-       } else if (!strcasecmp(var, "moveheard")) {
+       } else if (!strcasecmp(var, "moveheard")){
                ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
-       } else if (!strcasecmp(var, "sayduration")) {
+       } else if (!strcasecmp(var, "sayduration")){
                ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
-       } else if (!strcasecmp(var, "saydurationm")) {
+       } else if (!strcasecmp(var, "saydurationm")){
                if (sscanf(value, "%d", &x) == 1) {
                        vmu->saydurationm = x;
                } else {
                        ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
                }
-       } else if (!strcasecmp(var, "forcename")) {
+       } else if (!strcasecmp(var, "forcename")){
                ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
-       } else if (!strcasecmp(var, "forcegreetings")) {
+       } else if (!strcasecmp(var, "forcegreetings")){
                ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
        } else if (!strcasecmp(var, "callback")) {
                ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
@@ -811,6 +959,87 @@ static void apply_option(struct ast_vm_user *vmu, const char *var, const char *v
        }
 }
 
+static char *vm_check_password_shell(char *command, char *buf, size_t len) 
+{
+       int fds[2], pid = 0;
+
+       memset(buf, 0, len);
+
+       if (pipe(fds)) {
+               snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
+       } else {
+               /* good to go*/
+               pid = ast_safe_fork(0);
+
+               if (pid < 0) {
+                       /* ok maybe not */
+                       close(fds[0]);
+                       close(fds[1]);
+                       snprintf(buf, len, "FAILURE: Fork failed");
+               } else if (pid) {
+                       /* parent */
+                       close(fds[1]);
+                       if (read(fds[0], buf, len) < 0) {
+                               ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
+                       }
+                       close(fds[0]);
+               } else {
+                       /*  child */
+                       AST_DECLARE_APP_ARGS(arg,
+                               AST_APP_ARG(v)[20];
+                       );
+                       char *mycmd = ast_strdupa(command);
+
+                       close(fds[0]);
+                       dup2(fds[1], STDOUT_FILENO);
+                       close(fds[1]);
+                       ast_close_fds_above_n(STDOUT_FILENO);
+
+                       AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
+
+                       execv(arg.v[0], arg.v); 
+                       printf("FAILURE: %s", strerror(errno));
+                       _exit(0);
+               }
+       }
+       return buf;
+}
+
+/*!
+ * \brief Check that password meets minimum required length
+ * \param vmu The voicemail user to change the password for.
+ * \param password The password string to check
+ *
+ * \return zero on ok, 1 on not ok.
+ */
+static int check_password(struct ast_vm_user *vmu, char *password)
+{
+       /* check minimum length */
+       if (strlen(password) < minpassword)
+               return 1;
+       if (!ast_strlen_zero(ext_pass_check_cmd)) {
+               char cmd[255], buf[255];
+
+               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))) {
+                       ast_debug(5, "Result: %s\n", buf);
+                       if (!strncasecmp(buf, "VALID", 5)) {
+                               ast_debug(3, "Passed password check: '%s'\n", buf);
+                               return 0;
+                       } else if (!strncasecmp(buf, "FAILURE", 7)) {
+                               ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
+                               return 0;
+                       } else {
+                               ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
+                               return 1;
+                       }
+               }
+       }
+       return 0;
+}
+
 /*! 
  * \brief Performs a change of the voicemail passowrd in the realtime engine.
  * \param vmu The voicemail user to change the password for.
@@ -825,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;
@@ -861,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);
+       }
 }
 
 /*!
@@ -937,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);
@@ -963,7 +1192,7 @@ static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const cha
 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
 {
        /* This function could be made to generate one from a database, too */
-       struct ast_vm_user *vmu = NULL, *cur;
+       struct ast_vm_user *vmu=NULL, *cur;
        AST_LIST_LOCK(&users);
 
        if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
@@ -1026,30 +1255,30 @@ static int reset_user_pw(const char *context, const char *mailbox, const char *n
  */
 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
 {
-       struct ast_config *cfg = NULL;
-       struct ast_variable *var = NULL;
-       struct ast_category *cat = NULL;
-       char *category = NULL, *value = NULL, *new = NULL;
-       const char *tmp = NULL;
+       struct ast_config   *cfg=NULL;
+       struct ast_variable *var=NULL;
+       struct ast_category *cat=NULL;
+       char *category=NULL, *value=NULL, *new=NULL;
+       const char *tmp=NULL;
        struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
        if (!change_password_realtime(vmu, 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))) {
                                        ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
                                        break;
                                }
-                               value = strstr(tmp, ",");
+                               value = strstr(tmp,",");
                                if (!value) {
                                        ast_log(AST_LOG_WARNING, "variable has bad format.\n");
                                        break;
                                }
-                               new = alloca(strlen(value) + strlen(newpassword) + 1);
-                               sprintf(new, "%s%s", newpassword, value);
+                               new = alloca((strlen(value)+strlen(newpassword)+1));
+                               sprintf(new,"%s%s", newpassword, value);
                                if (!(cat = ast_category_get(cfg, category))) {
                                        ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
                                        break;
@@ -1060,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);
@@ -1075,7 +1304,7 @@ static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
                                        ast_debug(3, "looks like we need to make vmsecret!\n");
                                        var = ast_variable_new("vmsecret", newpassword, "");
                                } 
-                               new = alloca(strlen(newpassword) + 1);
+                               new = alloca(strlen(newpassword)+1);
                                sprintf(new, "%s", newpassword);
                                if (!(cat = ast_category_get(cfg, category))) {
                                        ast_debug(4, "failed to get category!\n");
@@ -1090,14 +1319,14 @@ 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");
        }
 }
 
 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
 {
        char buf[255];
-       snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
+       snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
        if (!ast_safe_system(buf)) {
                ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
                /* Reset the password in memory, too */
@@ -1120,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)
+/*! 
+ * \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/msg%04d", dir, 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
@@ -1177,1557 +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");
+       /* 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;
+               }
+       }
 
-               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
-               if (msgnum > -1)
-                       make_file(fn, sizeof(fn), dir, msgnum);
-               else
-                       ast_copy_string(fn, dir, sizeof(fn));
+       /* 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';
 
-               /* Create the information file */
-               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-               
-               if (!(f = fopen(full_fn, "w+"))) {
-                       ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
-                       goto yuck;
-               }
-               
-               snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
-               snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               res = SQLFetch(stmt);
-               if (res == SQL_NO_DATA) {
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_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;
+       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;
                }
-               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!\n[%s]\n\n", 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);
-                       }
+               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;
                }
-               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;
-               }
-               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;
+       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 = 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;
+       }
+       
+       /* 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;
                }
-               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 == 1) { /* Old messages */
+                       return vms_p->oldmessages;
+               }
+               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) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? 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));
 
-/*!
- * \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 store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
-{
-       int x = 0;
-       int res;
-       int fd = -1;
-       void *fdm = MAP_FAILED;
-       size_t fdlen = -1;
-       SQLHSTMT stmt;
-       SQLLEN len;
-       char sql[PATH_MAX];
-       char msgnums[20];
-       char fn[PATH_MAX];
-       char full_fn[PATH_MAX];
-       char fmt[80] = "";
-       char *c;
-       const char *context = "";
-       const char *macrocontext = "";
-       const char *callerid = "";
-       const char *origtime = ""; 
-       const char *duration = "";
-       const char *category = "";
-       struct ast_config *cfg = NULL;
-       struct odbc_obj *obj;
-       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+       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;
+       }
 
-       delete_file(dir, msgnum);
-       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));
-               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));
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               if (cfg) {
-                       context = ast_variable_retrieve(cfg, "message", "context");
-                       if (!context) context = "";
-                       macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
-                       if (!macrocontext) macrocontext = "";
-                       callerid = ast_variable_retrieve(cfg, "message", "callerid");
-                       if (!callerid) callerid = "";
-                       origtime = ast_variable_retrieve(cfg, "message", "origtime");
-                       if (!origtime) origtime = "";
-                       duration = ast_variable_retrieve(cfg, "message", "duration");
-                       if (!duration) duration = "";
-                       category = ast_variable_retrieve(cfg, "message", "category");
-                       if (!category) category = "";
-               }
-               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");
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               } 
-               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");
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
-               }
-               if (!ast_strlen_zero(category)) 
-                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
-               else
-                       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)", odbc_table);
-               res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+       if (!strcmp(fmt, "wav49"))
+               fmt = "WAV";
+       ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
+
+       /* 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;
+       }
+
+       if (msgnum < 0 && imapgreetings) {
+               if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
+                       ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
+                       return -1;
                }
-               len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
-               SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
-               SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
-               SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
-               SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
-               SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
-               SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
-               SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
-               SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
-               SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
-               SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
-               if (!ast_strlen_zero(category))
-                       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
-               res = ast_odbc_smart_execute(obj, stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-                       ast_odbc_release_obj(obj);
-                       goto yuck;
+               imap_delete_old_greeting(fn, vms);
+       }
+       
+       make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, 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;
                }
-               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 (cfg)
-               ast_config_destroy(cfg);
-       if (fdm != MAP_FAILED)
-               munmap(fdm, fdlen);
-       if (fd > -1)
-               close(fd);
-       return x;
-}
-
-/*!
- * \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 };
+       }
+       ((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;
 
-       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; 
 }
 
-#else
-#ifndef IMAP_STORAGE
 /*!
- * \brief Find all .txt files - even if they are not in sequence from 0000.
- * \param vmu
- * \param dir
- *
- * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ * \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 count of messages, zero or more.
+ * \return zero on success, -1 on error.
  */
-static int count_messages(struct ast_vm_user *vmu, char *dir)
-{
-
-       int vmcount = 0;
-       DIR *vmdir = NULL;
-       struct dirent *vment = NULL;
 
-       if (vm_lock_path(dir))
-               return ERROR_LOCK_PATH;
+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;
 
-       if ((vmdir = opendir(dir))) {
-               while ((vment = readdir(vmdir))) {
-                       if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
-                               vmcount++;
+       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;
+                               }
+                       }
                }
-               closedir(vmdir);
+               return 0;
        }
-       ast_unlock_path(dir);
-       
-       return vmcount;
+       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;
 }
 
-/*!
- * \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 inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
 {
-       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);
-       }
-       rename(stxt, dtxt);
+       return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
 }
-#endif
 
-#ifndef IMAP_STORAGE
-/*! 
- * \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.
+/** 
+ * \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
  *
- * \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.
+ * 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 int last_message_index(struct ast_vm_user *vmu, char *dir)
-{
-       int x;
-       unsigned char map[MAXMSGLIMIT] = "";
-       DIR *msgdir;
-       struct dirent *msgdirent;
-       int msgdirint;
-
-       /* 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);
 
-       for (x = 0; x < vmu->maxmsg; x++) {
-               if (map[x] == 0)
-                       break;
+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;
+                       }
+               }
        }
-
-       return x - 1;
+       if ((context= strchr(tmp, '@')))
+               *context++ = '\0';
+       else
+               context = "default";
+       return messagecount(context, tmp, folder) ? 1 : 0;
 }
 
-#endif /* #ifndef IMAP_STORAGE */
-#endif /* #else of #ifdef ODBC_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.
+ * \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
  *
- * 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.
+ * This works with IMAP storage based mailboxes.
  *
  * \return zero on success, -1 on error.
  */
-static int copy(char *infile, char *outfile)
+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)
 {
-       int ifd;
-       int ofd;
-       int res;
-       int len;
-       char buf[4096];
-
-#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 */
+       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;
        }
-#endif
+       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;
 }
 
-/*!
- * \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 imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
 {
-       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;
-                       }
-               }
-               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);
+       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));
        }
-       copy(frompath2, topath2);
-       ast_variables_destroy(var);
-}
-
-/*! 
- * \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)
-{
-       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, 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));
        }
-       snprintf(txt, txtsize, "%s.txt", file);
-       unlink(txt);
-       return ast_filedelete(file, NULL);
-}
 
-/*!
- * \brief utility used by inchar(), for base_encode()
- */
-static int inbuf(struct baseio *bio, FILE *fi)
-{
-       int l;
+       /* Build up server information */
+       ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
 
-       if (bio->ateof)
-               return 0;
+       /* Add authentication user if present */
+       if (!ast_strlen_zero(authuser))
+               ast_build_string(&t, &left, "/authuser=%s", authuser);
 
-       if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
-               if (ferror(fi))
-                       return -1;
+       /* Add flags if present */
+       if (!ast_strlen_zero(imapflags))
+               ast_build_string(&t, &left, "/%s", imapflags);
 
-               bio->ateof = 1;
-               return 0;
+       /* 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));
+               }
        }
-
-       bio->iolen = l;
-       bio->iocp = 0;
-
-       return 1;
 }
 
-/*!
- * \brief utility used by base_encode()
- */
-static int inchar(struct baseio *bio, FILE *fi)
+static int init_mailstream(struct vm_state *vms, int box)
 {
-       if (bio->iocp >= bio->iolen) {
-               if (!inbuf(bio, fi))
-                       return EOF;
-       }
-
-       return bio->iobuf[bio->iocp++];
-}
+       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? */
 
-/*!
- * \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)
+       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;
-
-               bio->linelength= 0;
+               }
+               get_mailbox_delimiter(stream);
+               /* update delimiter in imapfolder */
+               for (cp = imapfolder; *cp; cp++)
+                       if (*cp == '/')
+                               *cp = delimiter;
        }
-
-       if (putc(((unsigned char)c), so) == EOF)
+       /* 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;
-
-       bio->linelength++;
-
-       return 1;
+       } else {
+               return 0;
+       }
 }
 
-/*!
- * \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)
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
 {
-       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;
+       SEARCHPGM *pgm;
+       SEARCHHEADER *hdr;
+       int ret, urgent = 0;
 
-       memset(&bio, 0, sizeof(bio));
-       bio.iocp = BASEMAXINLINE;
+       /* If Urgent, then look at INBOX */
+       if (box == 11) {
+               box = NEW_FOLDER;
+               urgent = 1;
+       }
 
-       if (!(fi = fopen(filename, "rb"))) {
-               ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
+       ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
+       ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
+
+       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));
+       }
 
-       while (!hiteof) {
-               unsigned char igroup[3], ogroup[4];
-               int c, n;
+       ast_mutex_lock(&vms->lock);
+       pgm = mail_newsearchpgm();
 
-               igroup[0] = igroup[1] = igroup[2] = 0;
+       /* 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;
 
-               for (n = 0; n < 3; n++) {
-                       if ((c = inchar(&bio, fi)) == EOF) {
-                               hiteof = 1;
-                               break;
-                       }
+       /* 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;
+       }
 
-                       igroup[n] = (unsigned char)c;
-               }
+       ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
 
-               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];
+       vms->vmArrayIndex = 0;
+       mail_search_full (vms->mailstream, NULL, pgm, NIL);
+       vms->lastmsg = vms->vmArrayIndex - 1;
+       mail_free_searchpgm(&pgm);
 
-                       if (n < 3) {
-                               ogroup[3] = '=';
+       ast_mutex_unlock(&vms->lock);
+       return 0;
+}
 
-                               if (n < 2)
-                                       ogroup[2] = '=';
-                       }
+static void write_file(char *filename, char *buffer, unsigned long len)
+{
+       FILE *output;
 
-                       for (i = 0; i < 4; i++)
-                               ochar(&bio, ogroup[i], so);
+       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));
                }
        }
+       fclose (output);
+}
 
-       fclose(fi);
-       
-       if (fputs(eol, so) == EOF)
-               return 0;
+static void update_messages_by_imapuser(const char *user, unsigned long number)
+{
+       struct vmstate *vlist = NULL;
 
-       return 1;
+       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;
+       }
+       AST_LIST_UNLOCK(&vmstates);
 }
 
-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)
+void mm_searched(MAILSTREAM *stream, unsigned long number)
 {
-       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");
+       char *mailbox = stream->mailbox, buf[1024] = "", *user;
+
+       if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
+               return;
+
+       update_messages_by_imapuser(user, number);
 }
 
-/*!
- * \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)
+static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
 {
-       char *ptr = to;
-       *ptr++ = '"';
-       for (; ptr < to + len - 1; from++) {
-               if (*from == '"')
-                       *ptr++ = '\\';
-               else if (*from == '\0')
-                       break;
-               *ptr++ = *from;
+       struct ast_variable *var;
+       struct ast_vm_user *vmu;
+
+       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;
        }
-       if (ptr < to + len - 1)
-               *ptr++ = '"';
-       *ptr = '\0';
-       return to;
 }
 
-/*! \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)
+/* Interfaces to C-client */
+
+void mm_exists(MAILSTREAM * stream, unsigned long number)
 {
-       const struct vm_zone *z = NULL;
-       struct timeval t = ast_tvnow();
+       /* 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);
+}
 
-       /* 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;
+
+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);
 }
 
-/*! \brief same as mkstemp, but return a FILE * */
-static FILE *vm_mkftemp(char *template)
+
+void mm_flags(MAILSTREAM * stream, unsigned long number)
 {
-       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;
+       /* 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 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 *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
+
+void mm_notify(MAILSTREAM * stream, char *string, long errflg)
 {
-       char date[256];
-       char host[MAXHOSTNAMELEN] = "";
-       char who[256];
-       char bound[256];
-       char fname[256];
-       char dur[256];
-       char tmpcmd[256];
-       struct ast_tm tm;
-       char *passdata2;
-       size_t len_passdata;
-       char *greeting_attachment;
+       ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
+       mm_log (string, errflg);
+}
 
-#ifdef IMAP_STORAGE
-#define ENDL "\r\n"
-#else
-#define ENDL "\n"
-#endif
 
-       gethostname(host, sizeof(host) - 1);
+void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
+{
+       if (delimiter == '\0') {
+               delimiter = delim;
+       }
 
-       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';
+       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");
+}
 
-       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);
 
-       /* Set date format for voicemail mail */
-       ast_strftime(date, sizeof(date), emaildateformat, &tm);
+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 (!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);
-                       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);
-                       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))
-               fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
-       else
-               fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
-       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);
-               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 (!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);
+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");
+}
+
+
+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;
        }
-       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);
-                       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 message (number %d)" ENDL
+}
 
-               "in mailbox %s from %s, on %s so you might" ENDL
-               "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
-               dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
+
+void mm_dlog(char *string)
+{
+       ast_log(AST_LOG_NOTICE, "%s\n", string);
+}
+
+
+void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
+{
+       struct ast_vm_user *vmu;
+
+       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 {
-               fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
-                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
-       }
-       if (attach_user_voicemail) {
-               /* Eww. We want formats to tell us their own MIME type */
-               char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
-               char tmpdir[256], newtmp[256];
-               int tmpfd = -1;
-       
-               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);
+               AST_LIST_TRAVERSE(&users, vmu, list) {
+                       if (!strcasecmp(mb->user, vmu->imapuser)) {
+                               ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
+                               break;
                        }
                }
-               fprintf(p, "--%s" ENDL, bound);
-               if (msgnum > -1)
-                       fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
-               else
-                       fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
-               fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
-               fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
-               if (msgnum > -1)
-                       fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
-               else
-                       fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
-               snprintf(fname, sizeof(fname), "%s.%s", attach, format);
-               base_encode(fname, p);
-               fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
-               if (tmpfd > -1) {
-                       unlink(fname);
-                       close(tmpfd);
-                       unlink(newtmp);
+               if (!vmu) {
+                       if ((vmu = find_user_realtime_imapuser(mb->user))) {
+                               ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
+                               free_user(vmu);
+                       }
                }
        }
-#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 *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
+
+void mm_critical(MAILSTREAM * stream)
 {
-       FILE *p = NULL;
-       char tmp[80] = "/tmp/astmail-XXXXXX";
-       char tmp2[256];
+}
 
-       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, format, duration, attach_user_voicemail, chan, category, 0);
-               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;
+
+void mm_nocritical(MAILSTREAM * stream)
+{
 }
 
-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)
+
+long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
 {
-       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;
+       kill (getpid (), SIGSTOP);
+       return NIL;
+}
 
-       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 (*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);
-                       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);
-                       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
-               fprintf(p, "Subject: New VM\n\n");
+void mm_fatal(char *string)
+{
+       ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
+}
 
-       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);
-                       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");
+/* 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;
+       }
+       
+       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;
+       }
+
+       ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
+
+       vms->quota_usage = usage;
+       vms->quota_limit = limit;
+}
+
+static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
+{
+       char *start, *eol_pnt;
+       int taglen;
+
+       if (ast_strlen_zero(header) || ast_strlen_zero(tag))
+               return NULL;
+
+       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;
+}
+
+static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
+{
+       char *start, *quote, *eol_pnt;
+
+       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 {
-               fprintf(p, "New %s long msg in box %s\n"
-                               "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
+               eol_pnt = strchr(buf+1,'\"');
+               *eol_pnt = '\0';
+               return buf+1;
        }
-       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;
 }
 
-/*!
- * \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 struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
 {
-       struct ast_tm tm;
-       struct timeval t = ast_tvnow();
-       
-       ast_localtime(&t, &tm, "UTC");
+       struct vm_state *vms_p;
 
-       return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
+       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;
 }
 
-static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
+static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
 {
-       int res = -2;
-       
-#ifdef ODBC_STORAGE
-       int success = 
-#endif
-       RETRIEVE(filename, -1, vmu->mailbox, vmu->context);
-       if (ast_fileexists(filename, NULL, NULL) > 0) {
-               res = ast_streamfile(chan, filename, chan->language);
-               if (res > -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(filename, vmu->mailbox, vmu->context, -1);
+       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;
                }
-#endif
        }
-       DISPOSE(filename, -1);
+       AST_LIST_UNLOCK(&vmstates);
 
-       return res;
+       ast_debug(3, "%s not found in vmstates\n", user);
+
+       return NULL;
 }
 
-static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
+static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
 {
-       int res;
-       char fn[PATH_MAX];
-       char dest[PATH_MAX];
 
-       snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
+       struct vmstate *vlist = NULL;
+       const char *local_context = S_OR(context, "default");
 
-       if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, ""))) {
-               ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
-               return -1;
-       }
+       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;
+               }
 
-       res = play_greeting(chan, vmu, fn, ecodes);
-       if (res == -2) {
-               /* File did not exist */
-               res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
-               if (res)
-                       return res;
-               res = ast_say_digit_str(chan, ext, ecodes, chan->language);
+               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;
+               }
        }
-       if (res)
-               return res;
+       AST_LIST_UNLOCK(&vmstates);
 
-       res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
-       return res;
+       ast_debug(3, "%s not found in vmstates\n", mailbox);
+
+       return NULL;
 }
 
-static void free_user(struct ast_vm_user *vmu)
+static void vmstate_insert(struct vm_state *vms) 
 {
-       if (!ast_test_flag(vmu, VM_ALLOCED))
+       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;
+       
+       v->vms = vms;
 
-       ast_free(vmu);
+       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 void free_zone(struct vm_zone *z)
+static void vmstate_delete(struct vm_state *vms) 
 {
-       ast_free(z);
+       struct vmstate *vc = NULL;
+       struct vm_state *altvms = NULL;
+
+       /* 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;
+       }
+       
+       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;
+               }
+       }
+       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);
 }
 
-/*!
- * \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 void set_update(MAILSTREAM * stream) 
 {
-       static const char *msgs[] = {
-#ifdef IMAP_STORAGE
-               imapfolder,
-#else
-               "INBOX",
-#endif
-               "Old",
-               "Work",
-               "Family",
-               "Friends",
-               "Cust1",
-               "Cust2",
-               "Cust3",
-               "Cust4",
-               "Cust5",
-               "Deleted",
-       };
-       return (id >= 0 && id < (sizeof(msgs) / sizeof(msgs[0]))) ? msgs[id] : "tmp";
+       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);
+
+       vms->updated = 1; /* Set updated flag since mailbox changed */
 }
-#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.
+
+static void init_vm_state(struct vm_state *vms) 
+{
+       int x;
+       vms->vmArrayIndex = 0;
+       for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
+               vms->msgArray[x] = 0;
+       }
+       ast_mutex_init(&vms->lock);
+}
+
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
+{
+       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;
+}
+
+/*! 
+ * \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 /* 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 *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;
+       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");
+               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;
+               }
+               
+               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);
@@ -2735,23 +2912,126 @@ static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
                        goto yuck;
                }
                res = SQLFetch(stmt);
-               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               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;
                }
-               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);
+               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;
                }
-               *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");
+               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;
+                                                       }
+                                               }
+                                       }
+                                       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 last_message_index(struct ast_vm_user *vmu, char *dir)
+{
+       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 };
+
+       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);
@@ -2772,3696 +3052,4220 @@ static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
                        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);
-               *oldmsgs = atoi(rowdata);
-               x = 0;
        } else
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-               
 yuck:  
-       return x;
+       return x - 1;
 }
 
 /*!
- * \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).
+ * \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 messagecount(const char *context, const char *mailbox, const char *folder)
+static int message_exists(char *dir, int msgnum)
 {
-       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 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(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+               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;
                }
-               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;
+}
 
-yuck:
-       if (obj)
-               ast_odbc_release_obj(obj);
-       return nummsgs;
+/*!
+ * \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;
 }
 
-/** 
- * \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.
+/*!
+ * \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.
  * 
- * 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.
+ * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
  */
-static int has_voicemail(const char *mailbox, const char *folder)
+static void delete_file(char *sdir, int smsg)
 {
-       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;
-}
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char msgnums[20];
+       char *argv[] = { sdir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
 
-#elif defined(IMAP_STORAGE)
+       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; 
+}
 
-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)
+/*!
+ * \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)
 {
-       char *myserveremail = serveremail;
-       char fn[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;
-       
-       /* Attach only the first format */
-       fmt = ast_strdupa(fmt);
-       stringp = fmt;
-       strsep(&stringp, "|");
-
-       if (!ast_strlen_zero(vmu->serveremail))
-               myserveremail = vmu->serveremail;
+       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 (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;
-       }
+       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) {
-               init_mailstream(vms, GREETINGS_FOLDER);
-               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, fmt, duration, 1, chan, NULL, 1);
-       /* 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);
+       }
+       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;
        }
-       fread(buf, len, 1, p);
-       ((char *)buf)[len] = '\0';
-       INIT(&str, mail_string, buf, len);
-       init_mailstream(vms, NEW_FOLDER);
-       imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
-       if (!mail_append(vms->mailstream, mailbox, &str))
-               ast_log(AST_LOG_ERROR, "Error while sending the message to %s\n", mailbox);
-       fclose(p);
-       unlink(tmp);
-       ast_free(buf);
-       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! */
-       /* ast_log(AST_LOG_DEBUG, "Before find_user, context is %s and mailbox is %s\n", context, mailbox); */
-       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;
+       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 == 1) { /* Old messages */
-                       return vms_p->oldmessages;
+               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;
-               }
-               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;
-       }
-       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;
+               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;
                }
-               pgm->undeleted = 1;
-               pgm->deleted = 0;
-
-               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;
-               /* 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);
+       } while (0);
+       if (obj) {
+               ast_odbc_release_obj(obj);
        }
-       return 0;
+       if (cfg)
+               ast_config_destroy(cfg);
+       if (fdm != MAP_FAILED)
+               munmap(fdm, fdlen);
+       if (fd > -1)
+               close(fd);
+       return res;
 }
 
 /*!
- * \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 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.
  *
- * \return zero on success, -1 on error.
+ * 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 int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
+static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
 {
-       char tmp[PATH_MAX] = "";
-       char *mailboxnc;
-       char *context;
-       char *mb;
-       char *cur;
-       if (newmsgs)
-               *newmsgs = 0;
-       if (oldmsgs)
-               *oldmsgs = 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;
-       
-       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
-       context = strchr(tmp, '@');
-       if (strchr(mailbox_context, ',')) {
-               int tmpnew, tmpold;
-               ast_copy_string(tmp, mailbox_context, sizeof(tmp));
-               mb = tmp;
-               while ((cur = strsep(&mb, ", "))) {
-                       if (!ast_strlen_zero(cur)) {
-                               if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
-                                       return -1;
-                               else {
-                                       if (newmsgs)
-                                               *newmsgs += tmpnew; 
-                                       if (oldmsgs)
-                                               *oldmsgs += tmpold;
-                               }
-                       }
-               }
-               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;
-       }
-       return 0;
-}
-       
+       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 };
 
-/** 
- * \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 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 int has_voicemail(const char *mailbox, const char *folder)
-{
-       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;
-                       }
-               }
-       }
-       if ((context= strchr(tmp, '@')))
-               *context++ = '\0';
-       else
-               context = "default";
-       return messagecount(context, tmp, folder) ? 1 : 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), "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 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 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 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)
+static int remove_file(char *dir, int msgnum)
 {
-       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;
-       }
-       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;
+       char fn[PATH_MAX];
+       char full_fn[PATH_MAX];
+       char msgnums[80];
+       
+       if (msgnum > -1) {
+               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+               make_file(fn, sizeof(fn), dir, msgnum);
+       } else
+               ast_copy_string(fn, dir, sizeof(fn));
+       ast_filedelete(fn, NULL);       
+       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+       unlink(full_fn);
+       return 0;
 }
-
-#endif
+#else
 #ifndef IMAP_STORAGE
-/*! 
- * \brief Copies a message from one mailbox to another.
- * \param chan
+/*!
+ * \brief Find all .txt files - even if they are not in sequence from 0000.
  * \param vmu
- * \param imbox
- * \param msgnum
- * \param duration
- * \param recip
- * \param fmt
  * \param dir
  *
- * 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).
  *
- * \return zero on success, -1 on error.
+ * \return the count of messages, zero or more.
  */
-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)
+static int count_messages(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;
-
-       ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
-
-       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));
 
-       make_file(frompath, sizeof(frompath), fromdir, msgnum);
-       make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
+       int vmcount = 0;
+       DIR *vmdir = NULL;
+       struct dirent *vment = NULL;
 
-       if (vm_lock_path(todir))
+       if (vm_lock_path(dir))
                return ERROR_LOCK_PATH;
 
-       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);
-       } else {
-               ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
+       if ((vmdir = opendir(dir))) {
+               while ((vment = readdir(vmdir))) {
+                       if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
+                               vmcount++;
+                       }
+               }
+               closedir(vmdir);
        }
-       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));
+       ast_unlock_path(dir);
        
-       return 0;
+       return vmcount;
 }
-#endif
-#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
 
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+/*!
+ * \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)
 {
-       return __has_voicemail(context, mailbox, folder, 0);
+       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);
+       }
+       rename(stxt, dtxt);
 }
 
-
-static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
+/*! 
+ * \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)
 {
-       DIR *dir;
-       struct dirent *de;
-       char fn[256];
-       int ret = 0;
-
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
-               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 (!(dir = opendir(fn)))
-               return 0;
+       int x;
+       unsigned char map[MAXMSGLIMIT] = "";
+       DIR *msgdir;
+       struct dirent *msgdirent;
+       int msgdirint;
 
-       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))
-                               ret++;
-               }
+       /* 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);
 
-       closedir(dir);
+       for (x = 0; x < vmu->maxmsg; x++) {
+               if (map[x] == 0)
+                       break;
+       }
 
-       return ret;
+       return x - 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
+#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.
  *
- * 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.
+ * 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 has_voicemail(const char *mailbox, const char *folder)
+static int copy(char *infile, char *outfile)
 {
-       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;
+       int ifd;
+       int ofd;
+       int res;
+       int len;
+       char buf[4096];
+
+#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 {
+               /* Hard link succeeded */
+               return 0;
        }
-       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
 
-static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
+/*! 
+ * \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)
 {
-       char tmp[256];
-       char *context;
+       char *txt;
+       int txtsize = 0;
 
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
-               return 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);
+}
 
-       if (newmsgs)
-               *newmsgs = 0;
-       if (oldmsgs)
-               *oldmsgs = 0;
+/*!
+ * \brief utility used by inchar(), for base_encode()
+ */
+static int inbuf(struct baseio *bio, FILE *fi)
+{
+       int l;
 
-       if (strchr(mailbox, ',')) {
-               int tmpnew, tmpold;
-               char *mb, *cur;
+       if (bio->ateof)
+               return 0;
 
-               ast_copy_string(tmp, mailbox, sizeof(tmp));
-               mb = tmp;
-               while ((cur = strsep(&mb, ", "))) {
-                       if (!ast_strlen_zero(cur)) {
-                               if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
-                                       return -1;
-                               else {
-                                       if (newmsgs)
-                                               *newmsgs += tmpnew; 
-                                       if (oldmsgs)
-                                               *oldmsgs += tmpold;
-                               }
-                       }
-               }
+       if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
+               if (ferror(fi))
+                       return -1;
+
+               bio->ateof = 1;
                return 0;
        }
 
-       ast_copy_string(tmp, mailbox, sizeof(tmp));
-       
-       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);
+       bio->iolen= l;
+       bio->iocp= 0;
 
-       return 0;
+       return 1;
 }
 
-#endif
-
-static void run_externnotify(char *context, char *extension)
+/*!
+ * \brief utility used by base_encode()
+ */
+static int inchar(struct baseio *bio, FILE *fi)
 {
-       char arguments[255];
-       char ext_context[256] = "";
-       int newvoicemails = 0, oldvoicemails = 0;
-       struct ast_smdi_mwi_message *mwi_msg;
+       if (bio->iocp>=bio->iolen) {
+               if (!inbuf(bio, fi))
+                       return EOF;
+       }
 
-       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));
+       return bio->iobuf[bio->iocp++];
+}
 
-       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 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;
 
-               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);
-               }
+               bio->linelength= 0;
        }
 
-       if (!ast_strlen_zero(externnotify)) {
-               if (inboxcount(ext_context, &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&", externnotify, context, extension, newvoicemails);
-                       ast_debug(1, "Executing %s\n", arguments);
-                       ast_safe_system(arguments);
-               }
-       }
+       if (putc(((unsigned char)c),so) == EOF)
+               return -1;
+
+       bio->linelength++;
+
+       return 1;
 }
 
 /*!
- * \brief Variables used for saving a voicemail.
+ * \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.
  *
- * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
+ * 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.
  */
-struct leave_vm_options {
-       unsigned int flags;
-       signed char record_gain;
-       char *exitcontext;
-};
-
-/*!
- * \brief Prompts the user and records a voicemail to a mailbox.
- * \param chan
- * \param ext
- * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
- * 
- * 
- * 
- * \return zero on success, -1 on error.
- */
-static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
+static int base_encode(char *filename, FILE *so)
 {
-#ifdef IMAP_STORAGE
-       int newmsgs;
-       int oldmsgs;
-       struct vm_state *vms = NULL;
-#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*#";
+       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_copy_string(tmp, ext, sizeof(tmp));
-       ext = tmp;
-       if ((context = strchr(tmp, '@'))) {
-               *context++ = '\0';
-               tmpptr = strchr(context, '&');
-       } else {
-               tmpptr = strchr(ext, '&');
-       }
+       memset(&bio, 0, sizeof(bio));
+       bio.iocp = BASEMAXINLINE;
 
-       if (tmpptr)
-               *tmpptr++ = '\0';
+       if (!(fi = fopen(filename, "rb"))) {
+               ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
+               return -1;
+       }
 
-       category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
+       while (!hiteof){
+               unsigned char igroup[3], ogroup[4];
+               int c,n;
 
-       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;
-       }
-       /* 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));
+               igroup[0]= igroup[1]= igroup[2]= 0;
 
-       /* 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);
-       }
-       /* 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;
-       }
+               for (n= 0;n<3;n++) {
+                       if ((c = inchar(&bio, fi)) == EOF) {
+                               hiteof= 1;
+                               break;
+                       }
 
-       RETRIEVE(tempfile, -1, ext, context);
+                       igroup[n]= (unsigned char)c;
+               }
 
-       if (ast_fileexists(tempfile, NULL, NULL) > 0)
-               ast_copy_string(prefile, tempfile, sizeof(prefile));
+               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];
 
-       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");
+                       if (n<3) {
+                               ogroup[3]= '=';
 
-       /* 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 (n<2)
+                                       ogroup[2]= '=';
                        }
-               } 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;
+
+                       for (i= 0;i<4;i++)
+                               ochar(&bio, ogroup[i], so);
                }
        }
 
-       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;
-       }
+       fclose(fi);
+       
+       if (fputs(eol,so)==EOF)
+               return 0;
 
-       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);
-               }
+       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_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);
+}
+
+/*!
+ * \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;
+}
 
-       /* Play the beginning intro if desired */
-       if (!ast_strlen_zero(prefile)) {
-               res = play_greeting(chan, vmu, prefile, ecodes);
-               if (res == -2) {
-                       /* The file did not exist */
-                       ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
-                       res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
-               }
-               if (res < 0) {
-                       ast_debug(1, "Hang up during prefile playback\n");
-                       free_user(vmu);
-                       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-                       return -1;
+/*! \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();
+
+       /* 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);
        }
-       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;
+       ast_localtime(&t, tm, z ? z->timezone : NULL);
+       return tm;
+}
+
+/*!\brief Check if the string would need encoding within the MIME standard, to
+ * avoid confusing certain mail software that expects messages to be 7-bit
+ * clean.
+ */
+static int check_mime(const char *str)
+{
+       for (; *str; str++) {
+               if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
+                       return 1;
                }
        }
-       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));
+       return 0;
+}
+
+/*!\brief Encode a string according to the MIME rules for encoding strings
+ * that are not 7-bit clean or contain control characters.
+ *
+ * Additionally, if the encoded string would exceed the MIME limit of 76
+ * characters per line, then the encoding will be broken up into multiple
+ * sections, separated by a space character, in order to facilitate
+ * breaking up the associated header across multiple lines.
+ *
+ * \param start A string to be encoded
+ * \param end An expandable buffer for holding the result
+ * \param preamble The length of the first line already used for this string,
+ * to ensure that each line maintains a maximum length of 76 chars.
+ * \param postamble the length of any additional characters appended to the
+ * line, used to ensure proper field wrapping.
+ * \retval The encoded string.
+ */
+static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
+{
+       char tmp[80];
+       int first_section = 1;
+       size_t endlen = 0, tmplen = 0;
+       *end = '\0';
+
+       tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
+       for (; *start; start++) {
+               int need_encoding = 0;
+               if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
+                       need_encoding = 1;
+               }
+               if ((first_section && need_encoding && preamble + tmplen > 70) ||
+                       (first_section && !need_encoding && preamble + tmplen > 72) ||
+                       (!first_section && need_encoding && tmplen > 70) ||
+                       (!first_section && !need_encoding && tmplen > 72)) {
+                       /* Start new line */
+                       endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
+                       tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
+                       first_section = 0;
+               }
+               if (need_encoding && *start == ' ') {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
+               } else if (need_encoding) {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
+               } else {
+                       tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
                }
-               chan->priority = 0;
-               free_user(vmu);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
-               return 0;
        }
+       snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
+       return end;
+}
 
-       /* 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;
+/*!
+ * \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)
+{
+       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
+#define ENDL "\r\n"
+#else
+#define ENDL "\n"
+#endif
+
+       /* One alloca for multiple fields */
+       len_passdata2 = strlen(vmu->fullname);
+       if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
+               len_passdata2 = tmplen;
+       }
+       if ((tmplen = strlen(fromstring)) > len_passdata2) {
+               len_passdata2 = tmplen;
        }
+       len_passdata2 = len_passdata2 * 3 + 200;
+       passdata2 = alloca(len_passdata2);
 
-       /* 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;
+       if (cidnum) {
+               strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
+       }
+       if (cidname) {
+               strip_control(cidname, enc_cidname, sizeof(enc_cidname));
        }
+       gethostname(host, sizeof(host) - 1);
 
-       if (res < 0) {
-               free_user(vmu);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-               return -1;
+       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);
+
+       /* 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, "Substitution/voicemail"))) {
+                       char *ptr;
+                       memset(passdata2, 0, len_passdata2);
+                       prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
+                       pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
+                       len_passdata = strlen(passdata2) * 3 + 300;
+                       passdata = alloca(len_passdata);
+                       if (check_mime(passdata2)) {
+                               int first_line = 1;
+                               encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
+                               while ((ptr = strchr(passdata, ' '))) {
+                                       *ptr = '\0';
+                                       fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
+                                       first_line = 0;
+                                       passdata = ptr + 1;
+                               }
+                               fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
+                       } else {
+                               fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
+                       }
+                       ast_channel_free(ast);
+               } else {
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+               }
+       } else {
+               fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
        }
-       /* 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, &newmsgs, &oldmsgs);
-               if (res < 0) {
-                       ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
-                       return -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;
                }
-               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;
+               fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
+       } else {
+               fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
+       }
+       if (!ast_strlen_zero(emailsubject)) {
+               struct ast_channel *ast;
+               if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "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);
                        }
-                       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);
+                       ast_channel_free(ast);
+               } else {
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
                }
-               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");
-
-               /* 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;
+       } 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);
                }
-               
-               /* 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;
+       } 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);
                }
+       }
 
-#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;
+       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);
                }
+               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());
 
-#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;
-               }
+               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, "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);
+       }
 
-               /* 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", category ? category : "", NULL);
+       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);
                }
+       }
+}
 
-               /* 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);
-#else
-               res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
-#endif
+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;
 
-               if (txt) {
-                       if (duration < vmminsecs) {
-                               fclose(txt);
-                               ast_verb(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);
-                               }
+       /* Eww. We want formats to tell us their own MIME type */
+       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+
+       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 {
-                               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);
+                               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
 
-                                       /* 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
+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];
 
-                                       snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
-                                       ast_filerename(tmptxtfile, fn, NULL);
-                                       rename(tmptxtfile, txtfile);
+       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;
+}
 
-                                       /* 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));
+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;
 
-                                       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);
-                                       }
+       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);
 
-                                       /* 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);
-                                                       free_user(recip);
-                                               }
-                                       }
-                                       /* Notification and disposal 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));
-#else
-                                               notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
-#endif
-                                               DISPOSE(dir, msgnum);
-                                       }
-                               }
-                       }
+       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);
                }
-               if (res == '0') {
-                       goto transfer;
-               } else if (res > 0)
-                       res = 0;
+       }
 
-               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);
+       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;
+}
+
+/*!
+ * \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();
        
-       return res;
+       ast_localtime(&t, &tm, "UTC");
+
+       return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
 }
 
-#ifndef IMAP_STORAGE
-static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
+static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
 {
-       /* we know max messages, so stop process when number is hit */
+       int res;
+       char fn[PATH_MAX];
+       char dest[PATH_MAX];
 
-       int x, dest;
-       char sfn[PATH_MAX];
-       char dfn[PATH_MAX];
+       snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
 
-       if (vm_lock_path(dir))
-               return ERROR_LOCK_PATH;
+       if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
+               ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
+               return -1;
+       }
 
-       for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
-               make_file(sfn, sizeof(sfn), dir, x);
-               if (EXISTS(dir, x, sfn, NULL)) {
-                       
-                       if (x != dest) {
-                               make_file(dfn, sizeof(dfn), dir, dest);
-                               RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
-                       }
-                       
-                       dest++;
+       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;
        }
-       ast_unlock_path(dir);
-
-       return 0;
+       res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
+       return res;
 }
-#endif
 
-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 ERROR_MAILBOX_FULL;
+
+               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, "   .", "");
-#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);
+yuck:
+       if (obj)
+               ast_odbc_release_obj(obj);
+       return nummsgs;
+}
 
-       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;
+/** 
+ * \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;
        }
-
-#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);
+       return 0;
+}
 #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);
+#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;
 
-#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);
-
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
-
-       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);
-
-#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
-
-       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);
-
-#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
+       ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
 
-       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);
-               return 0;
+       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");
        }
-       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");
+       
+       if (!dir)
+               make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
+       else
+               ast_copy_string(fromdir, dir, sizeof(fromdir));
 
-#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");
+       make_file(frompath, sizeof(frompath), fromdir, msgnum);
+       make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
 
-       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 (vm_lock_path(todir))
+               return ERROR_LOCK_PATH;
 
-       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))
 
-static void adsi_begin(struct ast_channel *chan, int *useadsi)
+static int messagecount(const char *context, const char *mailbox, const char *folder)
 {
-       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;
-               }
-       } else
-               *useadsi = 1;
+       return __has_voicemail(context, mailbox, folder, 0);
 }
 
-static void adsi_login(struct ast_channel *chan)
+static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
 {
-       unsigned char buf[256];
-       int bytes = 0;
-       unsigned char keys[8];
-       int x;
-       if (!ast_adsi_available(chan))
-               return;
-
-       for (x = 0; x < 8; x++)
-               keys[x] = 0;
-       /* Set one key for next */
-       keys[3] = ADSI_KEY_APPS + 3;
+       DIR *dir;
+       struct dirent *de;
+       char fn[256];
+       int ret = 0;
 
-       bytes += adsi_logo(buf + bytes);
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 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);
-       bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
-       bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
-       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
-       bytes += ast_adsi_set_keys(buf + bytes, keys);
-       bytes += ast_adsi_voice_mode(buf + bytes, 0);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox))
+               return 0;
 
-static void adsi_password(struct ast_channel *chan)
-{
-       unsigned char buf[256];
-       int bytes = 0;
-       unsigned char keys[8];
-       int x;
-       if (!ast_adsi_available(chan))
-               return;
+       if (ast_strlen_zero(folder))
+               folder = "INBOX";
+       if (ast_strlen_zero(context))
+               context = "default";
 
-       for (x = 0; x < 8; x++)
-               keys[x] = 0;
-       /* Set one key for next */
-       keys[3] = ADSI_KEY_APPS + 3;
+       snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
 
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       bytes += ast_adsi_input_f