remove the PBX_ODBC logic from the configure script, and add GENERIC_ODCB logic that...
[asterisk/asterisk.git] / apps / app_voicemail.c
index e3718b5..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, char *altfile);
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix);
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
 static void get_mailbox_delimiter(MAILSTREAM *stream);
 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
-static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, char *introfile);
+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,10 +593,10 @@ struct vm_zone {
 struct vm_state {
        char curbox[80];
        char username[80];
+       char context[80];
        char curdir[PATH_MAX];
        char vmbox[PATH_MAX];
        char fn[PATH_MAX];
-       char fn2[PATH_MAX];
        char intro[PATH_MAX];
        int *deleted;
        int *heard;
@@ -421,6 +604,7 @@ struct vm_state {
        int lastmsg;
        int newmessages;
        int oldmessages;
+       int urgentmessages;
        int starting;
        int repeats;
 #ifdef IMAP_STORAGE
@@ -431,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;
@@ -446,31 +631,31 @@ static char odbc_table[80];
 #define EXISTS(a,b,c,d) (message_exists(a,b))
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
-#define DELETE(a,b,c) (delete_file(a,b))
+#define DELETE(a,b,c,d) (delete_file(a,b))
 #else
 #ifdef IMAP_STORAGE
-#define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
 #define DISPOSE(a,b) (imap_remove_file(a,b))
 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
-#define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
+#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
+#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
-#define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
-#define DELETE(a,b,c) (vm_delete(c))
+#define DELETE(a,b,c,d) (vm_imap_delete(b,d))
 #else
 #define RETRIEVE(a,b,c,d)
 #define DISPOSE(a,b)
 #define STORE(a,b,c,d,e,f,g,h,i,j)
-#define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
+#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;
 
@@ -492,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";
 
@@ -571,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;
@@ -586,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. */
@@ -615,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 */
@@ -636,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};
 
@@ -667,11 +796,11 @@ 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 *attach2, 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);
@@ -680,6 +809,22 @@ static int is_valid_dtmf(const char *key);
 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
 #endif
 
+static char *strip_control(const char *input, char *buf, size_t buflen)
+{
+       char *bufptr = buf;
+       for (; *input; input++) {
+               if (*input < 32) {
+                       continue;
+               }
+               *bufptr++ = *input;
+               if (bufptr == buf + buflen - 1) {
+                       break;
+               }
+       }
+       *bufptr = '\0';
+       return buf;
+}
+
 
 /*!
  * \brief Sets default voicemail system options to a voicemail user.
@@ -701,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)
@@ -734,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));
@@ -813,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.
@@ -827,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;
@@ -863,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);
+       }
 }
 
 /*!
@@ -939,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);
@@ -965,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))
@@ -1028,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;
@@ -1062,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);
@@ -1077,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");
@@ -1092,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 */
@@ -1122,40 +1349,35 @@ static int make_dir(char *dest, int len, const char *context, const char *ext, c
        return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
 }
 
-#ifdef IMAP_STORAGE
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix)
+/*! 
+ * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
+ * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
+ * \param len The length of the path string that was written out.
+ * 
+ * The path is constructed as 
+ *     VM_SPOOL_DIRcontext/ext/folder
+ *
+ * \return zero on success, -1 on error.
+ */
+static int make_file(char *dest, const int len, const char *dir, const int num)
 {
-       int res;
-       if ((res = ast_mkdir(dir, 01777))) {
-               ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
-               return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
-       }
-       return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
+       return snprintf(dest, len, "%s/msg%04d", dir, num);
 }
 
-static void vm_imap_delete(int msgnum, struct vm_state *vms)
+/* same as mkstemp, but return a FILE * */
+static FILE *vm_mkftemp(char *template)
 {
-       unsigned long messageNum = 0;
-       char arg[10];
-
-       /* find real message number based on msgnum */
-       /* this may be an index into vms->msgArray based on the msgnum. */
-
-       messageNum = vms->msgArray[msgnum];
-       if (messageNum == 0) {
-               ast_log(AST_LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
-               return;
+       FILE *p = NULL;
+       int pfd = mkstemp(template);
+       chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
+       if (pfd > -1) {
+               p = fdopen(pfd, "w+");
+               if (!p) {
+                       close(pfd);
+                       pfd = -1;
+               }
        }
-       ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
-       /* delete message */
-       snprintf(arg, sizeof(arg), "%lu", messageNum);
-       mail_setflag(vms->mailstream, arg, "\\DELETED");
-}
-
-#endif
-static int make_file(char *dest, int len, char *dir, int num)
-{
-       return snprintf(dest, len, "%s/msg%04d", dir, num);
+       return p;
 }
 
 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
@@ -1179,1610 +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));
 
-struct insert_data {
-       char *sql;
-       char *dir;
-       char *msgnums;
-       void *data;
-       SQLLEN datalen;
-       const char *context;
-       const char *macrocontext;
-       const char *callerid;
-       const char *origtime;
-       const char *duration;
-       char *mailboxuser;
-       char *mailboxcontext;
-       const char *category;
-};
+       snprintf(introfn, sizeof(introfn), "%sintro", fn);
+       if (ast_fileexists(introfn, NULL, NULL) <= 0) {
+               *introfn = '\0';
+       }
+       
+       if (ast_strlen_zero(vmu->email)) {
+               /* We need the vmu->email to be set when we call make_email_file, but
+                * if we keep it set, a duplicate e-mail will be created. So at the end
+                * of this function, we will revert back to an empty string if tempcopy
+                * is 1.
+                */
+               ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
+               tempcopy = 1;
+       }
 
-static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
-{
-       struct insert_data *data = vdata;
-       int res;
-       SQLHSTMT stmt;
-       SQLLEN len = data->datalen;
+       if (!strcmp(fmt, "wav49"))
+               fmt = "WAV";
+       ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
 
-       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               return NULL;
+       /* Make a temporary file instead of piping directly to sendmail, in case the mail
+          command hangs. */
+       if (!(p = vm_mkftemp(tmp))) {
+               ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
+               if (tempcopy)
+                       *(vmu->email) = '\0';
+               return -1;
        }
 
-       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
-       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
-       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &len);
-       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
-       SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
-       SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
-       SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
-       SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
-       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
-       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
-       if (!ast_strlen_zero(data->category)) {
-               SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
+       if (msgnum < 0 && imapgreetings) {
+               if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
+                       ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
+                       return -1;
+               }
+               imap_delete_old_greeting(fn, vms);
        }
-       res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
-       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
-               ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
-               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               return NULL;
+       
+       make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
+       /* read mail file to memory */          
+       len = ftell(p);
+       rewind(p);
+       if (!(buf = ast_malloc(len + 1))) {
+               ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
+               fclose(p);
+               if (tempcopy)
+                       *(vmu->email) = '\0';
+               return -1;
+       }
+       if (fread(buf, len, 1, p) < len) {
+               if (ferror(p)) {
+                       ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
+                       return -1;
+               }
+       }
+       ((char *)buf)[len] = '\0';
+       INIT(&str, mail_string, buf, len);
+       ret = init_mailstream(vms, NEW_FOLDER);
+       if (ret == 0) {
+               imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
+               ast_mutex_lock(&vms->lock);
+               if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
+                       ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
+               ast_mutex_unlock(&vms->lock);
+               fclose(p);
+               unlink(tmp);
+               ast_free(buf);
+       } else {
+               ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
+               fclose(p);
+               unlink(tmp);
+               ast_free(buf);
+               return -1;
        }
+       ast_debug(3, "%s stored\n", fn);
+       
+       if (tempcopy)
+               *(vmu->email) = '\0';
+       
+       return 0;
 
-       return stmt;
 }
 
 /*!
- * \brief Stores a voicemail into the database.
- * \param dir the folder the mailbox folder to store the message.
- * \param mailboxuser the user owning the mailbox folder.
- * \param mailboxcontext
- * \param msgnum the message index for the message to be stored.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * The message sound file and information file is looked up on the file system. 
- * A SQL query is invoked to store the message into the (MySQL) database.
+ * \brief Gets the number of messages that exist in the inbox folder.
+ * \param mailbox_context
+ * \param newmsgs The variable that is updated with the count of new messages within this inbox.
+ * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
+ * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
+ * 
+ * This method is used when IMAP backend is used.
+ * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
  *
- * \return the zero on success -1 on error.
+ * \return zero on success, -1 on error.
  */
-static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
-{
-       int res = 0;
-       int fd = -1;
-       void *fdm = MAP_FAILED;
-       size_t fdlen = -1;
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char msgnums[20];
-       char fn[PATH_MAX];
-       char full_fn[PATH_MAX];
-       char fmt[80] = "";
-       char *c;
-       struct ast_config *cfg = NULL;
-       struct odbc_obj *obj;
-       struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
-       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
 
-       delete_file(dir, msgnum);
-       if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-               return -1;
-       }
+static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+{
+       char tmp[PATH_MAX] = "";
+       char *mailboxnc;
+       char *context;
+       char *mb;
+       char *cur;
+       if (newmsgs)
+               *newmsgs = 0;
+       if (oldmsgs)
+               *oldmsgs = 0;
+       if (urgentmsgs)
+               *urgentmsgs = 0;
 
-       do {
-               ast_copy_string(fmt, vmfmts, sizeof(fmt));
-               c = strchr(fmt, '|');
-               if (c)
-                       *c = '\0';
-               if (!strcasecmp(fmt, "wav49"))
-                       strcpy(fmt, "WAV");
-               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
-               if (msgnum > -1)
-                       make_file(fn, sizeof(fn), dir, msgnum);
-               else
-                       ast_copy_string(fn, dir, sizeof(fn));
-               snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-               cfg = ast_config_load(full_fn, config_flags);
-               snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
-               fd = open(full_fn, O_RDWR);
-               if (fd < 0) {
-                       ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
-                       res = -1;
-                       break;
-               }
-               if (cfg) {
-                       if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
-                               idata.context = "";
-                       }
-                       if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
-                               idata.macrocontext = "";
-                       }
-                       if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
-                               idata.callerid = "";
-                       }
-                       if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
-                               idata.origtime = "";
-                       }
-                       if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
-                               idata.duration = "";
-                       }
-                       if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
-                               idata.category = "";
+       ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox_context))
+               return 0;
+       
+       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+       context = strchr(tmp, '@');
+       if (strchr(mailbox_context, ',')) {
+               int tmpnew, tmpold, tmpurgent;
+               ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+               mb = tmp;
+               while ((cur = strsep(&mb, ", "))) {
+                       if (!ast_strlen_zero(cur)) {
+                               if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+                                       return -1;
+                               else {
+                                       if (newmsgs)
+                                               *newmsgs += tmpnew; 
+                                       if (oldmsgs)
+                                               *oldmsgs += tmpold;
+                                       if (urgentmsgs)
+                                               *urgentmsgs += tmpurgent;
+                               }
                        }
                }
-               fdlen = lseek(fd, 0, SEEK_END);
-               lseek(fd, 0, SEEK_SET);
-               printf("Length is %zd\n", fdlen);
-               fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-               if (fdm == MAP_FAILED) {
-                       ast_log(AST_LOG_WARNING, "Memory map failed!\n");
-                       res = -1;
-                       break;
-               } 
-               idata.data = fdm;
-               idata.datalen = fdlen;
-
-               if (!ast_strlen_zero(idata.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);
-
-               if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
-                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-               } else {
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-                       res = -1;
-               }
-       } while (0);
-       if (obj) {
-               ast_odbc_release_obj(obj);
+               return 0;
        }
-       if (cfg)
-               ast_config_destroy(cfg);
-       if (fdm != MAP_FAILED)
-               munmap(fdm, fdlen);
-       if (fd > -1)
-               close(fd);
-       return res;
+       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 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)
+static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
 {
-       SQLHSTMT stmt;
-       char sql[PATH_MAX];
-       char msgnums[20];
-       char msgnumd[20];
-       struct odbc_obj *obj;
-       char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
-       struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
-
-       delete_file(ddir, dmsg);
-       obj = ast_odbc_request_obj(odbc_database, 0);
-       if (obj) {
-               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
-               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
-               snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
-               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
-               if (!stmt)
-                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
-               else
-                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
-               ast_odbc_release_obj(obj);
-       } else
-               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-       return; 
+       return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
 }
 
-#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 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
  *
- * \return the count of messages, zero or more.
+ * 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 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;
 
-       if ((vmdir = opendir(dir))) {
-               while ((vment = readdir(vmdir))) {
-                       if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
-                               vmcount++;
+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;
+                       }
                }
-               closedir(vmdir);
        }
-       ast_unlock_path(dir);
-       
-       return vmcount;
+       if ((context= strchr(tmp, '@')))
+               *context++ = '\0';
+       else
+               context = "default";
+       return messagecount(context, tmp, folder) ? 1 : 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.
+ * \brief Copies a message from one mailbox to another.
+ * \param chan
+ * \param vmu
+ * \param imbox
+ * \param msgnum
+ * \param duration
+ * \param recip
+ * \param fmt
+ * \param dir
  *
- * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ * This works with IMAP storage based mailboxes.
+ *
+ * \return zero on success, -1 on error.
  */
-static void rename_file(char *sfn, char *dfn)
+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)
 {
-       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);
+       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;
        }
-       rename(stxt, dtxt);
+       if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
+               ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
+               return -1;
+       }
+       if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
+               ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
+               return -1;
+       }
+       snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
+       ast_mutex_lock(&sendvms->lock);
+       if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
+               ast_mutex_unlock(&sendvms->lock);
+               return 0;
+       }
+       ast_mutex_unlock(&sendvms->lock);
+       ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
+       return -1;
 }
-#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.
- *
- * \note Should always be called with a lock already set on dir.
- * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
- */
-static int last_message_index(struct ast_vm_user *vmu, char *dir)
+static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
 {
-       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;
+       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));
        }
-       closedir(msgdir);
 
-       for (x = 0; x < vmu->maxmsg; x++) {
-               if (map[x] == 0)
-                       break;
+       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));
        }
 
-       return x - 1;
-}
+       /* Build up server information */
+       ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
 
-#endif /* #ifndef IMAP_STORAGE */
-#endif /* #else of #ifdef ODBC_STORAGE */
+       /* Add authentication user if present */
+       if (!ast_strlen_zero(authuser))
+               ast_build_string(&t, &left, "/authuser=%s", authuser);
 
-/*!
- * \brief Utility function to copy a file.
- * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
- * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
- *
- * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
- * The copy operation copies up to 4096 bytes at once.
- *
- * \return zero on success, -1 on error.
- */
-static int copy(char *infile, char *outfile)
+       /* Add flags if present */
+       if (!ast_strlen_zero(imapflags))
+               ast_build_string(&t, &left, "/%s", imapflags);
+
+       /* End with username */
+       ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
+       if (box == NEW_FOLDER || box == OLD_FOLDER)
+               snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
+       else if (box == GREETINGS_FOLDER)
+               snprintf(spec, len, "%s%s", tmp, greetingfolder);
+       else {  /* Other folders such as Friends, Family, etc... */
+               if (!ast_strlen_zero(imapparentfolder)) {
+                       /* imapparentfolder would typically be set to INBOX */
+                       snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
+               } else {
+                       snprintf(spec, len, "%s%s", tmp, mbox(box));
+               }
+       }
+}
+
+static int init_mailstream(struct vm_state *vms, int box)
 {
-       int ifd;
-       int ofd;
-       int res;
-       int len;
-       char buf[4096];
+       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? */
 
-#ifdef HARDLINK_WHEN_POSSIBLE
-       /* Hard link if possible; saves disk space & is faster */
-       if (link(infile, outfile)) {
+       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
-               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);
+               /* 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;
                }
-               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
+               get_mailbox_delimiter(stream);
+               /* update delimiter in imapfolder */
+               for (cp = imapfolder; *cp; cp++)
+                       if (*cp == '/')
+                               *cp = delimiter;
+       }
+       /* Now connect to the target folder */
+       imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
+       if (option_debug > 2)
+               ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
+       ast_mutex_lock(&vms->lock);
+       vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+       ast_mutex_unlock(&vms->lock);
+       if (vms->mailstream == NIL) {
+               return -1;
        } else {
-               /* Hard link succeeded */
                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)
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
 {
-       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);
+       SEARCHPGM *pgm;
+       SEARCHHEADER *hdr;
+       int ret, urgent = 0;
+
+       /* If Urgent, then look at INBOX */
+       if (box == 11) {
+               box = NEW_FOLDER;
+               urgent = 1;
        }
-       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;
+       ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
+       ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
 
-       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 ((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));
        }
-       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;
 
-       if (bio->ateof)
-               return 0;
+       ast_mutex_lock(&vms->lock);
+       pgm = mail_newsearchpgm();
 
-       if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
-               if (ferror(fi))
-                       return -1;
+       /* 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;
 
-               bio->ateof = 1;
-               return 0;
+       /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
+       if (box == NEW_FOLDER && urgent == 1) {
+               pgm->unseen = 1;
+               pgm->seen = 0;
+               pgm->flagged = 1;
+               pgm->unflagged = 0;
+       } else if (box == NEW_FOLDER && urgent == 0) {
+               pgm->unseen = 1;
+               pgm->seen = 0;
+               pgm->flagged = 0;
+               pgm->unflagged = 1;
+       } else if (box == OLD_FOLDER) {
+               pgm->seen = 1;
+               pgm->unseen = 0;
        }
 
-       bio->iolen = l;
-       bio->iocp = 0;
+       ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
 
-       return 1;
+       vms->vmArrayIndex = 0;
+       mail_search_full (vms->mailstream, NULL, pgm, NIL);
+       vms->lastmsg = vms->vmArrayIndex - 1;
+       mail_free_searchpgm(&pgm);
+
+       ast_mutex_unlock(&vms->lock);
+       return 0;
 }
 
-/*!
- * \brief utility used by base_encode()
- */
-static int inchar(struct baseio *bio, FILE *fi)
+static void write_file(char *filename, char *buffer, unsigned long len)
 {
-       if (bio->iocp >= bio->iolen) {
-               if (!inbuf(bio, fi))
-                       return EOF;
-       }
+       FILE *output;
 
-       return bio->iobuf[bio->iocp++];
+       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);
 }
 
-/*!
- * \brief utility used by base_encode()
- */
-static int ochar(struct baseio *bio, int c, FILE *so)
+static void update_messages_by_imapuser(const char *user, unsigned long number)
 {
-       if (bio->linelength >= BASELINELEN) {
-               if (fputs(eol, so) == EOF)
-                       return -1;
+       struct vmstate *vlist = NULL;
 
-               bio->linelength= 0;
+       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);
+}
 
-       if (putc(((unsigned char)c), so) == EOF)
-               return -1;
+void mm_searched(MAILSTREAM *stream, unsigned long number)
+{
+       char *mailbox = stream->mailbox, buf[1024] = "", *user;
 
-       bio->linelength++;
+       if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
+               return;
 
-       return 1;
+       update_messages_by_imapuser(user, number);
 }
 
-/*!
- * \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 struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
 {
-       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;
+       struct ast_variable *var;
+       struct ast_vm_user *vmu;
 
-       memset(&bio, 0, sizeof(bio));
-       bio.iocp = BASEMAXINLINE;
+       vmu = ast_calloc(1, sizeof *vmu);
+       if (!vmu)
+               return NULL;
+       ast_set_flag(vmu, VM_ALLOCED);
+       populate_defaults(vmu);
 
-       if (!(fi = fopen(filename, "rb"))) {
-               ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
-               return -1;
+       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;
        }
+}
 
-       while (!hiteof) {
-               unsigned char igroup[3], ogroup[4];
-               int c, n;
+/* Interfaces to C-client */
 
-               igroup[0] = igroup[1] = igroup[2] = 0;
+void mm_exists(MAILSTREAM * stream, unsigned long number)
+{
+       /* 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);
+}
 
-               for (n = 0; n < 3; n++) {
-                       if ((c = inchar(&bio, fi)) == EOF) {
-                               hiteof = 1;
-                               break;
-                       }
 
-                       igroup[n] = (unsigned char)c;
-               }
+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);
+}
 
-               if (n > 0) {
-                       ogroup[0] = dtable[igroup[0] >> 2];
-                       ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
-                       ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
-                       ogroup[3] = dtable[igroup[2] & 0x3F];
 
-                       if (n < 3) {
-                               ogroup[3] = '=';
+void mm_flags(MAILSTREAM * stream, unsigned long number)
+{
+       /* mail_ping will callback here if read mail! */
+       ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
+       if (number == 0) return;
+       set_update(stream);
+}
 
-                               if (n < 2)
-                                       ogroup[2] = '=';
-                       }
 
-                       for (i = 0; i < 4; i++)
-                               ochar(&bio, ogroup[i], so);
-               }
-       }
+void mm_notify(MAILSTREAM * stream, char *string, long errflg)
+{
+       ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
+       mm_log (string, errflg);
+}
 
-       fclose(fi);
-       
-       if (fputs(eol, so) == EOF)
-               return 0;
 
-       return 1;
+void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
+{
+       if (delimiter == '\0') {
+               delimiter = delim;
+       }
+
+       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");
 }
 
-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_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
 {
-       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");
+       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");
 }
 
-/*!
- * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
- * \param from The string to work with.
- * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
- * 
- * \return The destination string with quotes wrapped on it (the to field).
- */
-static char *quote(const char *from, char *to, size_t len)
+
+void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
 {
-       char *ptr = to;
-       *ptr++ = '"';
-       for (; ptr < to + len - 1; from++) {
-               if (*from == '"')
-                       *ptr++ = '\\';
-               else if (*from == '\0')
+       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;
-               *ptr++ = *from;
        }
-       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)
+
+void mm_dlog(char *string)
 {
-       const struct vm_zone *z = NULL;
-       struct timeval t = ast_tvnow();
+       ast_log(AST_LOG_NOTICE, "%s\n", string);
+}
 
-       /* 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))
+
+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 {
+               AST_LIST_TRAVERSE(&users, vmu, list) {
+                       if (!strcasecmp(mb->user, vmu->imapuser)) {
+                               ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
                                break;
+                       }
+               }
+               if (!vmu) {
+                       if ((vmu = find_user_realtime_imapuser(mb->user))) {
+                               ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
+                               free_user(vmu);
+                       }
                }
-               AST_LIST_UNLOCK(&zones);
        }
-       ast_localtime(&t, tm, z ? z->timezone : NULL);
-       return tm;
 }
 
-/*! \brief same as mkstemp, but return a FILE * */
-static FILE *vm_mkftemp(char *template)
+
+void mm_critical(MAILSTREAM * stream)
 {
-       FILE *p = NULL;
-       int pfd = mkstemp(template);
-       chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
-       if (pfd > -1) {
-               p = fdopen(pfd, "w+");
-               if (!p) {
-                       close(pfd);
-                       pfd = -1;
-               }
-       }
-       return p;
 }
 
-/*!
- * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
- * \param p The output file to generate the email contents into.
- * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
- * \param vmu The voicemail user who is sending the voicemail.
- * \param msgnum The message index in the mailbox folder.
- * \param context 
- * \param mailbox The voicemail box to read the voicemail to be notified in this email.
- * \param cidnum The caller ID number.
- * \param cidname The caller ID name.
- * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
- * \param format The message sound file format. i.e. .wav
- * \param duration The time of the message content, in seconds.
- * \param attach_user_voicemail if 1, the sound file is attached to the email.
- * \param chan
- * \param category
- * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
- *
- * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email.  That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
- */
-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
-{
-       char date[256];
-       char host[MAXHOSTNAMELEN] = "";
-       char who[256];
-       char bound[256];
-       char dur[256];
-       struct ast_tm tm;
-       char *passdata2;
-       size_t len_passdata;
-       char *greeting_attachment; 
-       char filename[256];
 
-#ifdef IMAP_STORAGE
-#define ENDL "\r\n"
-#else
-#define ENDL "\n"
-#endif
+void mm_nocritical(MAILSTREAM * stream)
+{
+}
 
-       gethostname(host, sizeof(host) - 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';
+long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
+{
+       kill (getpid (), SIGSTOP);
+       return NIL;
+}
 
-       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_fatal(char *string)
+{
+       ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
+}
 
-       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));
+/* 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 (!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);
+       
+       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;
        }
-       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);
-       } else {
-               fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
-                               "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
-       }
+       ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
 
-       if (attach_user_voicemail) {
-               if (!ast_strlen_zero(attach2)) {
-                       snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
-                       ast_debug(5, "creating attachment filename %s\n", filename);
-                       add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 0, msgnum);
-                       snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
-                       ast_debug(5, "creating second attachment filename %s\n", filename);
-                       add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
-               } else {
-                       snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
-                       ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
-                       add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
-               }
-       }
+       vms->quota_usage = usage;
+       vms->quota_limit = limit;
 }
 
-static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
+static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
 {
-       char tmpdir[256], newtmp[256];
-       char fname[256];
-       char tmpcmd[256];
-       int tmpfd = -1;
+       char *start, *eol_pnt;
+       int taglen;
 
-       /* Eww. We want formats to tell us their own MIME type */
-       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+       if (ast_strlen_zero(header) || ast_strlen_zero(tag))
+               return NULL;
 
-       if (vmu->volgain < -.001 || vmu->volgain > .001) {
-               create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
-               snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
-               tmpfd = mkstemp(newtmp);
-               chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
-               ast_debug(3, "newtmp: %s\n", newtmp);
-               if (tmpfd > -1) {
-                       snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
-                       ast_safe_system(tmpcmd);
-                       attach = newtmp;
-                       ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
-               }
-       }
-       fprintf(p, "--%s" ENDL, bound);
-       if (msgnum > -1)
-               fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
-       else
-               fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
-       fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
-       fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
-       if (msgnum > -1)
-               fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
-       else
-               fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
-       snprintf(fname, sizeof(fname), "%s.%s", attach, format);
-       base_encode(fname, p);
-       if (last)
-               fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
-       if (tmpfd > -1) {
-               unlink(fname);
-               close(tmpfd);
-               unlink(newtmp);
-       }
-       return 0;
-}
-#undef ENDL
+       taglen = strlen(tag) + 1;
+       if (taglen < 1)
+               return NULL;
 
-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)
-{
-       FILE *p = NULL;
-       char tmp[80] = "/tmp/astmail-XXXXXX";
-       char tmp2[256];
+       if (!(start = strstr(header, tag)))
+               return NULL;
 
-       if (vmu && ast_strlen_zero(vmu->email)) {
-               ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
-               return(0);
-       }
-       if (!strcmp(format, "wav49"))
-               format = "WAV";
-       ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
-       /* Make a temporary file instead of piping directly to sendmail, in case the mail
-          command hangs */
-       if ((p = vm_mkftemp(tmp)) == NULL) {
-               ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
-               return -1;
-       } else {
-               make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0);
-               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;
+       /* 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 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)
+static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
 {
-       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;
+       char *start, *quote, *eol_pnt;
 
-       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 (ast_strlen_zero(mailbox))
+               return NULL;
 
-       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");
+       if (!(start = strstr(mailbox, "/user=")))
+               return NULL;
 
-       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");
+       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;
-
-       res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
-       return res;
-}
+       AST_LIST_UNLOCK(&vmstates);
 
-static void free_user(struct ast_vm_user *vmu)
-{
-       if (!ast_test_flag(vmu, VM_ALLOCED))
-               return;
+       ast_debug(3, "%s not found in vmstates\n", mailbox);
 
-       ast_free(vmu);
+       return NULL;
 }
 
-static void free_zone(struct vm_zone *z)
+static void vmstate_insert(struct vm_state *vms) 
 {
-       ast_free(z);
-}
+       struct vmstate *v;
+       struct vm_state *altvms;
 
-/*!
- * \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)
+       /* 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_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 vmstate_delete(struct vm_state *vms) 
 {
-       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 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);
 }
-#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 set_update(MAILSTREAM * stream) 
+{
+       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 */
+}
+
+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);
@@ -2790,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);
+               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;
+}
 
-               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
+/*!
+ * \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);
@@ -2827,1958 +3052,1877 @@ 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:
-       if (obj)
-               ast_odbc_release_obj(obj);
-       return nummsgs;
+yuck:  
+       return x;
 }
 
-/** 
- * \brief Determines if the given folder has messages.
- * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- * 
- * This function is used when the mailbox is stored in an ODBC back end.
- * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
- * \return 1 if the folder has one or more messages. zero otherwise.
+/*!
+ * \brief returns the one-based count for messages.
+ * \param vmu
+ * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
+ * one-based messages.
+ * This method just calls last_message_index and returns +1 of its value.
+ *
+ * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
  */
-static int has_voicemail(const char *mailbox, const char *folder)
+static int count_messages(struct ast_vm_user *vmu, char *dir)
 {
-       char tmp[256], *tmp2 = tmp, *mbox, *context;
-       ast_copy_string(tmp, mailbox, sizeof(tmp));
-       while ((context = mbox = strsep(&tmp2, ","))) {
-               strsep(&context, "@");
-               if (ast_strlen_zero(context))
-                       context = "default";
-               if (messagecount(context, mbox, folder))
-                       return 1;
-       }
-       return 0;
+       return last_message_index(vmu, dir) + 1;
 }
 
-#elif defined(IMAP_STORAGE)
-
-static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, char *introfile)
-{
-       char *myserveremail = serveremail;
-       char fn[PATH_MAX];
-       char intro[PATH_MAX];
-       char mailbox[256];
-       char *stringp;
-       FILE *p = NULL;
-       char tmp[80] = "/tmp/astmail-XXXXXX";
-       long len;
-       void *buf;
-       int tempcopy = 0;
-       int ret;
-       STRING str;
-       
-       /* Attach only the first format */
-       fmt = ast_strdupa(fmt);
-       stringp = fmt;
-       strsep(&stringp, "|");
+/*!
+ * \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)
+{
+       SQLHSTMT stmt;
+       char sql[PATH_MAX];
+       char msgnums[20];
+       char *argv[] = { sdir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
 
-       if (!ast_strlen_zero(vmu->serveremail))
-               myserveremail = vmu->serveremail;
+       struct odbc_obj *obj;
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+               snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt)
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+               else
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+       return; 
+}
 
-       if (msgnum > -1)
-               make_file(fn, sizeof(fn), dir, msgnum);
-       else
-               ast_copy_string (fn, dir, sizeof(fn));
-       
-       if (ast_strlen_zero(vmu->email)) {
-               /* We need the vmu->email to be set when we call make_email_file, but
-                * if we keep it set, a duplicate e-mail will be created. So at the end
-                * of this function, we will revert back to an empty string if tempcopy
-                * is 1.
-                */
-               ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
-               tempcopy = 1;
-       }
+/*!
+ * \brief Copies a voicemail from one mailbox to another.
+ * \param sdir the folder for which to look for the message to be copied.
+ * \param smsg the index of the message to be copied.
+ * \param ddir the destination folder to copy the message into.
+ * \param dmsg the index to be used for the copied message.
+ * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
+ * \param dmailboxcontext The context for the destination user.
+ *
+ * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
+ */
+static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
+{
+       SQLHSTMT stmt;
+       char sql[512];
+       char msgnums[20];
+       char msgnumd[20];
+       struct odbc_obj *obj;
+       char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
+       struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
 
-       if (!ast_strlen_zero(introfile)) {
-               snprintf(intro, sizeof(intro), "%s/msgintro%04d", dir, msgnum);
-       } else {
-               intro[0] = '\0';
-       }
+       delete_file(ddir, dmsg);
+       obj = ast_odbc_request_obj(odbc_database, 0);
+       if (obj) {
+               snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+               snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+               snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext, flag) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt)
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+               else
+                       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               ast_odbc_release_obj(obj);
+       } else
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+       return; 
+}
 
-       if (!strcmp(fmt, "wav49"))
-               fmt = "WAV";
-       ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
+struct insert_data {
+       char *sql;
+       char *dir;
+       char *msgnums;
+       void *data;
+       SQLLEN datalen;
+       SQLLEN indlen;
+       const char *context;
+       const char *macrocontext;
+       const char *callerid;
+       const char *origtime;
+       const char *duration;
+       char *mailboxuser;
+       char *mailboxcontext;
+       const char *category;
+       const char *flag;
+};
 
-       /* Make a temporary file instead of piping directly to sendmail, in case the mail
-          command hangs. */
-       if (!(p = vm_mkftemp(tmp))) {
-               ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
-               if (tempcopy)
-                       *(vmu->email) = '\0';
-               return -1;
-       }
+static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
+{
+       struct insert_data *data = vdata;
+       int res;
+       SQLHSTMT stmt;
 
-       if (msgnum < 0 && imapgreetings) {
-               if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
-                       ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
-                       return -1;
-               }
-               imap_delete_old_greeting(fn, vms);
+       res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               return NULL;
        }
-       
-       make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, intro, fmt, duration, 1, chan, NULL, 1);
-       /* read mail file to memory */          
-       len = ftell(p);
-       rewind(p);
-       if (!(buf = ast_malloc(len + 1))) {
-               ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
-               fclose(p);
-               if (tempcopy)
-                       *(vmu->email) = '\0';
-               return -1;
+
+       SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
+       SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
+       SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
+       SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
+       SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
+       SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
+       SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
+       SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
+       SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
+       SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
+       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
+       if (!ast_strlen_zero(data->category)) {
+               SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
        }
-       fread(buf, len, 1, p);
-       ((char *)buf)[len] = '\0';
-       INIT(&str, mail_string, buf, len);
-       if ((ret = init_mailstream(vms, NEW_FOLDER))) {
-               ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
-               return -1;
+       res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
+       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+               ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
+               SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+               return NULL;
        }
-       
-       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;
-               }
-               if (fold == 1) { /* Old messages */
-                       return vms_p->oldmessages;
-               }
-       }
-
-       /* 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 (!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;
+       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;
                }
-               /* 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 (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 = "";
+                       }
                }
-               pgm->undeleted = 1;
-               pgm->deleted = 0;
+               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;
 
-               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);
+               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 ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
+                       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+               } else {
+                       ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       res = -1;
+               }
+       } while (0);
+       if (obj) {
+               ast_odbc_release_obj(obj);
        }
-       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;
+       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 };
 
-       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;
+       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 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
+/*!
+ * \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.
  *
- * 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.
+ * 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 has_voicemail(const char *mailbox, const char *folder)
+static int remove_file(char *dir, int msgnum)
 {
-       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;
+       char fn[PATH_MAX];
+       char full_fn[PATH_MAX];
+       char msgnums[80];
+       
+       if (msgnum > -1) {
+               snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+               make_file(fn, sizeof(fn), dir, msgnum);
+       } else
+               ast_copy_string(fn, dir, sizeof(fn));
+       ast_filedelete(fn, NULL);       
+       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+       unlink(full_fn);
+       return 0;
 }
-
+#else
+#ifndef IMAP_STORAGE
 /*!
- * \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 works with IMAP 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)
 {
-       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;
-}
-
-#endif
-#ifndef IMAP_STORAGE
-/*! 
- * \brief Copies a message from one mailbox to another.
- * \param chan
- * \param vmu
- * \param imbox
- * \param msgnum
- * \param duration
- * \param recip
- * \param fmt
- * \param dir
- *
- * 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)
-{
-       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)
-{
-       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;
-       }
-       return 0;
-}
-
-
-static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
+static int copy(char *infile, char *outfile)
 {
-       char tmp[256];
-       char *context;
-
-       /* If no mailbox, return immediately */
-       if (ast_strlen_zero(mailbox))
-               return 0;
-
-       if (newmsgs)
-               *newmsgs = 0;
-       if (oldmsgs)
-               *oldmsgs = 0;
-
-       if (strchr(mailbox, ',')) {
-               int tmpnew, tmpold;
-               char *mb, *cur;
+       int ifd;
+       int ofd;
+       int res;
+       int len;
+       char buf[4096];
 
-               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;
+#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;
        }
-
-       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);
-
-       return 0;
-}
-
 #endif
+}
 
-static void run_externnotify(char *context, char *extension)
+/*!
+ * \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 arguments[255];
-       char ext_context[256] = "";
-       int newvoicemails = 0, oldvoicemails = 0;
-       struct ast_smdi_mwi_message *mwi_msg;
-
-       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));
+       char frompath2[PATH_MAX], topath2[PATH_MAX];
+       struct ast_variable *tmp,*var = NULL;
+       const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
+       ast_filecopy(frompath, topath, NULL);
+       snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
+       snprintf(topath2, sizeof(topath2), "%s.txt", topath);
+       if (ast_check_realtime("voicemail_data")) {
+               var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
+               /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
+               for (tmp = var; tmp; tmp = tmp->next) {
+                       if (!strcasecmp(tmp->name, "origmailbox")) {
+                               origmailbox = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "context")) {
+                               context = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "macrocontext")) {
+                               macrocontext = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "exten")) {
+                               exten = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "priority")) {
+                               priority = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "callerchan")) {
+                               callerchan = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "callerid")) {
+                               callerid = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "origdate")) {
+                               origdate = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "origtime")) {
+                               origtime = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "category")) {
+                               category = tmp->value;
+                       } else if (!strcasecmp(tmp->name, "duration")) {
+                               duration = tmp->value;
+                       }
+               }
+               ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
+       }
+       copy(frompath2, topath2);
+       ast_variables_destroy(var);
+}
+#endif
 
-       if (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 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;
 
-               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);
-               }
+       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 (!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);
-               }
+/*!
+ * \brief utility used by inchar(), for base_encode()
+ */
+static int inbuf(struct baseio *bio, FILE *fi)
+{
+       int l;
+
+       if (bio->ateof)
+               return 0;
+
+       if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
+               if (ferror(fi))
+                       return -1;
+
+               bio->ateof = 1;
+               return 0;
        }
+
+       bio->iolen= l;
+       bio->iocp= 0;
+
+       return 1;
 }
 
 /*!
- * \brief Variables used for saving a voicemail.
- *
- * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
+ * \brief utility used by base_encode()
  */
-struct leave_vm_options {
-       unsigned int flags;
-       signed char record_gain;
-       char *exitcontext;
-};
+static int inchar(struct baseio *bio, FILE *fi)
+{
+       if (bio->iocp>=bio->iolen) {
+               if (!inbuf(bio, fi))
+                       return EOF;
+       }
+
+       return bio->iobuf[bio->iocp++];
+}
 
 /*!
- * \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.
+ * \brief utility used by base_encode()
  */
-static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
+static int ochar(struct baseio *bio, int c, 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*#";
+       if (bio->linelength >= BASELINELEN) {
+               if (fputs(eol,so) == EOF)
+                       return -1;
 
-       ast_copy_string(tmp, ext, sizeof(tmp));
-       ext = tmp;
-       if ((context = strchr(tmp, '@'))) {
-               *context++ = '\0';
-               tmpptr = strchr(context, '&');
-       } else {
-               tmpptr = strchr(ext, '&');
+               bio->linelength= 0;
        }
 
-       if (tmpptr)
-               *tmpptr++ = '\0';
+       if (putc(((unsigned char)c),so) == EOF)
+               return -1;
 
-       ast_channel_lock(chan);
-       if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
-               category = ast_strdupa(category);
-       }
-       ast_channel_unlock(chan);
+       bio->linelength++;
 
-       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));
+       return 1;
+}
 
-       /* 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);
+/*!
+ * \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 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;
+
+       memset(&bio, 0, sizeof(bio));
+       bio.iocp = BASEMAXINLINE;
+
+       if (!(fi = fopen(filename, "rb"))) {
+               ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
                return -1;
        }
 
-       RETRIEVE(tempfile, -1, ext, context);
-
-       if (ast_fileexists(tempfile, NULL, NULL) > 0)
-               ast_copy_string(prefile, tempfile, sizeof(prefile));
+       while (!hiteof){
+               unsigned char igroup[3], ogroup[4];
+               int c,n;
 
-       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");
+               igroup[0]= igroup[1]= igroup[2]= 0;
 
-       /* 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;
+               for (n= 0;n<3;n++) {
+                       if ((c = inchar(&bio, fi)) == EOF) {
+                               hiteof= 1;
+                               break;
                        }
-               } 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;
+
+                       igroup[n]= (unsigned char)c;
                }
-       }
 
-       if (!ast_strlen_zero(vmu->exit)) {
-               if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
-                       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
-       } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
-               strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
-       else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
-               strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
-               ausemacro = 1;
-       }
+               if (n> 0) {
+                       ogroup[0]= dtable[igroup[0]>>2];
+                       ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
+                       ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
+                       ogroup[3]= dtable[igroup[2]&0x3F];
 
-       if (ast_test_flag(options, OPT_DTMFEXIT)) {
-               for (code = alldtmf; *code; code++) {
-                       char e[2] = "";
-                       e[0] = *code;
-                       if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
-                               strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
-               }
-       }
+                       if (n<3) {
+                               ogroup[3]= '=';
 
-       /* 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;
+                               if (n<2)
+                                       ogroup[2]= '=';
+                       }
+
+                       for (i= 0;i<4;i++)
+                               ochar(&bio, ogroup[i], so);
                }
        }
-       if (res == '#') {
-               /* On a '#' we skip the instructions */
-               ast_set_flag(options, OPT_SILENT);
-               res = 0;
+
+       fclose(fi);
+       
+       if (fputs(eol,so)==EOF)
+               return 0;
+
+       return 1;
+}
+
+static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
+{
+       char callerid[256];
+       /* Prepare variables for substitution in email body and subject */
+       pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
+       pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
+       snprintf(passdata, passdatasize, "%d", msgnum);
+       pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
+       pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
+       pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
+       pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_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 (!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;
+       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)
+{
+       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 > 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));
+       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;
                }
-               chan->priority = 0;
-               free_user(vmu);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
-               return 0;
        }
+       return 0;
+}
 
-       /* 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");
+/*!\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);
                }
-               return 0;
        }
+       snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
+       return end;
+}
 
-       /* 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;
+/*!
+ * \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);
 
-       if (res < 0) {
-               free_user(vmu);
-               pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
-               return -1;
+       if (cidnum) {
+               strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
        }
-       /* 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;
+       if (cidname) {
+               strip_control(cidname, enc_cidname, sizeof(enc_cidname));
+       }
+       gethostname(host, sizeof(host) - 1);
 
-#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 (!(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;
+       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_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");
+       } else {
+               fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
+       }
 
-               /* 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;
+       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;
                }
-               
-               /* 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;
+               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_channel_free(ast);
+               } else {
+                       ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
                }
-
-#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;
+       } 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);
                }
-
-#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;
+       } 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);
                }
+       }
 
-               /* 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);
+       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());
 
-               /* 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 : ""); 
+               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, "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
-
-               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);
-                               }
-                       } 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);
-
-                                       /* 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
-
-                                       snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
-                                       ast_filerename(tmptxtfile, fn, NULL);
-                                       rename(tmptxtfile, txtfile);
-
-                                       /* 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));
-
-                                       ast_unlock_path(dir);
-                                       if (ast_check_realtime("voicemail_data")) {
-                                               snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
-                                               snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
-                                               ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
-                                       }
-                                       /* We must store the file first, before copying the message, because
-                                        * ODBC storage does the entire copy with SQL.
-                                        */
-                                       if (ast_fileexists(fn, NULL, NULL) > 0) {
-                                               STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, NULL);
-                                       }
+                       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);
+       }
 
-                                       /* 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 (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);
                }
-               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);
-       
-       return res;
+       }
 }
 
-#ifndef IMAP_STORAGE
-static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
+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)
 {
-       /* we know max messages, so stop process when number is hit */
-
-       int x, dest;
-       char sfn[PATH_MAX];
-       char dfn[PATH_MAX];
+       char tmpdir[256], newtmp[256];
+       char fname[256];
+       char tmpcmd[256];
+       int tmpfd = -1;
 
-       if (vm_lock_path(dir))
-               return ERROR_LOCK_PATH;
+       /* Eww. We want formats to tell us their own MIME type */
+       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
 
-       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);
+       if (vmu->volgain < -.001 || vmu->volgain > .001) {
+               create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
+               snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
+               tmpfd = mkstemp(newtmp);
+               chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
+               ast_debug(3, "newtmp: %s\n", newtmp);
+               if (tmpfd > -1) {
+                       int soxstatus;
+                       snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
+                       if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
+                               attach = newtmp;
+                               ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+                       } else {
+                               ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
+                                       soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
+                               ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
                        }
-                       
-                       dest++;
                }
        }
-       ast_unlock_path(dir);
-
-       return 0;
-}
-#endif
-
-static int say_and_wait(struct ast_channel *chan, int num, const char *language)
-{
-       int d;
-       d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
-       return d;
+       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
 
-static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
+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)
 {
-#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];
-
-       /* 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));
+       FILE *p=NULL;
+       char tmp[80] = "/tmp/astmail-XXXXXX";
+       char tmp2[256];
 
-       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 (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;
+}
 
-       if (vm_lock_path(ddir))
-               return ERROR_LOCK_PATH;
+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;
 
-       x = last_message_index(vmu, ddir) + 1;
+       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 (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 (*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 (x >= vmu->maxmsg) {
-                       ast_unlock_path(ddir);
-                       return ERROR_MAILBOX_FULL;
+               if (ast_strlen_zero(flag)) {
+                       fprintf(p, "Subject: New VM\n\n");
+               } else {
+                       fprintf(p, "Subject: New %s VM\n\n", flag);
                }
        }
-       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_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);
        }
-       ast_unlock_path(ddir);
-#endif
+       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;
 }
 
-static int adsi_logo(unsigned char *buf)
+/*!
+ * \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)
 {
-       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;
+       struct ast_tm tm;
+       struct timeval t = ast_tvnow();
+       
+       ast_localtime(&t, &tm, "UTC");
+
+       return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
 }
 
-static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
+static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
 {
-       unsigned char buf[256];
-       int bytes = 0;
-       int x;
-       char num[5];
-
-       *useadsi = 0;
-       bytes += ast_adsi_data_mode(buf + bytes);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+       int res;
+       char fn[PATH_MAX];
+       char dest[PATH_MAX];
 
-       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);
+       snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
 
-       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;
+       if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
+               ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
+               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);
-#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);
-
-#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);
+       RETRIEVE(fn, -1, ext, context);
+       if (ast_fileexists(fn, NULL, NULL) > 0) {
+               res = ast_stream_and_wait(chan, fn, ecodes);
+               if (res) {
+                       DISPOSE(fn, -1);
+                       return res;
+               }
+       } else {
+               /* Dispose just in case */
+               DISPOSE(fn, -1);
+               res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
+               if (res)
+                       return res;
+               res = ast_say_digit_str(chan, ext, ecodes, chan->language);
+               if (res)
+                       return res;
+       }
+       res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
+       return res;
+}
 
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
+static void free_zone(struct vm_zone *z)
+{
+       ast_free(z);
+}
 
-       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 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)
+{
+       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 };
 
-#ifdef DISPLAY
-       /* Add another dot */
-       bytes = 0;
-       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
-       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
+       if (newmsgs)
+               *newmsgs = 0;
+       if (oldmsgs)
+               *oldmsgs = 0;
+       if (urgentmsgs)
+               *urgentmsgs = 0;
 
-       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);
+       /* If no mailbox, return immediately */
+       if (ast_strlen_zero(mailbox))
                return 0;
-       }
-       bytes = 0;
-       bytes += ast_adsi_download_disconnect(buf + bytes);
-       bytes += ast_adsi_voice_mode(buf + bytes, 0);
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
-
-       ast_debug(1, "Done downloading scripts...\n");
-
-#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");
 
-       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!", "");
+       ast_copy_string(tmp, mailbox, sizeof(tmp));
+       
+       context = strchr(tmp, '@');
+       if (context) {
+               *context = '\0';
+               context++;
        } else
-               bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
+               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);
 
-       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-       return 0;
-}
+               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);
 
-static void adsi_begin(struct ast_channel *chan, int *useadsi)
-{
-       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;
+               if (!urgentmsgs) {
+                       x = 0;
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+
+               snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
+               stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+               if (!stmt) {
+                       ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+                       ast_odbc_release_obj(obj);
+                       goto yuck;
+               }
+               res = SQLFetch(stmt);
+               if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+                       ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+                       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
-               *useadsi = 1;
+               ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+               
+yuck:  
+       return x;
 }
 
-static void adsi_login(struct ast_channel *chan)
+static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 {
-       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;
-
-       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);
+       return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
 }
 
-static void adsi_password(struct ast_channel *chan)
+/*!
+ * \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;
-       unsigned char keys[8];
-       int x;
-       if (!ast_adsi_available(chan))
-               return;
+       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;
 
-       for (x = 0; x < 8; x++)
-               keys[x] = 0;
-       /* Set one key for next */
-       keys[3] = ADSI_KEY_APPS + 3;
+       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 += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-       bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
-       bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
-       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);
+yuck:
+       if (obj)
+               ast_odbc_release_obj(obj);
+       return nummsgs;
 }
 
-static void adsi_folders(struct ast_channel *chan, int start, char *label)
+/** 
+ * \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)
 {
-       unsigned char buf[256];
-       int bytes = 0;
-       unsigned char keys[8];
-       int x, y;
+       char tmp[256], *tmp2 = tmp, *box, *context;
+       ast_copy_string(tmp, mailbox, sizeof(tmp));
+       while ((context = box = strsep(&tmp2, ","))) {
+               strsep(&context, "@");
+               if (ast_strlen_zero(context))
+                       context = "default";
+               if (messagecount(context, box, folder))
+                       return 1;
+       }
+       return 0;
+}
+#endif
+#ifndef IMAP_STORAGE
+/*! 
+ * \brief Copies a message from one mailbox to another.
+ * \param chan
+ * \param vmu
+ * \param imbox
+ * \param msgnum
+ * \param duration
+ * \param recip
+ * \param fmt
+ * \param dir
+ *
+ * This is only used by file storage based mailboxes.
+ *
+ * \return zero on success, -1 on error.
+ */
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+{
+       char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
+       const char *frombox = mbox(imbox);
+       int recipmsgnum;
 
-       if (!ast_adsi_available(chan))
-               return;
+       ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
 
-       for (x = 0; x < 5; x++) {
-               y = ADSI_KEY_APPS + 12 + start + x;
-               if (y > ADSI_KEY_APPS + 12 + 4)
-                       y = 0;
-               keys[x] = ADSI_KEY_SKT | y;
+       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 {