[Freeswitch-svn] [commit] r3183 - in freeswitch/branches/ptinsley/voicemail/src: . include mod/applications/mod_voicemail

Freeswitch SVN ptinsley at freeswitch.org
Tue Oct 24 15:43:24 EDT 2006


Author: ptinsley
Date: Tue Oct 24 15:43:23 2006
New Revision: 3183

Added:
   freeswitch/branches/ptinsley/voicemail/src/mod/applications/mod_voicemail/   (props changed)
   freeswitch/branches/ptinsley/voicemail/src/mod/applications/mod_voicemail/mod_voicemail.c
Modified:
   freeswitch/branches/ptinsley/voicemail/src/include/switch_ivr.h
   freeswitch/branches/ptinsley/voicemail/src/switch_ivr.c

Log:
Getting my pre-cluecon code in a branch so i can get it up to date and finished up, pretty much ignore the voicemail stuff ATM, just me learning the modules way back


Modified: freeswitch/branches/ptinsley/voicemail/src/include/switch_ivr.h
==============================================================================
--- freeswitch/branches/ptinsley/voicemail/src/include/switch_ivr.h	(original)
+++ freeswitch/branches/ptinsley/voicemail/src/include/switch_ivr.h	Tue Oct 24 15:43:23 2006
@@ -152,6 +152,48 @@
 													 void *buf,
 													 unsigned int buflen);
 
+/*!
+ \brief Function to evaluate an expression against a string
+ \param target The string to find a match in
+ \param expression The regular expression to run against the string
+ \return Boolean if a match was found or not
+*/
+SWITCH_DECLARE(switch_status_t) switch_regex_match(char *target, char *expression);
+
+/*!
+  \brief Play a sound and gather digits with the number of retries specified if the user doesn't give digits in the set time
+  \param session the current session to play sound to and collect digits
+  \param min_digits the fewest digits allowed for the response to be valid
+  \param max_digits the max number of digits to accept
+  \param max_tries number of times to replay the sound and capture digits
+  \param timeout time to wait for input (this is per iteration, so total possible time = max_tries * (timeout + audio playback length)
+  \param valid_terminators for input that can include # or * (useful for variable length prompts)
+  \param audio_file file to play
+  \param bad_input_audio_file file to play if the input from the user was invalid
+  \param digit_buffer variable digits captured will be put back into (empty if capture failed)
+  \param digit_buffer_length length of the buffer for digits (should be the same or larger than max_digits)
+  \return switch status, used to note status of channel (will still return success if digit capture failed)
+  \note to test for digit capture failure look for \0 in the first position of the buffer
+*/
+SWITCH_DECLARE(switch_status_t) switch_play_and_get_digits(switch_core_session_t *session,
+                                                           unsigned int min_digits,
+                                                           unsigned int max_digits,
+                                                           unsigned int max_tries,
+                                                           unsigned int timeout,
+                                                           char* valid_terminators,
+                                                           char* audio_file,
+                                                           char* bad_input_audio_file,
+                                                           void* digit_buffer,
+                                                           unsigned int digit_buffer_length,
+                                                           char* digits_regex);
+
+SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session_t *session,
+                                                             switch_speech_handle_t *sh,                                                                                                      switch_codec_t *codec,
+                                                             switch_timer_t *timer,                                                                                                           switch_input_callback_function_t dtmf_callback,
+                                                             char *text,                                                                                                                      void *buf,
+                                                             unsigned int buflen);
+
+
 
 SWITCH_DECLARE(switch_status_t) switch_ivr_speak_text_handle(switch_core_session_t *session,
                                                              switch_speech_handle_t *sh,

Added: freeswitch/branches/ptinsley/voicemail/src/mod/applications/mod_voicemail/mod_voicemail.c
==============================================================================
--- (empty file)
+++ freeswitch/branches/ptinsley/voicemail/src/mod/applications/mod_voicemail/mod_voicemail.c	Tue Oct 24 15:43:23 2006
@@ -0,0 +1,339 @@
+/* 
+ * 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
+ * Paul Tinsley <jackhammer at gmail.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>
+ * Paul Tinsley <jackhammer at gmail.com>
+ *
+ *
+ * mod_voicemail.c -- A basic voicemail system
+ *
+ */
+#include <switch.h>
+#include <unistd.h>
+#include <pcre.h>
+#include <stdio.h>
+
+static const char modname[] = "mod_voicemail";
+static const char global_cf_name[] = "voicemail.conf";
+
+typedef struct voicemail_user voicemail_user;
+typedef struct voicemail_message voicemail_message;
+
+struct voicemail_user {
+	switch_core_session_t *session;
+	char mailbox[10];
+	char password[10];
+	int nopass;
+	int isvalid;
+	char context[25];
+	char prompt_dir[1024];
+};
+
+struct voicemail_message {
+	char message_id[40];
+	int unread;
+	struct tm time_recieved;
+	voicemail_user *vm_user;
+};
+
+static switch_status_t _get_credentials(voicemail_user *vm_user);
+static void _authenticate_user(voicemail_user *vm_user);
+
+/*!
+ \brief Function to populate a time struct with the current time details
+ \return tm structure (time.h)
+*/
+struct tm *current_time() {
+	time_t now;
+	now = time(0);
+
+	return localtime(&now);
+}
+
+/*!
+ \brief Function to write out an xml description file of the details of a voicemail (date, CID, etc...)
+ \param voicemail_message to save the details of
+ \return Succes or failure of file write
+*/
+#ifdef ASDF
+static int save_message_detail_xml(voicemail_message *vm_message) {
+	
+}
+#endif
+
+static switch_status_t _get_credentials(voicemail_user *vm_user) {
+	switch_channel_t *channel;  //used to hold the current channel in session
+	char input[10];
+
+	// Get the associated channel
+	channel = switch_core_session_get_channel(vm_user->session);
+
+	//see if we already have a mailbox
+	if(!strlen(vm_user->mailbox)) {
+		//try to get the mailbox # from the user
+		switch_play_and_get_digits(vm_user->session, 3, 3, 3, 7000, "#", "/usr/local/freeswitch/voicemail/prompts/en-us/mailbox.gsm", "NULL", input, sizeof(input), "^13[68]$");
+
+		//see if we got a mailbox back
+		if(!strlen(input)) {
+			//say goodbye
+			return switch_channel_get_state(channel);
+		} else {
+			//save the input
+			strncpy(vm_user->mailbox, input, sizeof(vm_user->mailbox));
+		}
+	}
+
+	//See if we need the password
+	if(!vm_user->nopass) {
+		//try and get the password
+		switch_play_and_get_digits(vm_user->session, 1, 10, 3, 7000, "#", "/usr/local/freeswitch/voicemail/prompts/en-us/password.gsm", "NULL", input, sizeof(input), NULL);
+
+		//see if we got the password
+		if(!strlen(input)) {
+			// say goodbye
+			return switch_channel_get_state(channel);
+		} else {
+			//save the input
+			strncpy(vm_user->password, input, sizeof(vm_user->password));
+		}
+	}
+
+	//if we get here we should be in good shape
+	return switch_channel_get_state(channel);
+}
+
+static switch_status_t _main_menu(voicemail_user *vm_user) {
+	switch_channel_t *channel; //used to hold the channel variable
+	switch_status_t  status;   //used to hold the status variable
+	char input[10];            //used to hold the input from the user
+
+	// Get the associated channel
+	channel = switch_core_session_get_channel(vm_user->session);
+
+	//retrieve the current status
+	status = switch_channel_get_state(channel);
+
+	//Make sure we are in good standing before we start
+	if(status != CS_EXECUTE) {
+		//Bail
+		return status;
+	}
+
+	//Forever, muhahahahaha
+	while(1) {
+		//play the main menu and get user input (1 digit, 3 tries, 7 second timeout, 123789 allowed)
+		status = switch_play_and_get_digits(vm_user->session, 1, 1, 3, 7000, "", "/usr/local/freeswitch/voicemail/prompts/en-us/main-menu.wav", NULL, input, sizeof(input), "^[123789]$");
+
+		//Make sure everything is still copasetic
+		if(status != CS_EXECUTE) {
+			//Bail
+			return status;
+		}
+
+		//Figure out where to go
+		switch(input[0]) {
+			case '1': { //press 1 to listen to your messages
+				break;
+			}
+			case '2': { //press 2 to change your current folder
+				break;
+			}
+			case '3': { //press 3 to send a message to another user
+				break;
+			}
+			case '7': { //Press 7 to change your greetings
+				break;
+			}
+			case '8': { //Press 8 to change your password
+				break;
+			}
+			case '9': { //Press 9 to logout
+				//User is done, bail
+				return status;
+			}
+			default: {
+				//User didn't respond in time, bail
+				return status;
+			}
+		}
+	}
+	
+
+	//Stuff rolls down hill...
+	return switch_channel_get_state(channel);
+}
+
+static void _authenticate_user(voicemail_user *vm_user) {
+	if(!strcmp(vm_user->mailbox, "136")) {
+		if(!strcmp(vm_user->password, "1234")) {
+			vm_user->isvalid = 1;
+		}
+	}
+}
+
+static void login_function(switch_core_session_t *session, char *data) {
+	switch_channel_t *channel;
+	voicemail_user *vm_user;    //Struct that holds details about the logged in user
+	switch_status_t status;
+
+	//voicemail_user foo;
+
+	//vm_user = &foo;
+
+	//allocate our user struct
+	if(!(vm_user = switch_core_session_alloc(session, sizeof(*vm_user)))) {
+		//bad things
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "COULDN't Allocate\n");
+		return;
+	}
+
+
+	// Parse the data coming from the dialplan
+	switch_xml_t parameters = switch_xml_parse_str(data, strlen(data));
+
+	//pull off all of the individual params
+	char *context = (char*) switch_xml_attr_soft(parameters, "context");
+
+	//setup the user
+	vm_user->isvalid = 0;
+	strncpy(vm_user->context, context, sizeof(vm_user->context));
+	strncpy(vm_user->prompt_dir, "/usr/local/freeswitch/voicemail/prompts", sizeof(vm_user->context));
+	vm_user->session = session;
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "STRUCT %s", vm_user->prompt_dir);
+
+
+	// Get the associated channel
+	channel = switch_core_session_get_channel(session);
+
+	// Make sure somebody is home
+	assert(channel != NULL);
+
+	//Answer the channel if we haven't already
+	switch_channel_answer(channel);
+
+	int tries_left;
+
+	for(tries_left = 3; tries_left > 0 && (switch_channel_get_state(channel) == CS_EXECUTE); tries_left--) {
+		//prompt the user for their credentials
+		status = _get_credentials(vm_user);
+
+		
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "STRUCT %s\n", vm_user->mailbox);
+
+		//Make sure the channel is in good standing
+		if (status != SWITCH_STATUS_SUCCESS && status != CS_EXECUTE) {
+			switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+			return;
+		}
+
+		//make sure we got what we needed.
+		if(!(strlen(vm_user->mailbox) && (strlen(vm_user->password) || vm_user->nopass))) {
+			//Can't go on without user info...
+			return;
+		}
+
+		_authenticate_user(vm_user);
+
+		if(vm_user->isvalid) {
+			break;
+		}
+	}
+
+	if(!vm_user->isvalid) {
+		// say goodbye
+		switch_ivr_play_file(session, NULL, "/usr/local/freeswitch/voicemail/prompts/en-us/goodbye.gsm", NULL, NULL, NULL, 0);
+		return;
+	}
+
+	
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "WORKED\n");
+
+	//User is authenticated
+	status = _main_menu(vm_user);
+
+	//If we get here we should be done
+	switch_ivr_play_file(session, NULL, "/usr/local/freeswitch/voicemail/prompts/en-us/goodbye.gsm", NULL, NULL, NULL, 0);
+	return;
+}
+
+
+/*
+static switch_status_t my_on_hangup(switch_core_session_t *session)
+{
+	switch_channel_t *channel;
+
+	channel = switch_core_session_get_channel(session);
+    assert(channel != NULL);
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "I globally hooked to [%s] on the hangup event\n", switch_channel_get_name(channel));
+	return SWITCH_STATUS_SUCCESS;
+
+}
+*/
+
+static const switch_state_handler_table_t state_handlers = {
+	/*.on_init */ NULL,
+	/*.on_ring */ NULL,
+	/*.on_execute */ NULL,
+	/*.on_hangup */ NULL,
+	/*.on_loopback */ NULL,
+	/*.on_transmit */ NULL
+};
+
+static const switch_application_interface_t voicemail_login_application_interface = {
+	/*.interface_name */ "voicemail_login",
+	/*.application_function */ login_function,
+	NULL, NULL, NULL,
+	/*.next*/ NULL
+};
+
+static const switch_loadable_module_interface_t mod_voicemail_module_interface = {
+	/*.module_name = */ modname,
+	/*.endpoint_interface = */ NULL,
+	/*.timer_interface = */ NULL,
+	/*.dialplan_interface = */ NULL,
+	/*.codec_interface = */ NULL,
+	/*.application_interface */ &voicemail_login_application_interface
+};
+
+SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) {
+
+	/* connect my internal structure to the blank pointer passed to me */
+	*module_interface = &mod_voicemail_module_interface;
+
+	/* test global state handlers */
+	switch_core_add_state_handler(&state_handlers);
+
+	/* indicate that the module should continue to be loaded */
+	return SWITCH_STATUS_SUCCESS;
+}
+
+/* 'switch_module_runtime' will start up in a thread by itself just by having it exist 
+   if it returns anything but SWITCH_STATUS_TERM it will be called again automaticly
+*/
+
+
+//switch_status_t switch_module_runtime(void)

Modified: freeswitch/branches/ptinsley/voicemail/src/switch_ivr.c
==============================================================================
--- freeswitch/branches/ptinsley/voicemail/src/switch_ivr.c	(original)
+++ freeswitch/branches/ptinsley/voicemail/src/switch_ivr.c	Tue Oct 24 15:43:23 2006
@@ -933,6 +933,172 @@
 	return status;
 }
 
+SWITCH_DECLARE(switch_status_t) switch_regex_match(char *target, char *expression) {
+	const char* error   = NULL;  //Used to hold any errors
+	int error_offset    = 0;     //Holds the offset of an error
+	pcre* pcre_prepared = NULL;  //Holds the compiled regex
+	int match_count     = 0;     //Number of times the regex was matched
+	int offset_vectors[2];       //not used, but has to exist or pcre won't even try to find a match
+	
+	//Compile the expression
+	pcre_prepared = pcre_compile(expression, 0, &error, &error_offset, NULL);
+
+	//See if there was an error in the expression
+	if(error != NULL) {
+		//Clean up after ourselves
+		if(pcre_prepared) {
+			pcre_free(pcre_prepared);
+			pcre_prepared = NULL;
+		}	       
+				
+		//Note our error	
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Regular Expression Error expression[%s] error[%s] location[%d]\n", expression, error, error_offset);
+					
+		//We definitely didn't match anything
+		return SWITCH_STATUS_FALSE;
+	}
+			
+	//So far so good, run the regex
+	match_count = pcre_exec(pcre_prepared, NULL, target, (int) strlen(target), 0, 0, offset_vectors, sizeof(offset_vectors) / sizeof(offset_vectors[0]));
+					
+	//Clean up
+	if(pcre_prepared) {
+		pcre_free(pcre_prepared);
+		pcre_prepared = NULL;
+	}
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "number of matches: %d\n", match_count);
+
+	//Was it a match made in heaven?
+	if(match_count > 0) {
+		return SWITCH_STATUS_SUCCESS;
+	} else {
+		return SWITCH_STATUS_FALSE;
+	}
+}
+
+SWITCH_DECLARE(switch_status_t) switch_play_and_get_digits(switch_core_session_t *session,
+	unsigned int min_digits,
+	unsigned int max_digits,
+	unsigned int max_tries,
+	unsigned int timeout,
+	char* valid_terminators,
+	char* prompt_audio_file,
+	char* bad_input_audio_file,
+	void* digit_buffer,
+	unsigned int digit_buffer_length,
+	char* digits_regex) {
+
+	char terminator;	   //used to hold terminator recieved from  
+	switch_channel_t *channel; //the channel contained in session
+	switch_status_t status;    //used to recieve state out of called functions
+
+
+	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "switch_play_and_get_digits(session, %d, %d, %d, %d, %s, %s, %s, digit_buffer, %d, %s)\n", min_digits, max_digits, max_tries, timeout, valid_terminators, prompt_audio_file, bad_input_audio_file, digit_buffer_length, digits_regex);
+
+	//Get the channel
+	channel = switch_core_session_get_channel(session);
+
+	//Make sure somebody is home
+	assert(channel != NULL);
+
+	//Answer the channel if it hasn't already been answered
+	switch_channel_answer(channel);
+
+	//Start pestering the user for input
+	for(;(switch_channel_get_state(channel) == CS_EXECUTE) && max_tries > 0; max_tries--) {
+		//make the buffer so fresh and so clean clean
+		memset(digit_buffer, 0, digit_buffer_length);
+
+		//Play the file
+		status = switch_ivr_play_file(session, NULL, prompt_audio_file, NULL, NULL, digit_buffer, digit_buffer_length);
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "play gave up %s", digit_buffer);
+
+		//Make sure we made it out alive
+		if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+			switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+			break;
+		}
+
+		//we only get one digit out of playback, see if thats all we needed and what we got
+		if(max_digits == 1 && status == SWITCH_STATUS_BREAK) {
+			//Check the digit if we have a regex
+			if(digits_regex != NULL) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking regex [%s] on [%s]\n", digits_regex, digit_buffer);
+
+				//Make sure the digit is allowed
+				if(switch_regex_match(digit_buffer, digits_regex) == SWITCH_STATUS_SUCCESS) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Match found!\n");
+					//jobs done
+					break;
+				} else {
+					//See if a bad input prompt was specified, if so, play it
+					if(strlen(bad_input_audio_file) > 0) {
+						status = switch_ivr_play_file(session, NULL, bad_input_audio_file, NULL, NULL, NULL, 0);
+
+						//Make sure we made it out alive
+						if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+							switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+							break;
+						}
+					}
+				}
+			} else {
+				//jobs done
+				break;
+			}
+		}
+
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Calling more digits try %d\n", max_tries);
+
+		//Try to grab some more digits for the timeout period
+		status = switch_ivr_collect_digits_count(session, digit_buffer, digit_buffer_length, max_digits, valid_terminators, &terminator, timeout);
+
+		//Make sure we made it out alive
+		if(status != SWITCH_STATUS_SUCCESS) {
+			//Bail
+			switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+			break;
+		}
+
+		//see if we got enough
+		if(min_digits <= strlen(digit_buffer)) {
+			//See if we need to test a regex
+			if(digits_regex != NULL) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Checking regex [%s] on [%s]\n", digits_regex, digit_buffer);
+				//Test the regex
+				if(switch_regex_match(digit_buffer, digits_regex) == SWITCH_STATUS_SUCCESS) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Match found!\n");
+					//Jobs done
+					return switch_channel_get_state(channel);
+				} else {
+					//See if a bad input prompt was specified, if so, play it
+					if(strlen(bad_input_audio_file) > 0) {
+						status = switch_ivr_play_file(session, NULL, bad_input_audio_file, NULL, NULL, NULL, 0);
+
+						//Make sure we made it out alive
+						if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+							switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
+							break;
+						}
+					}
+					
+				}
+			} else {
+				//Jobs done
+				return switch_channel_get_state(channel);
+			}
+		}
+	}
+
+	//if we got here, we got no digits or lost the channel
+	memset(digit_buffer, 0, sizeof(digit_buffer));
+	return switch_channel_get_state(channel);
+}
+
+
+
+
 
 
 



More information about the Freeswitch-svn mailing list