[Freeswitch-svn] [commit] r5846 - in freeswitch/trunk: . build conf src src/include src/mod/applications/mod_dptools src/mod/applications/mod_voicemail

Freeswitch SVN anthm at freeswitch.org
Thu Oct 11 23:28:59 EDT 2007


Author: anthm
Date: Thu Oct 11 23:28:59 2007
New Revision: 5846

Added:
   freeswitch/trunk/conf/voicemail.conf.xml
   freeswitch/trunk/conf/voicemail_en_tts.xml
   freeswitch/trunk/src/mod/applications/mod_voicemail/
   freeswitch/trunk/src/mod/applications/mod_voicemail/Makefile
   freeswitch/trunk/src/mod/applications/mod_voicemail/mod_voicemail.c
   freeswitch/trunk/src/mod/applications/mod_voicemail/mod_voicemail.vcproj
Modified:
   freeswitch/trunk/Makefile.am
   freeswitch/trunk/build/modules.conf.in
   freeswitch/trunk/conf/default_context.xml
   freeswitch/trunk/conf/directory.xml
   freeswitch/trunk/conf/freeswitch.xml
   freeswitch/trunk/conf/modules.conf.xml
   freeswitch/trunk/src/include/switch_apr.h
   freeswitch/trunk/src/include/switch_types.h
   freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c
   freeswitch/trunk/src/switch_apr.c
   freeswitch/trunk/src/switch_core.c
   freeswitch/trunk/src/switch_core_file.c
   freeswitch/trunk/src/switch_xml.c

Log:
EXECUTE ORDER 66

Modified: freeswitch/trunk/Makefile.am
==============================================================================
--- freeswitch/trunk/Makefile.am	(original)
+++ freeswitch/trunk/Makefile.am	Thu Oct 11 23:28:59 2007
@@ -135,6 +135,7 @@
 endif
 
 if ADD_ODBC
+CORE_CFLAGS    += -DSWITCH_HAVE_ODBC
 libfreeswitch_la_SOURCES += src/switch_odbc.c
 libfreeswitch_la_LDFLAGS     += -lodbc
 endif

Modified: freeswitch/trunk/build/modules.conf.in
==============================================================================
--- freeswitch/trunk/build/modules.conf.in	(original)
+++ freeswitch/trunk/build/modules.conf.in	Thu Oct 11 23:28:59 2007
@@ -5,6 +5,7 @@
 applications/mod_dptools
 applications/mod_enum
 applications/mod_fifo
+#applications/mod_voicemail
 #applications/mod_ivrtest
 #applications/mod_soundtouch
 #applications/mod_rss

Modified: freeswitch/trunk/conf/default_context.xml
==============================================================================
--- freeswitch/trunk/conf/default_context.xml	(original)
+++ freeswitch/trunk/conf/default_context.xml	Thu Oct 11 23:28:59 2007
@@ -19,6 +19,21 @@
     </condition>
   </extension>
 
+  <extension name="7771">
+    <condition field="destination_number" expression="^7771$">
+      <action application="answer"/>
+      <action application="set" data="voicemail_authorized=${sip_authorized}"/>
+      <action application="voicemail" data="check demo $${domain} ${sip_from_user}"/>
+    </condition>
+  </extension>
+
+  <extension name="7772">
+    <condition field="destination_number" expression="^7772$">
+      <action application="answer"/>
+      <action application="voicemail" data="demo $${domain} ${sip_from_user}"/>
+    </condition>
+  </extension>
+
   <extension name="9193">
     <condition field="destination_number" expression="^9193$">
       <action application="set" data="bridge_pre_execute_bleg_app=soundtouch"/>

Modified: freeswitch/trunk/conf/directory.xml
==============================================================================
--- freeswitch/trunk/conf/directory.xml	(original)
+++ freeswitch/trunk/conf/directory.xml	Thu Oct 11 23:28:59 2007
@@ -1,7 +1,7 @@
 <!--the domain or ip (the right hand side of the @ in the addr-->
 <domain name="$${domain}">
   <!--the user id (the left hand side of the @ in the addr-->
-  <user id="stpeter">
+  <user id="stpeter" mailbox="1000"> <!--if id is numeric mailbox param is not necessary-->
     <!-- Outbound Registrations Related to this user -->
     <gateways>
       <!--<gateway name="asterlink.com">-->
@@ -34,6 +34,8 @@
     <params>
       <!-- omit password for authless registration -->
       <param name="password" value="mypass"/>
+      <param name="vm-password" value="1234"/><!--if vm-password is omitted password param is used-->
+      <!--<param name="vm-mailto" value="me at mydomain.com"/>-->
       <!-- optionally use this instead if you want to store the hash of user:domain:pass-->
       <!--<param name="a1-hash" value="c6440e5de50b403206989679159de89a"/>-->
     </params>

Modified: freeswitch/trunk/conf/freeswitch.xml
==============================================================================
--- freeswitch/trunk/conf/freeswitch.xml	(original)
+++ freeswitch/trunk/conf/freeswitch.xml	Thu Oct 11 23:28:59 2007
@@ -102,6 +102,7 @@
     <!--#include "conference.conf.xml"-->
     <!-- ivr.conf is used by mod_dptools -->
     <!--#include "ivr.conf.xml"-->
+    <!--#include "voicemail.conf.xml"-->
 
     <!-- Dialplan Interfaces -->
     <!--#include "dialplan_directory.conf.xml"-->
@@ -145,6 +146,8 @@
     <macros>
       <language name="en" sound_path="/snds" tts_engine="cepstral" tts_voice="david">
 	<!--#include "lang_en.xml"-->
+	<!--voicemail_en_tts is purely implemented with tts, we need a files based implementation too -->
+	<!--#include "voicemail_en_tts.xml"-->
       </language>
       <language name="fr" sound_path="/var/sounds/lang/fr/jean" tts_engine="cepstral" tts_voice="jean-pierre">
 	<!--#include "lang_fr.xml"-->

Modified: freeswitch/trunk/conf/modules.conf.xml
==============================================================================
--- freeswitch/trunk/conf/modules.conf.xml	(original)
+++ freeswitch/trunk/conf/modules.conf.xml	Thu Oct 11 23:28:59 2007
@@ -37,6 +37,7 @@
     <load module="mod_conference"/>
     <load module="mod_dptools"/>
     <load module="mod_fifo"/>
+    <!--<load module="mod_voicemail"/>-->
 
     <!-- Dialplan Interfaces -->
     <!-- <load module="mod_dialplan_directory"/> -->

Added: freeswitch/trunk/conf/voicemail.conf.xml
==============================================================================
--- (empty file)
+++ freeswitch/trunk/conf/voicemail.conf.xml	Thu Oct 11 23:28:59 2007
@@ -0,0 +1,30 @@
+<configuration name="voicemail.conf" description="Voicemail">
+  <settings>
+  </settings>
+  <profiles>
+    <profile name="demo">
+      <param name="terminator-key" value="#"/>
+      <param name="max-login-attempts" value="3"/>
+      <param name="digit-timeout" value="10000"/>
+      <param name="max-record-len" value="300"/>
+      <param name="tone-spec" value="%(1000, 0, 640)"/>
+      <param name="terminator-key" value="#"/>
+      <param name="play-new-messages-key" value="1"/>
+      <param name="play-saved-messages-key" value="2"/>
+      <param name="save-message-key" value="8"/>
+      <param name="delete-message-key" value="7"/>
+      <param name="replay-message-key" value="1"/>
+      <param name="main-menu-key" value="0"/>
+      <param name="config-menu-key" value="5"/>
+      <param name="record-greeting-key" value="1"/>
+      <param name="choose-greeting-key" value="2"/>
+      <param name="record-name-key" value="3"/>
+      <param name="record-file-key" value="3"/>
+      <param name="listen-file-key" value="1"/>
+      <param name="save-file-key" value="2"/>
+      <param name="delete-file-key" value="7"/>
+      <param name="undelete-file-key" value="8"/>
+      <!--<param name="odbc-dsn" value="dsn:user:pass"/>-->
+    </profile>
+  </profiles>
+</configuration> 

Added: freeswitch/trunk/conf/voicemail_en_tts.xml
==============================================================================
--- (empty file)
+++ freeswitch/trunk/conf/voicemail_en_tts.xml	Thu Oct 11 23:28:59 2007
@@ -0,0 +1,209 @@
+<include><!--This line will be ignored it's here to validate the xml and is optional -->
+  <macro name="voicemail_enter_id">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="please enter your i d, followed by $1"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_enter_pass">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="please enter your password, followed by $1"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_fail_auth">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="login incorrect"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_hello">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="welcome to your voicemail"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_goodbye">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="goodbye"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_abort">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="too many failed attempts"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_new_message_count">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="you have $1 new messages in folder ${voicemail_current_folder}"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_saved_message_count">
+    <input pattern="(.*)">
+      <match>
+	<action function="speak-text" data="you have $1 saved messages in folder ${voicemail_current_folder}"/>
+      </match>
+    </input>
+  </macro>
+
+
+  <macro name="voicemail_menu">
+    <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+      <match>
+	<action function="speak-text" data="press $1 to listen to new messages. press $2 to listen to saved messages. press $3 for advanced options"/>
+      </match>
+    </input>
+  </macro>
+
+
+  <macro name="voicemail_config_menu">
+    <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+      <match>
+	<action function="speak-text" 
+		data="press $1 to record a greeting, press $2 to choose a greeting, press $3 to record your name, press $4 for the main menu"/>
+      </match>
+    </input>
+  </macro>
+
+
+  <macro name="voicemail_record_name">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="at the tone, record your name, press any key or stop talking to end the recording."/>
+
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_record_file_check">
+    <input pattern="^([0-9#*]):([0-9#*]):([0-9#*])$">
+      <match>
+	<action function="speak-text" 
+		data="press $1 to listen to the recording, press $2 to save the recording, press $3 to re record"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_listen_file_check">
+    <input pattern="^([0-9#*]):([0-9#*]):([0-9#*]):([0-9#*])$">
+      <match>
+	<action function="speak-text" 
+		data="press $1 to listen to the recording again, press $2 to save the recording, press $3 to delete the recording"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_choose_greeting">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="choose a greeting between 1 and 3"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_choose_greeting_fail">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="invalid value"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_record_greeting">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="record your greeting at the tone, press any key or stop talking to end the recording"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_record_message">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="record your message at the tone, press any key or stop talking to end the recording"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_greeting_selected">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="greeting $1 selected"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_play_greeting">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="$1 is not available"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_say_number">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="$1"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_say_message_number">
+    <input pattern="^([a-z]+):(.*)$">
+      <match>
+	<action function="speak-text" data="$1 message number $2"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_say_phone_number">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="$1"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_say_name">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="$1"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_ack">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="message $1"/>
+      </match>
+    </input>
+  </macro>
+
+  <macro name="voicemail_say_date">
+    <input pattern="^(.*)$">
+      <match>
+	<action function="speak-text" data="$strftime($1|%A, %B %d %Y, %I %m %p)"/>
+      </match>
+    </input>
+  </macro>
+
+</include><!--This line will be ignored it's here to validate the xml and is optional -->

Modified: freeswitch/trunk/src/include/switch_apr.h
==============================================================================
--- freeswitch/trunk/src/include/switch_apr.h	(original)
+++ freeswitch/trunk/src/include/switch_apr.h	Thu Oct 11 23:28:59 2007
@@ -748,6 +748,11 @@
 
 SWITCH_DECLARE(switch_status_t) switch_file_exists(const char *filename, switch_memory_pool_t *pool);
 
+SWITCH_DECLARE(switch_status_t) switch_dir_make(const char *path, switch_fileperms_t perm,
+												switch_memory_pool_t *pool);
+SWITCH_DECLARE(switch_status_t) switch_dir_make_recursive(const char *path, switch_fileperms_t perm,
+														  switch_memory_pool_t *pool);
+
 typedef struct switch_dir switch_dir_t;
 
 struct switch_array_header_t {

Modified: freeswitch/trunk/src/include/switch_types.h
==============================================================================
--- freeswitch/trunk/src/include/switch_types.h	(original)
+++ freeswitch/trunk/src/include/switch_types.h	Thu Oct 11 23:28:59 2007
@@ -253,6 +253,7 @@
 	char *temp_dir;
 	char *htdocs_dir;
 	char *grammar_dir;
+	char *storage_dir;
 };
 
 typedef struct switch_directories switch_directories;

Modified: freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c
==============================================================================
--- freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c	(original)
+++ freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c	Thu Oct 11 23:28:59 2007
@@ -675,9 +675,16 @@
 	switch_size_t retsize;
 	switch_time_exp_t tm;
 	char date[80] = "";
-
-	switch_time_exp_lt(&tm, switch_time_now());
-	switch_strftime(date, &retsize, sizeof(date), cmd ? cmd : "%Y-%m-%d %T", &tm);
+	switch_time_t thetime;
+	char *p;
+	if (!switch_strlen_zero(cmd) && (p = strchr(cmd, '|'))) {
+		thetime = switch_time_make(atoi(cmd),0);
+		cmd = p+1;
+	} else {
+		thetime = switch_time_now();
+	}
+	switch_time_exp_lt(&tm, thetime);
+	switch_strftime(date, &retsize, sizeof(date), switch_strlen_zero(cmd) ? "%Y-%m-%d %T" : cmd, &tm);
 	stream->write_function(stream, "%s", date);
 
 	return SWITCH_STATUS_SUCCESS;

Added: freeswitch/trunk/src/mod/applications/mod_voicemail/Makefile
==============================================================================
--- (empty file)
+++ freeswitch/trunk/src/mod/applications/mod_voicemail/Makefile	Thu Oct 11 23:28:59 2007
@@ -0,0 +1,2 @@
+BASE=../../../..
+include /usr/src/freeswitch.trunk/build/modmake.rules

Added: freeswitch/trunk/src/mod/applications/mod_voicemail/mod_voicemail.c
==============================================================================
--- (empty file)
+++ freeswitch/trunk/src/mod/applications/mod_voicemail/mod_voicemail.c	Thu Oct 11 23:28:59 2007
@@ -0,0 +1,1633 @@
+/* 
+ * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ * Copyright (C) 2005/2006, Anthony Minessale II <anthmct at yahoo.com>
+ *
+ * Version: MPL 1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
+ *
+ * The Initial Developer of the Original Code is
+ * Anthony Minessale II <anthmct at yahoo.com>
+ * Portions created by the Initial Developer are Copyright (C)
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * 
+ * Anthony Minessale II <anthmct at yahoo.com>
+ *
+ *
+ * mod_voicemail.c -- Voicemail Module
+ *
+ */
+#include <switch.h>
+
+#ifdef SWITCH_HAVE_ODBC
+#include <switch_odbc.h>
+#endif
+
+
+#define TRY_CODE(code) do {status = code; if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { goto end; } break;} while(status)
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_voicemail_load);
+SWITCH_MODULE_DEFINITION(mod_voicemail, mod_voicemail_load, NULL, NULL);
+
+static struct {
+	switch_hash_t *profile_hash;
+    int debug;
+    switch_memory_pool_t *pool;
+} globals;
+
+struct vm_profile {
+    char *name;
+    char *dbname;
+	char *odbc_dsn;
+	char *odbc_user;
+	char *odbc_pass;
+    char terminator_key[2];
+    char play_new_messages_key[2];
+    char play_saved_messages_key[2];
+    char save_message_key[2];
+    char delete_message_key[2];
+    char replay_message_key[2];
+
+    char main_menu_key[2];    
+    char config_menu_key[2];
+    char record_greeting_key[2];
+    char choose_greeting_key[2];
+    char record_name_key[2];
+    
+    char record_file_key[2];
+    char listen_file_key[2];
+    char save_file_key[2];
+    char delete_file_key[2];
+    char undelete_file_key[2];
+    char *tone_spec;
+    uint32_t digit_timeout;
+    uint32_t max_login_attempts;
+    uint32_t max_record_len;
+    switch_mutex_t *mutex;
+#ifdef SWITCH_HAVE_ODBC
+	switch_odbc_handle_t *master_odbc;
+#else
+	void *filler1;
+#endif
+};
+typedef struct vm_profile vm_profile_t;
+
+
+#define B64BUFFLEN 1024
+static const char c64[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static int write_buf(int fd, char *buf)
+{
+
+	int len = (int) strlen(buf);
+	if (fd && write(fd, buf, len) != len) {
+		close(fd);
+		return 0;
+	}
+
+	return 1;
+}
+static switch_bool_t vm_email(char *to, char *from, char *headers, char *body, char *file)
+{
+	char *bound = "XXXX_boundary_XXXX";
+	char filename[80], buf[B64BUFFLEN];
+	int fd = 0, ifd = 0;
+	int x = 0, y = 0, bytes = 0, ilen = 0;
+	unsigned int b = 0, l = 0;
+	unsigned char in[B64BUFFLEN];
+	unsigned char out[B64BUFFLEN + 512];
+	char *path = NULL;
+
+    snprintf(filename, 80, "%smail.%ld%04x", SWITCH_GLOBAL_dirs.temp_dir, time(NULL), rand() & 0xffff);
+    
+    if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644))) {
+        if (file) {
+            path = file;
+            if ((ifd = open(path, O_RDONLY)) < 1) {
+                return SWITCH_FALSE;
+            }
+
+            snprintf(buf, B64BUFFLEN, "MIME-Version: 1.0\nContent-Type: multipart/mixed; boundary=\"%s\"\n", bound);
+            if (!write_buf(fd, buf)) {
+                return SWITCH_FALSE;
+            }
+        }
+
+        if (headers && !write_buf(fd, headers))
+            return SWITCH_FALSE;
+
+        if (!write_buf(fd, "\n\n"))
+            return SWITCH_FALSE;
+
+        if (file) {
+            snprintf(buf, B64BUFFLEN, "--%s\nContent-Type: text/plain\n\n", bound);
+            if (!write_buf(fd, buf))
+                return SWITCH_FALSE;
+        }
+
+        if (body) {
+            if (!write_buf(fd, body)) {
+                return SWITCH_FALSE;
+            }
+        }
+
+        if (file) {
+            snprintf(buf, B64BUFFLEN, "\n\n--%s\nContent-Type: application/octet-stream\n"
+                     "Content-Transfer-Encoding: base64\n"
+                     "Content-Description: Sound attachment.\n" "Content-Disposition: attachment; filename=\"%s\"\n\n", bound,  switch_cut_path(file));
+            if (!write_buf(fd, buf))
+                return SWITCH_FALSE;
+
+            while ((ilen = read(ifd, in, B64BUFFLEN))) {
+                for (x = 0; x < ilen; x++) {
+                    b = (b << 8) + in[x];
+                    l += 8;
+                    while (l >= 6) {
+                        out[bytes++] = c64[(b >> (l -= 6)) % 64];
+                        if (++y != 72)
+                            continue;
+                        out[bytes++] = '\n';
+                        y = 0;
+                    }
+                }
+                if (write(fd, &out, bytes) != bytes) {
+                    return -1;
+                } else
+                    bytes = 0;
+
+            }
+
+            if (l > 0) {
+                out[bytes++] = c64[((b % 16) << (6 - l)) % 64];
+            }
+            if (l != 0)
+                while (l < 6) {
+                    out[bytes++] = '=', l += 2;
+                }
+            if (write(fd, &out, bytes) != bytes) {
+                return -1;
+            }
+
+        }
+
+
+
+        if (file) {
+            snprintf(buf, B64BUFFLEN, "\n\n--%s--\n.\n", bound);
+            if (!write_buf(fd, buf))
+                return SWITCH_FALSE;
+        }
+    }
+
+    if (fd) {
+        close(fd);
+    }
+    if (ifd) {
+        close(ifd);
+    }
+    snprintf(buf, B64BUFFLEN, "/bin/cat %s | /usr/sbin/sendmail -tvf \"%s\" %s", filename, from, to);
+    if(system(buf)) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to execute command: %s\n", buf);
+    }
+
+    unlink(filename);
+
+
+    if (file) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Emailed file [%s] to [%s]\n", filename, to);
+    } else {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Emailed data to [%s]\n", to);
+    }
+
+    return SWITCH_TRUE;
+}
+
+static switch_status_t vm_execute_sql(vm_profile_t *profile, char *sql, switch_mutex_t *mutex)
+{
+	switch_core_db_t *db;
+    switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+	if (mutex) {
+		switch_mutex_lock(mutex);
+	}
+
+#ifdef SWITCH_HAVE_ODBC
+    if (profile->odbc_dsn) {
+		SQLHSTMT stmt;
+		if (switch_odbc_handle_exec(profile->master_odbc, sql, &stmt) != SWITCH_ODBC_SUCCESS) {
+			char *err_str;
+			err_str = switch_odbc_handle_get_error(profile->master_odbc, stmt);
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ERR: [%s]\n[%s]\n", sql, switch_str_nil(err_str));
+			switch_safe_free(err_str);
+            status = SWITCH_STATUS_FALSE;
+		}
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	} else {
+#endif
+		if (!(db = switch_core_db_open_file(profile->dbname))) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname);
+            status = SWITCH_STATUS_FALSE;
+			goto end;
+		}
+
+		status = switch_core_db_persistant_execute(db, sql, 25);
+		switch_core_db_close(db);
+
+#ifdef SWITCH_HAVE_ODBC
+    }
+#endif
+
+
+ end:
+	if (mutex) {
+		switch_mutex_unlock(mutex);
+	}
+
+    return status;
+}
+
+
+static switch_bool_t vm_execute_sql_callback(vm_profile_t *profile,
+											  switch_mutex_t *mutex,
+											  char *sql,
+											  switch_core_db_callback_func_t callback,
+											  void *pdata)
+{
+	switch_bool_t ret = SWITCH_FALSE;
+	switch_core_db_t *db;
+	char *errmsg = NULL;
+	
+	if (mutex) {
+        switch_mutex_lock(mutex);
+    }
+
+
+#ifdef SWITCH_HAVE_ODBC
+    if (profile->odbc_dsn) {
+		switch_odbc_handle_callback_exec(profile->master_odbc, sql, callback, pdata);
+	} else {
+#endif
+
+
+
+		if (!(db = switch_core_db_open_file(profile->dbname))) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Opening DB %s\n", profile->dbname);
+			goto end;
+		}
+
+	
+		switch_core_db_exec(db, sql, callback, pdata, &errmsg);
+
+		if (errmsg) {
+			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "SQL ERR: [%s] %s\n", sql, errmsg);
+			free(errmsg);
+		}
+
+		if (db) {
+			switch_core_db_close(db);
+		}
+
+#ifdef SWITCH_HAVE_ODBC
+    }
+#endif
+
+
+ end:
+
+	if (mutex) {
+        switch_mutex_unlock(mutex);
+    }
+	
+
+
+	return ret;
+
+}
+
+
+static char vm_sql[] =
+	"CREATE TABLE voicemail_data (\n"
+	"   created_epoch INTEGER(8),\n"
+	"   read_epoch    INTEGER(8),\n" 
+	"   user          VARCHAR(255),\n" 
+	"   domain        VARCHAR(255),\n" 
+	"   uuid          VARCHAR(255),\n" 
+	"   cid_name      VARCHAR(255),\n" 
+	"   cid_number    VARCHAR(255),\n" 
+	"   in_folder     VARCHAR(255),\n" 
+	"   file_path     VARCHAR(255),\n" 
+	"   flags         VARCHAR(255)\n" 
+	");\n";
+
+
+static char vm_pref_sql[] =
+	"CREATE TABLE voicemail_prefs (\n"
+	"   user            VARCHAR(255),\n" 
+	"   domain          VARCHAR(255),\n" 
+	"   name_path       VARCHAR(255),\n" 
+	"   greeting_path VARCHAR(255)\n" 
+	");\n";
+
+
+
+
+static switch_status_t load_config(void)
+{
+	char *cf = "voicemail.conf";
+	vm_profile_t *profile = NULL;
+	switch_xml_t cfg, xml, settings, param, x_profile, x_profiles;
+
+	memset(&globals, 0, sizeof(globals));
+    switch_core_new_memory_pool(&globals.pool);
+	switch_core_hash_init(&globals.profile_hash, globals.pool);
+
+	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf);
+		return SWITCH_STATUS_TERM;
+	}
+    
+	if ((settings = switch_xml_child(cfg, "settings"))) {
+		for (param = switch_xml_child(settings, "param"); param; param = param->next) {
+			char *var = (char *) switch_xml_attr_soft(param, "name");
+			char *val = (char *) switch_xml_attr_soft(param, "value");
+            
+			if (!strcasecmp(var, "debug")) {
+				globals.debug = atoi(val);
+			}
+		}
+	}
+
+	if (!(x_profiles = switch_xml_child(cfg, "profiles"))) {
+        goto end;
+    }
+
+    for (x_profile = switch_xml_child(x_profiles, "profile"); x_profile; x_profile = x_profile->next) {
+        char *name = (char *) switch_xml_attr_soft(x_profile, "name");
+        char *odbc_dsn = NULL, *odbc_dsn_tmp = NULL, *odbc_user = NULL, *odbc_pass = NULL, *dbname = NULL;
+        char *terminator_key = "#";
+        char *play_new_messages_key = "1";
+        char *play_saved_messages_key = "2";
+        char *save_message_key = "8";
+        char *delete_message_key = "7";
+        char *replay_message_key = "1";
+        
+        char *main_menu_key = "0";    
+        char *config_menu_key = "5";
+        char *record_greeting_key = "1";
+        char *choose_greeting_key = "2";
+        char *record_name_key = "3";
+        
+        char *record_file_key = "3";
+        char *listen_file_key = "1";
+        char *save_file_key = "2";
+        char *delete_file_key = "7";
+        char *undelete_file_key = "8";
+        char *tone_spec = "%(1000, 0, 640)";
+
+        switch_core_db_t *db;
+        uint32_t timeout = 10000, max_login_attempts = 3, max_record_len = 300;
+        
+        db = NULL;
+        for (param = switch_xml_child(x_profile, "param"); param; param = param->next) {
+            char *var, *val;
+
+            var = (char *) switch_xml_attr_soft(param, "name");
+            val = (char *) switch_xml_attr_soft(param, "value");
+
+            if (!strcasecmp(var, "terminator-key") && !switch_strlen_zero(val)) {
+                terminator_key = val;
+            } else if (!strcasecmp(var, "play-new-messages-key") && !switch_strlen_zero(val)) {
+                play_new_messages_key = val;
+            } else if (!strcasecmp(var, "play-saved-messages-key") && !switch_strlen_zero(val)) {
+                play_saved_messages_key = val;
+            } else if (!strcasecmp(var, "save-message-key") && !switch_strlen_zero(val)) {
+                save_message_key = val;
+            } else if (!strcasecmp(var, "delete-message-key") && !switch_strlen_zero(val)) {
+                delete_message_key = val;
+            } else if (!strcasecmp(var, "replay-message-key") && !switch_strlen_zero(val)) {
+                replay_message_key = val;
+            } else if (!strcasecmp(var, "main-menu-key") && !switch_strlen_zero(val)) {
+                main_menu_key = val;
+            } else if (!strcasecmp(var, "config-menu-key") && !switch_strlen_zero(val)) {
+                config_menu_key = val;
+            } else if (!strcasecmp(var, "record-greeting-key") && !switch_strlen_zero(val)) {
+                record_greeting_key = val;
+            } else if (!strcasecmp(var, "choose-greeting-key") && !switch_strlen_zero(val)) {
+                choose_greeting_key = val;
+            } else if (!strcasecmp(var, "record-name-key") && !switch_strlen_zero(val)) {
+                record_name_key = val;
+            } else if (!strcasecmp(var, "listen-file-key") && !switch_strlen_zero(val)) {
+                listen_file_key = val;
+            } else if (!strcasecmp(var, "save-file-key") && !switch_strlen_zero(val)) {
+                save_file_key = val;
+            } else if (!strcasecmp(var, "delete-file-key") && !switch_strlen_zero(val)) {
+                delete_file_key = val;
+            } else if (!strcasecmp(var, "undelete-file-key") && !switch_strlen_zero(val)) {
+                undelete_file_key = val;
+            } else if (!strcasecmp(var, "tone-spec")) {
+                tone_spec = val;
+            } else if (!strcasecmp(var, "digit-timeout")) {
+                int tmp = 0;
+                if (!switch_strlen_zero(val)) {
+                    tmp = atoi(val);
+                }
+                if (tmp >= 1000 && tmp <= 30000) {
+                    timeout = tmp;
+                } else {
+                    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "invalid timeout value [%s] must be between 1000 and 30000 ms\n", val);
+                }
+            } else if (!strcasecmp(var, "max-login-attempts")) {
+                int tmp = 0;
+                if (!switch_strlen_zero(val)) {
+                    tmp = atoi(val);
+                }
+                if (tmp > 0 && tmp < 11) {
+                    max_login_attempts = tmp;
+                } else {
+                    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "invalid attempts [%s] must be between 1 and 10 ms\n", val);
+                }
+            } else if (!strcasecmp(var, "max-record-len")) {
+                int tmp = 0;
+                if (!switch_strlen_zero(val)) {
+                    tmp = atoi(val);
+                }
+                if (tmp > 0 && tmp < 10000) {
+                    max_record_len = tmp;
+                } else {
+                    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "invalid attempts [%s] must be between 1 and 10000s\n", val);
+                }
+            } else if (!strcasecmp(var, "odbc-dsn")) {
+#ifdef SWITCH_HAVE_ODBC
+                odbc_dsn_tmp = val;
+                odbc_dsn = strdup(val);
+
+                if ((odbc_user = strchr(odbc_dsn, ':'))) {
+                    *odbc_user++ = '\0';
+                }
+                
+                if ((odbc_pass = strchr(odbc_user, ':'))) {
+                    *odbc_pass++ = '\0';
+                }
+#else
+                switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "ODBC IS NOT AVAILABLE!\n");
+#endif
+            }
+        }
+        
+        if (switch_strlen_zero(name)) {
+            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No name specified.\n");
+        } else {
+            profile = switch_core_alloc(globals.pool, sizeof(*profile));
+            profile->name = switch_core_strdup(globals.pool, name);
+
+            if (!switch_strlen_zero(odbc_dsn) && !switch_strlen_zero(odbc_user) && !switch_strlen_zero(odbc_pass)) {
+                profile->odbc_dsn = switch_core_strdup(globals.pool, odbc_dsn_tmp);
+                profile->odbc_user = switch_core_strdup(globals.pool, odbc_user);
+                profile->odbc_pass = switch_core_strdup(globals.pool, odbc_pass);
+            } else {
+                profile->dbname = switch_core_sprintf(globals.pool, "voicemail_%s", name);
+            }
+#ifdef SWITCH_HAVE_ODBC
+			if (profile->odbc_dsn) {
+				if (!(profile->master_odbc = switch_odbc_handle_new(profile->odbc_dsn, profile->odbc_user, profile->odbc_pass))) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open ODBC Database!\n");
+					continue;
+					
+				}
+				if (switch_odbc_handle_connect(profile->master_odbc) != SWITCH_ODBC_SUCCESS) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open ODBC Database!\n");
+					continue;
+				}
+
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connected ODBC DSN: %s\n", profile->odbc_dsn);
+				switch_odbc_handle_exec(profile->master_odbc, vm_sql, NULL);
+				switch_odbc_handle_exec(profile->master_odbc, vm_pref_sql, NULL);
+			} else {
+#endif
+				if ((db = switch_core_db_open_file(profile->dbname))) {
+					switch_core_db_test_reactive(db, "select * from voicemail_data", vm_sql);
+					switch_core_db_test_reactive(db, "select * from voicemail_prefs", vm_pref_sql);
+				} else {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Cannot Open SQL Database!\n");
+					continue;
+				}
+				switch_core_db_close(db);
+#ifdef SWITCH_HAVE_ODBC
+			}
+#endif
+            profile->digit_timeout = timeout;
+            profile->max_login_attempts = max_login_attempts;
+            profile->max_record_len = max_record_len;
+            *profile->terminator_key = *terminator_key;
+            *profile->play_new_messages_key = *play_new_messages_key;
+            *profile->play_saved_messages_key = *play_saved_messages_key;
+            *profile->save_message_key = *save_message_key;
+            *profile->delete_message_key = *delete_message_key;
+            *profile->replay_message_key = *replay_message_key;
+            *profile->main_menu_key = *main_menu_key;
+            *profile->config_menu_key = *config_menu_key;
+            *profile->record_greeting_key = *record_greeting_key;
+            *profile->choose_greeting_key = *choose_greeting_key;
+            *profile->record_name_key = *record_name_key;
+            *profile->record_file_key = *record_file_key;
+            *profile->listen_file_key = *listen_file_key;
+            *profile->save_file_key = *save_file_key;
+            *profile->delete_file_key = *delete_file_key;
+            *profile->undelete_file_key = *undelete_file_key;
+            profile->tone_spec = switch_core_strdup(globals.pool, tone_spec);
+            
+            switch_mutex_init(&profile->mutex, SWITCH_MUTEX_NESTED, globals.pool);
+            
+            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Added Profile %s\n", profile->name);
+            switch_core_hash_insert(globals.profile_hash, profile->name, profile);
+            
+        }
+        
+
+
+        switch_safe_free(odbc_dsn);
+        switch_safe_free(dbname);
+        
+    }
+        
+ end:
+
+	switch_xml_free(xml);
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t cancel_on_dtmf(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
+{
+
+
+	switch (itype) {
+	case SWITCH_INPUT_TYPE_DTMF:
+        {
+            char *dtmf = (char *) input;
+            if (buf && buflen) {
+                switch_copy_string(buf, dtmf, buflen);
+            }
+            return SWITCH_STATUS_BREAK;
+        }
+		break;
+	default:
+		break;
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+
+static switch_status_t control_playback(switch_core_session_t *session, void *input, switch_input_type_t itype, void *buf, unsigned int buflen)
+{
+
+
+	switch (itype) {
+	case SWITCH_INPUT_TYPE_DTMF:
+        {
+            char *dtmf = (char *) input;
+            switch_file_handle_t *fh = (switch_file_handle_t *) buf;
+            uint32_t pos = 0;
+            
+            switch(*dtmf) {
+            case '0':
+                {
+                    if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) {
+                        switch_clear_flag(fh, SWITCH_FILE_PAUSE);
+                    } else {
+                        switch_set_flag(fh, SWITCH_FILE_PAUSE);
+                    }
+                    return SWITCH_STATUS_SUCCESS;
+                }
+                break;
+            case '1':
+                {
+                    unsigned int pos = 0;
+                    fh->speed = 0;
+                    switch_core_file_seek(fh, &pos, 0, SEEK_SET);
+                    return SWITCH_STATUS_SUCCESS;
+                }
+                break;
+            case '6':
+                {
+                    int samps = 24000;
+                    switch_core_file_seek(fh, &pos, samps, SEEK_CUR);
+                    return SWITCH_STATUS_SUCCESS;
+                }
+                break;
+            case '4':
+                {
+                    int samps = 24000;
+                    switch_core_file_seek(fh, &pos, fh->pos - samps, SEEK_SET);
+                    return SWITCH_STATUS_SUCCESS;
+                }
+                break;
+            case '#':
+                return SWITCH_STATUS_BREAK;
+            
+            default:
+                return SWITCH_STATUS_SUCCESS;
+            }
+        }
+		break;
+	default:
+		break;
+	}
+
+	return SWITCH_STATUS_SUCCESS;
+}
+
+struct prefs_callback {
+    char name_path[255];
+    char greeting_path[255];
+};
+typedef struct prefs_callback prefs_callback_t;
+
+static int prefs_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+	prefs_callback_t *cbt = (prefs_callback_t *) pArg;
+
+	switch_copy_string(cbt->name_path, argv[2], sizeof(cbt->name_path));
+	switch_copy_string(cbt->greeting_path, argv[3], sizeof(cbt->greeting_path));
+    
+	return 0;
+}
+
+
+typedef enum {
+    VM_CHECK_START,
+    VM_CHECK_AUTH,
+    VM_CHECK_MENU,
+    VM_CHECK_CONFIG,
+    VM_CHECK_PLAY_MESSAGES,
+    VM_CHECK_FOLDER_SUMMARY,
+    VM_CHECK_LISTEN
+} vm_check_state_t;
+
+
+#define VM_ACK_MACRO "voicemail_ack"
+#define VM_SAY_DATE_MACRO "voicemail_say_date"
+#define VM_PLAY_GREETING_MACRO "voicemail_play_greeting"
+#define VM_SAY_MESSAGE_NUMBER_MACRO "voicemail_say_message_number"
+#define VM_SAY_NUMBER_MACRO "voicemail_say_number"
+#define VM_SAY_PHONE_NUMBER_MACRO "voicemail_say_phone_number"
+#define VM_SAY_NAME_MACRO "voicemail_say_name"
+
+#define VM_RECORD_MESSAGE_MACRO "voicemail_record_message"
+#define VM_CHOOSE_GREETING_MACRO "voicemail_choose_greeting"
+#define VM_CHOOSE_GREETING_FAIL_MACRO "voicemail_choose_greeting_fail"
+#define VM_CHOOSE_GREETING_SELECTED_MACRO "voicemail_greeting_selected"
+#define VM_RECORD_GREETING_MACRO "voicemail_record_greeting"
+#define VM_RECORD_NAME_MACRO "voicemail_record_name"
+#define VM_LISTEN_FILE_CHECK_MACRO "voicemail_listen_file_check"
+#define VM_RECORD_FILE_CHECK_MACRO "voicemail_record_file_check"
+#define VM_MENU_MACRO "voicemail_menu"
+#define VM_CONFIG_MENU_MACRO "voicemail_config_menu"
+#define VM_ENTER_ID_MACRO "voicemail_enter_id"
+#define VM_ENTER_PASS_MACRO "voicemail_enter_pass"
+#define VM_FAIL_AUTH_MACRO "voicemail_fail_auth"
+#define VM_ABORT_MACRO "voicemail_abort"
+#define VM_HELLO_MACRO "voicemail_hello"
+#define VM_GOODBYE_MACRO "voicemail_goodbye"
+#define VM_NEW_MESSAGE_COUNT_MACRO "voicemail_new_message_count"
+#define VM_SAVED_MESSAGE_COUNT_MACRO "voicemail_saved_message_count"
+
+
+static switch_status_t vm_macro_get(switch_core_session_t *session,
+                                    char *macro,
+                                    char *macro_arg,
+                                    char *buf,
+                                    switch_size_t buflen,
+                                    switch_size_t maxlen,
+                                    char *term_chars,
+                                    char *terminator_key,
+                                    uint32_t timeout)
+{
+    switch_input_args_t args = { 0 }, *ap = NULL;
+    switch_channel_t *channel;
+    switch_status_t status = SWITCH_STATUS_SUCCESS;
+    switch_size_t bslen;
+
+    channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+    
+    if (buf && buflen) {
+        *buf = '\0';
+        args.input_callback = cancel_on_dtmf;
+        args.buf = buf;
+        args.buflen = sizeof(buf);
+        ap = &args;
+    }
+
+    status = switch_ivr_phrase_macro(session, macro, macro_arg, NULL, ap);
+    
+    if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+        if (buf) {
+            memset(buf, 0, buflen);
+        }
+        return status;
+    }
+
+    if (!buf) {
+        return status;
+    }
+
+    bslen = strlen(buf);
+
+    if (maxlen == 0 || maxlen > buflen - 1) {
+        maxlen = buflen -1;
+    }
+    if (bslen < maxlen) {
+        status = switch_ivr_collect_digits_count(session, buf + bslen, buflen, maxlen - bslen, term_chars, terminator_key, timeout);
+    }
+
+    return status;
+}
+
+struct callback {
+    char *buf;
+    size_t len;
+    int matches;
+};
+typedef struct callback callback_t;
+
+static int sql2str_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+	callback_t *cbt = (callback_t *) pArg;
+
+	switch_copy_string(cbt->buf, argv[0], cbt->len);
+	cbt->matches++;
+	return 0;
+}
+
+
+static int unlink_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+    if (argv[0]) {
+        unlink(argv[0]);
+    }
+	return 0;
+}
+
+
+typedef enum {
+    MSG_NONE,
+    MSG_NEW,
+    MSG_SAVED
+} msg_type_t;
+
+static uint32_t DEFAULT_DIR_PERMS = SWITCH_FPROT_UREAD | SWITCH_FPROT_UWRITE | SWITCH_FPROT_UEXECUTE | SWITCH_FPROT_GREAD | SWITCH_FPROT_GEXECUTE;
+
+
+static switch_status_t create_file(switch_core_session_t *session, vm_profile_t *profile, char *macro_name, char *file_path)
+{
+    switch_channel_t *channel;
+    switch_status_t status = SWITCH_STATUS_SUCCESS;
+    switch_file_handle_t fh = { 0 };
+    switch_input_args_t args = { 0 };
+    char term;
+    char input[10] = "" , key_buf[80] = "";
+
+    channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+    
+
+    while(switch_channel_ready(channel)) {
+
+        
+        switch_channel_set_variable(channel, "RECORD_COMMENT", macro_name);
+
+
+
+
+        
+        snprintf(key_buf, sizeof(key_buf), "%s:%s:%s", 
+                 profile->listen_file_key,
+                 profile->save_file_key,
+                 profile->record_file_key);
+
+
+    record_file:
+        args.input_callback = cancel_on_dtmf;
+        TRY_CODE(switch_ivr_phrase_macro(session, macro_name, NULL, NULL, NULL));
+        TRY_CODE(switch_ivr_gentones(session, profile->tone_spec, 0, NULL));
+
+        memset(&fh, 0, sizeof(fh));
+        fh.thresh = 200;
+        fh.silence_hits = 2;
+
+
+        switch_ivr_record_file(session, &fh, file_path, &args, profile->max_record_len);
+        status = SWITCH_STATUS_SUCCESS;
+
+    play_file:
+        memset(&fh, 0, sizeof(fh));
+        args.input_callback = control_playback;
+        args.buf = &fh;
+        switch_ivr_play_file(session, &fh, file_path, &args);
+        
+        
+        while(switch_channel_ready(channel)) {
+            status = vm_macro_get(session, VM_RECORD_FILE_CHECK_MACRO, 
+                                  key_buf, input, sizeof(input), 1, profile->terminator_key, &term, profile->digit_timeout);
+            
+            if (!strcmp(input, profile->listen_file_key)) {
+                goto play_file;
+            } else if (!strcmp(input, profile->record_file_key)) {
+                goto record_file;
+            } else {
+                TRY_CODE(switch_ivr_phrase_macro(session, VM_ACK_MACRO, "saved", NULL, NULL));
+                goto end;
+            }
+        }
+    }
+    
+ end:
+
+    return status;
+}
+
+
+struct listen_callback {
+    char created_epoch[255];
+    char read_epoch[255];
+    char user[255];
+    char domain[255];
+    char uuid[255];
+    char cid_name[255];
+    char cid_number[255];
+    char in_folder[255];
+    char file_path[255];
+    char flags[255];
+    int index;
+    int want;
+    msg_type_t type;
+};
+typedef struct listen_callback listen_callback_t;
+
+static int listen_callback(void *pArg, int argc, char **argv, char **columnNames)
+{
+	listen_callback_t *cbt = (listen_callback_t *) pArg;
+
+    if (cbt->index++ != cbt->want) {
+        return 0;
+    }
+
+    switch_copy_string(cbt->created_epoch, argv[0], 255);
+	switch_copy_string(cbt->read_epoch, argv[1], 255);
+	switch_copy_string(cbt->user, argv[2], 255);
+	switch_copy_string(cbt->domain, argv[3], 255);
+	switch_copy_string(cbt->uuid, argv[4], 255);
+	switch_copy_string(cbt->cid_name, argv[5], 255);
+	switch_copy_string(cbt->cid_number, argv[6], 255);
+	switch_copy_string(cbt->in_folder, argv[7], 255);
+	switch_copy_string(cbt->file_path, argv[8], 255);
+	switch_copy_string(cbt->flags, argv[9], 255);
+    
+    
+	return -1;
+}
+
+
+static switch_status_t listen_file(switch_core_session_t *session, vm_profile_t *profile, listen_callback_t *cbt)
+{
+    switch_channel_t *channel;
+    switch_status_t status = SWITCH_STATUS_SUCCESS;
+    switch_input_args_t args = { 0 };
+    char term;
+    char input[10] = "" , key_buf[80] = "";
+    switch_file_handle_t fh = { 0 };
+    channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+    
+
+    if(switch_channel_ready(channel)) {
+        
+        args.input_callback = cancel_on_dtmf;
+
+
+        snprintf(key_buf, sizeof(key_buf), "%s:%s:%s:%s", 
+                 profile->listen_file_key,
+                 profile->save_file_key,
+                 profile->delete_file_key,
+                 profile->undelete_file_key);
+
+
+        snprintf(input, sizeof(input), "%s:%d", cbt->type == MSG_NEW ? "new" : "saved", cbt->index);
+        TRY_CODE(switch_ivr_phrase_macro(session, VM_SAY_MESSAGE_NUMBER_MACRO, input, NULL, NULL));
+        TRY_CODE(switch_ivr_phrase_macro(session, VM_SAY_DATE_MACRO, cbt->created_epoch, NULL, NULL));
+    play_file:
+
+        memset(&fh, 0, sizeof(fh));
+        args.input_callback = control_playback;
+        args.buf = &fh;
+        TRY_CODE(switch_ivr_play_file(session, NULL, cbt->file_path, &args));
+        
+        if (switch_channel_ready(channel)) {
+            status = vm_macro_get(session, VM_LISTEN_FILE_CHECK_MACRO, 
+                                  key_buf, input, sizeof(input), 1, profile->terminator_key, &term, profile->digit_timeout);
+            
+            if (!strcmp(input, profile->listen_file_key)) {
+                goto play_file;
+            } else if (!strcmp(input, profile->delete_file_key)) {
+                char *sql = switch_mprintf("update voicemail_data set flags='delete' where uuid='%s'", cbt->uuid);
+                vm_execute_sql(profile, sql, profile->mutex);
+                switch_safe_free(sql);
+                TRY_CODE(switch_ivr_phrase_macro(session, VM_ACK_MACRO, "deleted", NULL, NULL));
+            } else {
+                char *sql = switch_mprintf("update voicemail_data set flags='save' where uuid='%s'", cbt->uuid);
+                vm_execute_sql(profile, sql, profile->mutex);
+                switch_safe_free(sql);
+                TRY_CODE(switch_ivr_phrase_macro(session, VM_ACK_MACRO, "saved", NULL, NULL));
+            }
+        }
+    }
+    
+ end:
+
+    return status;
+}
+
+static void message_count(vm_profile_t *profile, char *myid, char *domain_name, char *myfolder, int *total_new_messages, int *total_saved_messages)
+{
+    char msg_count[80] = "";
+    callback_t cbt = { 0 };
+    char sql[128];
+    
+    cbt.buf = msg_count;
+    cbt.len = sizeof(msg_count);
+                
+
+    snprintf(sql, sizeof(sql), 
+             "select count(*) from voicemail_data where user='%s' and domain='%s' and in_folder='%s' and read_epoch=0", 
+             myid,
+             domain_name,
+             myfolder);
+    vm_execute_sql_callback(profile, profile->mutex, sql, sql2str_callback, &cbt);
+    *total_new_messages = atoi(msg_count);
+    
+    snprintf(sql, sizeof(sql), 
+             "select count(*) from voicemail_data where user='%s' and domain='%s' and in_folder='%s' and read_epoch!=0", 
+             myid,
+             domain_name,
+             myfolder);
+    vm_execute_sql_callback(profile, profile->mutex, sql, sql2str_callback, &cbt);
+    *total_saved_messages = atoi(msg_count);
+}
+
+
+static void voicemail_check_main(switch_core_session_t *session, char *profile_name, char *domain_name, char *id, int auth)
+{
+    vm_check_state_t vm_check_state = VM_CHECK_START;
+    switch_channel_t *channel;
+    vm_profile_t *profile;
+    switch_xml_t x_domain, x_domain_root, x_user, x_params, x_param;
+    switch_status_t status;
+    char pass_buf[80] = "", *mypass = NULL, id_buf[80] = "", *myid = id, *myfolder = NULL;
+    const char *thepass = NULL;
+    char term = 0;
+    uint32_t timeout, attempts = 0;
+    int failed = 0;
+    msg_type_t play_msg_type = MSG_NONE;
+    char *dir_path = NULL, *file_path = NULL;
+    int total_new_messages = 0;
+    int total_saved_messages = 0;
+    int heard_auto_saved = 0, heard_auto_new = 0;
+
+    channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);    
+
+    
+    if (!(profile = switch_core_hash_find(globals.profile_hash, profile_name))) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error invalid profile %s", profile_name);
+        return;
+    }
+    
+    x_user = x_domain = x_domain_root = NULL;
+
+    timeout = profile->digit_timeout;
+    attempts = profile->max_login_attempts;
+    
+    status = switch_ivr_phrase_macro(session, VM_HELLO_MACRO, NULL, NULL, NULL);
+
+    while(switch_channel_ready(channel)) {
+
+        switch_ivr_sleep(session, 100);
+        
+        switch(vm_check_state) {
+        case VM_CHECK_START:
+            {
+                total_new_messages = 0;
+                total_saved_messages = 0;
+                heard_auto_saved = 0;
+                heard_auto_new = 0;
+                play_msg_type = MSG_NONE;
+                attempts = profile->max_login_attempts;
+                myid = id;
+                mypass = NULL;
+                myfolder = "inbox";
+                vm_check_state = VM_CHECK_AUTH;
+                if (x_domain_root) {
+                    switch_xml_free(x_domain_root);
+                }
+                x_user = x_domain = x_domain_root = NULL;
+            }
+            break;
+        case VM_CHECK_FOLDER_SUMMARY:
+            {
+                char msg_count[80] = "";
+                switch_channel_set_variable(channel, "voicemail_current_folder", myfolder);
+                message_count(profile, myid, domain_name, myfolder, &total_new_messages, &total_saved_messages);
+                snprintf(msg_count, sizeof(msg_count), "%d", total_new_messages);
+                if ((status = switch_ivr_phrase_macro(session, VM_NEW_MESSAGE_COUNT_MACRO, msg_count, NULL, NULL)) != SWITCH_STATUS_SUCCESS) {
+                    goto end;
+                }
+
+                if (!heard_auto_new && total_new_messages > 0) {
+                    heard_auto_new = 1;
+                    play_msg_type = MSG_NEW;
+                    vm_check_state = VM_CHECK_PLAY_MESSAGES;
+                    continue;
+                }
+
+                snprintf(msg_count, sizeof(msg_count), "%d", total_saved_messages);
+                if ((status = switch_ivr_phrase_macro(session, VM_SAVED_MESSAGE_COUNT_MACRO, msg_count, NULL, NULL)) != SWITCH_STATUS_SUCCESS) {
+                    goto end;
+                }
+                
+                if (!heard_auto_saved && total_saved_messages > 0) {
+                    heard_auto_saved = 1;
+                    play_msg_type = MSG_SAVED;
+                    vm_check_state = VM_CHECK_PLAY_MESSAGES;
+                    continue;
+                }
+                
+                vm_check_state = VM_CHECK_MENU;
+            }
+            break;
+        case VM_CHECK_PLAY_MESSAGES:
+            {
+                listen_callback_t cbt;
+                char sql[128];
+                int cur_message, total_messages;
+                message_count(profile, myid, domain_name, myfolder, &total_new_messages, &total_saved_messages);
+                memset(&cbt, 0, sizeof(cbt));
+                switch(play_msg_type) {
+                case MSG_NEW:
+                    {
+                        snprintf(sql, sizeof(sql),
+                                 "select * from voicemail_data where user='%s' and domain='%s' and read_epoch=0", myid, domain_name);
+                        total_messages = total_new_messages;
+                        heard_auto_new = heard_auto_saved = 1;
+                    }
+                    break;
+                case MSG_SAVED:
+                default:
+                    {
+                        snprintf(sql, sizeof(sql),
+                                 "select * from voicemail_data where user='%s' and domain='%s' and read_epoch!=0", myid, domain_name);
+                        total_messages = total_saved_messages;
+                        heard_auto_new = heard_auto_saved = 1;                        
+                    }
+                    break;
+                }
+                for (cur_message = 0; cur_message < total_messages; cur_message++) {
+                    cbt.index = 0;
+                    cbt.want = cur_message;
+                    cbt.type = play_msg_type;
+                    vm_execute_sql_callback(profile, profile->mutex, sql, listen_callback, &cbt);
+                    status = listen_file(session, profile, &cbt);
+                    if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+                        break;
+                    }
+                }
+                snprintf(sql, sizeof(sql), "update voicemail_data set read_epoch=%ld where user='%s' and domain='%s' and flags='save'", 
+                         (long)time(NULL), myid, domain_name);
+                vm_execute_sql(profile, sql, profile->mutex);
+                snprintf(sql, sizeof(sql), "select file_path from voicemail_data where user='%s' and domain='%s' and flags='delete'", myid, domain_name);
+                vm_execute_sql_callback(profile, profile->mutex, sql, unlink_callback, NULL);
+                snprintf(sql, sizeof(sql), "delete from voicemail_data where user='%s' and domain='%s' and flags='delete'", myid, domain_name);
+                vm_execute_sql(profile, sql, profile->mutex);
+                vm_check_state = VM_CHECK_FOLDER_SUMMARY;
+            }
+            break;
+        case VM_CHECK_CONFIG:
+            {
+                char *sql = NULL;
+                char input[10] = "";
+                char key_buf[80] = "";
+                callback_t cbt = { 0 };
+                char msg_count[80] = "";
+                cbt.buf = msg_count;
+                cbt.len = sizeof(msg_count);
+                sql = switch_mprintf("select count(*) from voicemail_prefs where user='%q' and domain = '%q'", myid, domain_name);
+                vm_execute_sql_callback(profile, profile->mutex, sql, sql2str_callback, &cbt);
+                switch_safe_free(sql);
+                if (switch_strlen_zero(msg_count) || !atoi(msg_count)) {
+                    sql = switch_mprintf("insert into voicemail_prefs values('%q','%q','','')", myid, domain_name);
+                    vm_execute_sql(profile, sql, profile->mutex);
+                    switch_safe_free(sql);
+                }
+
+                snprintf(key_buf, sizeof(key_buf), "%s:%s:%s:%s", 
+                         profile->record_greeting_key, 
+                         profile->choose_greeting_key, 
+                         profile->record_name_key,
+                         profile->main_menu_key);
+                
+
+                TRY_CODE(vm_macro_get(session, VM_CONFIG_MENU_MACRO, key_buf, input, sizeof(input), 1, 
+                                      profile->terminator_key, &term, timeout));
+                if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+                    goto end;
+                }
+                
+                if (!strcmp(input, profile->main_menu_key)) {
+                    vm_check_state = VM_CHECK_MENU;
+                } else if (!strcmp(input, profile->choose_greeting_key)) {
+                    int num;
+                    switch_input_args_t args = { 0 };
+                    args.input_callback = cancel_on_dtmf;
+                    
+                    TRY_CODE(vm_macro_get(session, VM_CHOOSE_GREETING_MACRO, key_buf, input, sizeof(input), 1, 
+                                          profile->terminator_key, &term, timeout));
+                    
+                    num = atoi(input);
+                    file_path = switch_mprintf("%s%sgreeting_%d.wav", dir_path, SWITCH_PATH_SEPARATOR, num);
+                    if (num < 1 || num > 3) {
+                        status = SWITCH_STATUS_FALSE;
+                    } else {
+                        switch_file_handle_t fh = { 0 };
+                        memset(&fh, 0, sizeof(fh));
+                        args.input_callback = control_playback;
+                        args.buf = &fh;
+                        status = switch_ivr_play_file(session, NULL, file_path, &args);
+                    }
+                    if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+                        TRY_CODE(switch_ivr_phrase_macro(session, VM_CHOOSE_GREETING_FAIL_MACRO, NULL, NULL, NULL));
+                    } else {
+                        TRY_CODE(switch_ivr_phrase_macro(session, VM_CHOOSE_GREETING_SELECTED_MACRO, input, NULL, NULL));
+                        sql = switch_mprintf("update voicemail_prefs set greeting_path='%s' where user='%s' and domain='%s'", file_path, myid, domain_name);
+                        vm_execute_sql(profile, sql, profile->mutex);
+                        switch_safe_free(sql);
+                    }
+                    switch_safe_free(file_path);
+                } else if (!strcmp(input, profile->record_greeting_key)) {
+                    int num;
+                    TRY_CODE(vm_macro_get(session, VM_CHOOSE_GREETING_MACRO, key_buf, input, sizeof(input), 1, 
+                                          profile->terminator_key, &term, timeout));
+                    
+                    num = atoi(input);
+                    if (num < 1 || num > 3) {
+                        TRY_CODE(switch_ivr_phrase_macro(session, VM_CHOOSE_GREETING_FAIL_MACRO, NULL, NULL, NULL));
+                    } else {
+                        file_path = switch_mprintf("%s%sgreeting_%d.wav", dir_path, SWITCH_PATH_SEPARATOR, num);
+                        TRY_CODE(create_file(session, profile, VM_RECORD_GREETING_MACRO, file_path));
+                        sql = switch_mprintf("update voicemail_prefs set greeting_path='%s' where user='%s' and domain='%s'", file_path, myid, domain_name);
+                        vm_execute_sql(profile, sql, profile->mutex);
+                        switch_safe_free(sql);
+                        switch_safe_free(file_path);
+                    }
+
+                } else if (!strcmp(input, profile->record_name_key)) {
+                    file_path = switch_mprintf("%s%srecorded_name.wav", dir_path, SWITCH_PATH_SEPARATOR);
+                    TRY_CODE(create_file(session, profile, VM_RECORD_NAME_MACRO, file_path));
+                    sql = switch_mprintf("update voicemail_prefs set name_path='%s' where user='%s' and domain='%s'", file_path, myid, domain_name);
+                    vm_execute_sql(profile, sql, profile->mutex);
+                    switch_safe_free(file_path);
+                    switch_safe_free(sql);
+                }
+
+                continue;
+            }
+            break;
+        case VM_CHECK_MENU:
+            {
+                char input[10] = "";
+                char key_buf[80] = "";
+                play_msg_type = MSG_NONE;
+                
+                snprintf(key_buf, sizeof(key_buf), "%s:%s:%s", 
+                         profile->play_new_messages_key, 
+                         profile->play_saved_messages_key,
+                         profile->config_menu_key);
+                
+                status = vm_macro_get(session, VM_MENU_MACRO, key_buf, input, sizeof(input), 1, 
+                                      profile->terminator_key, &term, timeout);
+                if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+                    goto end;
+                }
+                
+                if (!strcmp(input, profile->play_new_messages_key)) {
+                    play_msg_type = MSG_NEW;
+                } else if (!strcmp(input, profile->play_saved_messages_key)) {
+                    play_msg_type = MSG_SAVED;
+                } else if (!strcmp(input, profile->config_menu_key)) {
+                    vm_check_state = VM_CHECK_CONFIG;
+                }
+                
+                if (play_msg_type) {
+                    vm_check_state = VM_CHECK_PLAY_MESSAGES;
+                }
+
+
+                continue;
+            }
+            break;
+        case VM_CHECK_AUTH:
+            {
+                if (!attempts) {
+                    failed = 1;
+                    goto end;
+                }
+
+                attempts--;
+
+                if (!myid) {
+                    status = vm_macro_get(session, VM_ENTER_ID_MACRO, profile->terminator_key, id_buf, sizeof(id_buf), 0, 
+                                          profile->terminator_key, &term, timeout);
+                    if (status != SWITCH_STATUS_SUCCESS) {
+                        goto end;
+                    }
+
+                    if (switch_strlen_zero(id_buf)) {
+                        continue;
+                    } else {
+                        myid = id_buf;
+                    } 
+                }
+
+                if (!x_user) {
+                    int ok = 1;
+                    char *xtra = switch_mprintf("mailbox=%s", myid);
+                    
+                    assert(xtra);
+
+                    if (switch_xml_locate_user(myid, domain_name, switch_channel_get_variable(channel, "network_addr"), 
+                                               &x_domain_root, &x_domain, &x_user, xtra) != SWITCH_STATUS_SUCCESS) {
+                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "can't find user [%s@%s]\n", myid, domain_name);
+                        ok = 0;
+                    }
+
+                    switch_safe_free(xtra);
+
+                    if (!ok) {
+                        goto failed;
+                    }
+                }
+                
+                if (!mypass) {
+                    if (auth) {
+                        mypass = "OK";
+                    } else {
+                        status = vm_macro_get(session, VM_ENTER_PASS_MACRO, profile->terminator_key, 
+                                              pass_buf, sizeof(pass_buf), 0, profile->terminator_key, &term, timeout);
+                        if (status != SWITCH_STATUS_SUCCESS) {
+                            goto end;
+                        }
+                        if (switch_strlen_zero(pass_buf)) {
+                            continue;
+                        } else {
+                            mypass = pass_buf;
+                        }
+                    }
+                }
+                
+                if (!(x_params = switch_xml_child(x_user, "params"))) {
+                    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "can't find params for user [%s@%s]\n", myid, domain_name);
+                    goto failed;
+                }
+                
+                thepass = NULL;
+                for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) {
+                    const char *var = switch_xml_attr_soft(x_param, "name");
+                    const char *val = switch_xml_attr_soft(x_param, "value");
+                    
+                    if (!strcasecmp(var, "password")) {
+                        thepass = val;
+                    } else if (!strcasecmp(var, "vm-password")) {
+                        thepass = val;
+                    }
+                }
+                switch_xml_free(x_domain_root);
+                x_domain_root = NULL;
+
+                if (auth || !thepass || (thepass && mypass && !strcmp(thepass, mypass))) {
+                    if (!dir_path) {
+                        dir_path = switch_core_session_sprintf(session, "%s%svoicemail%s%s%s%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, 
+                                                               SWITCH_PATH_SEPARATOR,
+                                                               SWITCH_PATH_SEPARATOR,
+                                                               profile->name,
+                                                               SWITCH_PATH_SEPARATOR,
+                                                               domain_name,
+                                                               SWITCH_PATH_SEPARATOR,
+                                                               myid);
+                        
+                        
+                        if (switch_dir_make_recursive(dir_path, DEFAULT_DIR_PERMS, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+                            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating %s", dir_path);
+                            return;
+                        }
+                    }
+
+
+                    vm_check_state = VM_CHECK_FOLDER_SUMMARY;
+                    //vm_check_state = VM_CHECK_MENU;
+                } else {
+                    goto failed;
+                }
+                
+                continue;
+            failed:
+                status = switch_ivr_phrase_macro(session, VM_FAIL_AUTH_MACRO, NULL, NULL, NULL);
+                myid = id;
+                mypass = NULL;
+                continue;
+            }
+            break;
+        default:
+            break;
+        }
+    }
+    
+ end:
+
+    if (switch_channel_ready(channel)) {
+        if (failed) {
+            status = switch_ivr_phrase_macro(session, VM_ABORT_MACRO, NULL, NULL, NULL);
+        }
+        status = switch_ivr_phrase_macro(session, VM_GOODBYE_MACRO, NULL, NULL, NULL);
+    }
+
+    if (x_domain_root) {
+        switch_xml_free(x_domain_root);
+    }
+
+}
+
+
+static switch_status_t voicemail_leave_main(switch_core_session_t *session, char *profile_name, char *domain_name, char *id)
+{
+    switch_channel_t *channel;
+    char *myfolder = "inbox";
+    char sql[128];
+    prefs_callback_t cbt;
+    vm_profile_t *profile;
+    char *uuid = switch_core_session_get_uuid(session);
+    char *file_path = NULL;
+    char *dir_path = NULL;
+    switch_status_t status = SWITCH_STATUS_SUCCESS;
+    switch_caller_profile_t *caller_profile;
+    switch_file_handle_t fh = { 0 };
+    switch_input_args_t args = { 0 };
+    char *email_vm = NULL;
+
+    memset(&cbt, 0, sizeof(cbt));
+    if (!(profile = switch_core_hash_find(globals.profile_hash, profile_name))) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error invalid profile %s", profile_name);
+        return SWITCH_STATUS_FALSE;
+    }
+
+    channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+    
+    caller_profile = switch_channel_get_caller_profile(channel);
+    dir_path = switch_core_session_sprintf(session, "%s%svoicemail%s%s%s%s%s%s", SWITCH_GLOBAL_dirs.storage_dir, 
+                                           SWITCH_PATH_SEPARATOR,
+                                           SWITCH_PATH_SEPARATOR,
+                                           profile->name,
+                                           SWITCH_PATH_SEPARATOR,
+                                           domain_name,
+                                           SWITCH_PATH_SEPARATOR,
+                                           id);
+    if (switch_dir_make_recursive(dir_path, DEFAULT_DIR_PERMS, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating %s", dir_path);
+        goto end;
+    }
+
+
+    if (id) {
+        int ok = 1;
+        char *xtra = switch_mprintf("mailbox=%s", id);
+        switch_xml_t x_domain, x_domain_root, x_user, x_params, x_param;
+        
+        assert(xtra);
+        x_user = x_domain = x_domain_root = NULL;
+        if (switch_xml_locate_user(id, domain_name, switch_channel_get_variable(channel, "network_addr"), 
+                                   &x_domain_root, &x_domain, &x_user, xtra) != SWITCH_STATUS_SUCCESS) {
+            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "can't find user [%s@%s]\n", id, domain_name);
+            ok = 0;
+        }
+
+        if ((x_params = switch_xml_child(x_user, "params"))) {
+            for (x_param = switch_xml_child(x_params, "param"); x_param; x_param = x_param->next) {
+                const char *var = switch_xml_attr_soft(x_param, "name");
+                const char *val = switch_xml_attr_soft(x_param, "value");
+            
+                if (!strcasecmp(var, "vm-mailto")) {
+                    email_vm = switch_core_session_strdup(session, val);
+                }
+            }
+        }
+
+        switch_safe_free(xtra);
+        switch_xml_free(x_domain_root);
+        if (!ok) {
+            goto end;
+        }
+    }
+
+    snprintf(sql, sizeof(sql), 
+             "select * from voicemail_prefs where user='%s' and domain='%s'", 
+             id,
+             domain_name);
+    vm_execute_sql_callback(profile, profile->mutex, sql, prefs_callback, &cbt);
+    
+    file_path = switch_mprintf("%s%smsg_%s.wav", dir_path, SWITCH_PATH_SEPARATOR, uuid);
+
+    memset(&fh, 0, sizeof(fh));
+    args.input_callback = control_playback;
+    args.buf = &fh;
+
+    if (!switch_strlen_zero(cbt.greeting_path)) {
+        TRY_CODE(switch_ivr_play_file(session, NULL, cbt.greeting_path, NULL));
+    } else {
+        if (!switch_strlen_zero(cbt.name_path)) {
+            TRY_CODE(switch_ivr_play_file(session, NULL, cbt.name_path, NULL));
+        }
+        TRY_CODE(switch_ivr_phrase_macro(session, VM_PLAY_GREETING_MACRO, id, NULL, NULL));
+    }
+
+    if(switch_channel_ready(channel)) {
+        char *usql;
+        status = create_file(session, profile, VM_RECORD_MESSAGE_MACRO, file_path);
+        if (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK) {
+            usql = switch_mprintf("insert into voicemail_data values(%ld,0,'%q','%q','%q','%q','%q','%q','%q','')", (long)time(NULL),
+                                  id, domain_name, uuid, caller_profile->caller_id_name, caller_profile->caller_id_number, myfolder, file_path);
+            vm_execute_sql(profile, usql, profile->mutex);
+            switch_safe_free(usql);
+        } else {
+            unlink(file_path);
+            goto end;
+        }
+
+    }
+
+ end:
+    
+    if (!switch_strlen_zero(email_vm)) {
+        
+        switch_event_t *event;
+        char *from = switch_core_session_sprintf(session, "FreeSWITCH mod_voicemail <%s@%s>", id, domain_name);
+        char *headers = switch_core_session_sprintf(session, "Subject: Voicemail from %s %s", 
+                                                    caller_profile->caller_id_name, caller_profile->caller_id_number);
+        char *body;
+
+        
+        if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) {
+            switch_channel_event_set_data(channel, event);
+            switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Message-Type", "voicemail");
+            switch_event_serialize(event, &body);
+            switch_event_fire(&event);
+        } else {
+            body = switch_mprintf("Voicemail from %s %s", 
+                                  caller_profile->caller_id_name, caller_profile->caller_id_number);
+        }
+
+        //TBD add better formatting to the body
+        vm_email(email_vm, from, headers, body, file_path);
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Sending message to %s", email_vm);
+        switch_safe_free(body);
+    }
+
+    switch_safe_free(file_path);
+    
+    if (switch_channel_ready(channel)) {
+        status = switch_ivr_phrase_macro(session, VM_GOODBYE_MACRO, NULL, NULL, NULL);
+    }
+
+    return status;
+
+
+}
+
+
+#define VM_DESC "voicemail"
+#define VM_USAGE "[check|auth] <profile_name> <domain_name> [<id>]"
+
+SWITCH_STANDARD_APP(voicemail_function)
+{
+	int argc = 0;
+	char *argv[4] = { 0 };
+	char *mydata = NULL;
+    char *profile_name = NULL;
+    char *domain_name = NULL;
+    char *id = NULL;
+    char *auth_var = NULL;
+    int x = 0, check = 0, auth = 0;
+    switch_channel_t *channel;
+
+    channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+    
+    if (switch_dir_make_recursive(SWITCH_GLOBAL_dirs.storage_dir, DEFAULT_DIR_PERMS, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error creating %s", SWITCH_GLOBAL_dirs.storage_dir);
+        return;
+    }
+    
+    if (!switch_strlen_zero(data)) {
+        mydata = switch_core_session_strdup(session, data);
+        argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+    }
+
+    if (argv[x] && !strcasecmp(argv[x], "check")) {
+        check++;
+        x++;
+    }
+
+    if (argv[x] && !strcasecmp(argv[x], "auth")) {
+        auth++;
+        x++;
+    }
+
+    if (argv[x]) {
+        profile_name = argv[x++];
+    }
+
+    if (argv[x]) {
+        domain_name = argv[x++];
+    }
+
+    if (argv[x]) {
+        id = argv[x++];
+    }
+
+
+    if ((auth_var = switch_channel_get_variable(channel, "voicemail_authorized")) && switch_true(auth_var)) {
+        auth = 1;
+    }
+
+    if (switch_strlen_zero(profile_name)) {
+        profile_name = switch_channel_get_variable(channel, "voicemail_profile_name");
+    }
+
+    if (switch_strlen_zero(domain_name)) {
+        domain_name = switch_channel_get_variable(channel, "voicemail_domain_name");
+    }
+
+    if (switch_strlen_zero(id)) {
+        id = switch_channel_get_variable(channel, "voicemail_id");
+    }
+
+    if (switch_strlen_zero(profile_name) || switch_strlen_zero(domain_name)) {
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Usage: %s\n", VM_USAGE);
+        return;
+    }
+    
+    if (check) {
+        voicemail_check_main(session, profile_name, domain_name, id, auth);
+    } else {
+        voicemail_leave_main(session, profile_name, domain_name, id);
+    }
+}
+
+SWITCH_MODULE_LOAD_FUNCTION(mod_voicemail_load)
+{
+	switch_application_interface_t *app_interface;
+    switch_status_t status;
+
+    if ((status = load_config()) != SWITCH_STATUS_SUCCESS) {
+        return status;
+    }
+	/* connect my internal structure to the blank pointer passed to me */
+	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
+	SWITCH_ADD_APP(app_interface, "voicemail", "Voicemail", VM_DESC, voicemail_function, VM_USAGE, SAF_NONE);
+    
+	/* indicate that the module should continue to be loaded */
+	return SWITCH_STATUS_NOUNLOAD;
+}
+
+/* For Emacs:
+ * Local Variables:
+ * mode:c
+ * indent-tabs-mode:nil
+ * tab-width:4
+ * c-basic-offset:4
+ * End:
+ * For VIM:
+ * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab:
+ */

Added: freeswitch/trunk/src/mod/applications/mod_voicemail/mod_voicemail.vcproj
==============================================================================
--- (empty file)
+++ freeswitch/trunk/src/mod/applications/mod_voicemail/mod_voicemail.vcproj	Thu Oct 11 23:28:59 2007
@@ -0,0 +1,210 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="8.00"
+	Name="mod_voicemail"
+	ProjectGUID="{C24FB505-05D7-4319-8485-7540B44C8603}"
+	RootNamespace="mod_voicemail"
+	Keyword="Win32Proj"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(InputDir)..\..\..\include&quot;;&quot;$(InputDir)include&quot;;&quot;$(InputDir)..\..\..\..\libs\include&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				UsePrecompiledHeader="0"
+				WarningLevel="4"
+				WarnAsError="true"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="$(SolutionDir)$(OutDir)/mod/$(InputName).dll"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="..\..\..\..\w32\vsnet\$(OutDir)"
+				GenerateDebugInformation="true"
+				ProgramDatabaseFile="$(OutDir)$(TargetName).pdb"
+				SubSystem="2"
+				ImportLibrary="$(OutDir)/mod_voicemail.lib"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="$(ConfigurationName)"
+			IntermediateDirectory="$(ConfigurationName)"
+			ConfigurationType="2"
+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+			CharacterSet="2"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				AdditionalIncludeDirectories="&quot;$(InputDir)..\..\..\include&quot;;&quot;$(InputDir)include&quot;;&quot;$(InputDir)..\..\..\..\libs\include&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MOD_EXPORTS"
+				RuntimeLibrary="2"
+				UsePrecompiledHeader="0"
+				WarningLevel="4"
+				WarnAsError="true"
+				Detect64BitPortabilityProblems="true"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				OutputFile="$(SolutionDir)$(OutDir)/mod/$(InputName).dll"
+				LinkIncremental="1"
+				AdditionalLibraryDirectories="..\..\..\..\w32\vsnet\$(OutDir)"
+				GenerateDebugInformation="true"
+				ProgramDatabaseFile="$(OutDir)$(TargetName).pdb"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				ImportLibrary="$(OutDir)/mod_voicemail.lib"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCWebDeploymentTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+			>
+			<File
+				RelativePath=".\mod_voicemail.c"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="Header Files"
+			Filter="h;hpp;hxx;hm;inl;inc;xsd"
+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+			>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+			>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>

Modified: freeswitch/trunk/src/switch_apr.c
==============================================================================
--- freeswitch/trunk/src/switch_apr.c	(original)
+++ freeswitch/trunk/src/switch_apr.c	Thu Oct 11 23:28:59 2007
@@ -394,6 +394,51 @@
 	return status;
 }
 
+/* #define SWITCH_FPROT_USETID      0x8000 /\**< Set user id *\/ */
+/* #define SWITCH_FPROT_UREAD       0x0400 /\**< Read by user *\/ */
+/* #define SWITCH_FPROT_UWRITE      0x0200 /\**< Write by user *\/ */
+/* #define SWITCH_FPROT_UEXECUTE    0x0100 /\**< Execute by user *\/ */
+
+/* #define SWITCH_FPROT_GSETID      0x4000 /\**< Set group id *\/ */
+/* #define SWITCH_FPROT_GREAD       0x0040 /\**< Read by group *\/ */
+/* #define SWITCH_FPROT_GWRITE      0x0020 /\**< Write by group *\/ */
+/* #define SWITCH_FPROT_GEXECUTE    0x0010 /\**< Execute by group *\/ */
+
+/* #define SWITCH_FPROT_WSTICKY     0x2000 /\**< Sticky bit *\/ */
+/* #define SWITCH_FPROT_WREAD       0x0004 /\**< Read by others *\/ */
+/* #define SWITCH_FPROT_WWRITE      0x0002 /\**< Write by others *\/ */
+/* #define SWITCH_FPROT_WEXECUTE    0x0001 /\**< Execute by others *\/ */
+
+/* #define SWITCH_FPROT_OS_DEFAULT  0x0FFF /\**< use OS's default permissions *\/ */
+
+
+/**
+ * Create a new directory on the file system.
+ * @param path the path for the directory to be created. (use / on all systems)
+ * @param perm Permissions for the new direcoty.
+ * @param pool the pool to use.
+ */
+SWITCH_DECLARE(switch_status_t) switch_dir_make(const char *path, switch_fileperms_t perm,
+												switch_memory_pool_t *pool)
+{
+	return apr_dir_make(path, perm, pool);
+}
+
+/** Creates a new directory on the file system, but behaves like
+ * 'mkdir -p'. Creates intermediate directories as required. No error
+ * will be reported if PATH already exists.
+ * @param path the path for the directory to be created. (use / on all systems)
+ * @param perm Permissions for the new direcoty.
+ * @param pool the pool to use.
+ */
+SWITCH_DECLARE(switch_status_t) switch_dir_make_recursive(const char *path,
+														  switch_fileperms_t perm,
+														  switch_memory_pool_t *pool)
+{
+	return apr_dir_make_recursive(path, perm, pool);
+}
+
+
 struct switch_dir {
 	apr_dir_t *dir_handle;
 	apr_finfo_t finfo;

Modified: freeswitch/trunk/src/switch_core.c
==============================================================================
--- freeswitch/trunk/src/switch_core.c	(original)
+++ freeswitch/trunk/src/switch_core.c	Thu Oct 11 23:28:59 2007
@@ -295,6 +295,14 @@
 #endif
 	}
 
+	if (!SWITCH_GLOBAL_dirs.storage_dir && (SWITCH_GLOBAL_dirs.storage_dir = (char *) malloc(BUFSIZE))) {
+#ifdef SWITCH_STORAGE_DIR
+		switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s", SWITCH_STORAGE_DIR);
+#else
+		switch_snprintf(SWITCH_GLOBAL_dirs.storage_dir, BUFSIZE, "%s%sstorage", base_dir, SWITCH_PATH_SEPARATOR);
+#endif
+	}
+
 	if (!SWITCH_GLOBAL_dirs.db_dir && (SWITCH_GLOBAL_dirs.db_dir = (char *) malloc(BUFSIZE))) {
 #ifdef SWITCH_DB_DIR
 		switch_snprintf(SWITCH_GLOBAL_dirs.db_dir, BUFSIZE, "%s", SWITCH_DB_DIR);

Modified: freeswitch/trunk/src/switch_core_file.c
==============================================================================
--- freeswitch/trunk/src/switch_core_file.c	(original)
+++ freeswitch/trunk/src/switch_core_file.c	Thu Oct 11 23:28:59 2007
@@ -55,7 +55,7 @@
 	}
 
 	if ((fh->file_interface = switch_loadable_module_get_file_interface(ext)) == 0) {
-		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid file format [%s]!\n", ext);
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "invalid file format [%s] for [%s]!\n", ext, file_path);
 		return SWITCH_STATUS_GENERR;
 	}
 

Modified: freeswitch/trunk/src/switch_xml.c
==============================================================================
--- freeswitch/trunk/src/switch_xml.c	(original)
+++ freeswitch/trunk/src/switch_xml.c	Thu Oct 11 23:28:59 2007
@@ -1241,7 +1241,8 @@
 }
 
 
-SWITCH_DECLARE(switch_status_t) switch_xml_locate_user(char *user_name, char *domain_name, 
+SWITCH_DECLARE(switch_status_t) switch_xml_locate_user(char *user_name, 
+													   char *domain_name, 
 													   char *ip, 
 													   switch_xml_t *root,
 													   switch_xml_t *domain,
@@ -1272,7 +1273,8 @@
 	} 
 
 	if (user_name) {
-		if (!(*user = switch_xml_find_child(*domain, "user", "id", user_name))) {
+		if (!(*user = switch_xml_find_child(*domain, "user", "id", user_name)) && strstr(xtra_params, "mailbox") && 
+			!(*user = switch_xml_find_child(*domain, "user", "mailbox", user_name))) {
 			return SWITCH_STATUS_FALSE;
 		}
 		return SWITCH_STATUS_SUCCESS;



More information about the Freeswitch-svn mailing list