[svn-commits] mjordan: branch mjordan/voicemail_refactor_11_10_19 r341860 - in /team/mjorda...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Fri Oct 21 15:14:19 CDT 2011


Author: mjordan
Date: Fri Oct 21 15:14:17 2011
New Revision: 341860

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=341860
Log: (empty)

Added:
    team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/
    team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/
    team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h   (with props)
    team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h   (with props)
    team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/vm_config_parser.c   (with props)
Modified:
    team/mjordan/voicemail_refactor_11_10_19/apps/Makefile

Modified: team/mjordan/voicemail_refactor_11_10_19/apps/Makefile
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/voicemail_refactor_11_10_19/apps/Makefile?view=diff&rev=341860&r1=341859&r2=341860
==============================================================================
--- team/mjordan/voicemail_refactor_11_10_19/apps/Makefile (original)
+++ team/mjordan/voicemail_refactor_11_10_19/apps/Makefile Fri Oct 21 15:14:17 2011
@@ -29,9 +29,13 @@
 
 clean::
 	rm -f confbridge/*.o confbridge/*.i
+	rm -f voicemail/*.o voicemail/*.i
 
 $(if $(filter app_confbridge,$(EMBEDDED_MODS)),modules.link,app_confbridge.so): $(subst .c,.o,$(wildcard confbridge/*.c))
 $(subst .c,.o,$(wildcard confbridge/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,app_confbridge)
+
+$(if $(filter app_voicemail,$(EMBEDDED_MODS)),modules.link,app_voicemail.so): $(subst .c,.o,$(wildcard voicemail/*.c))
+$(subst .c,.o,$(wildcard voicemail/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,app_voicemail)
 
 ifneq ($(findstring $(OSARCH), mingw32 cygwin ),)
   LIBS+= -lres_features.so -lres_ael_share.so -lres_monitor.so -lres_speech.so

Added: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h?view=auto&rev=341860
==============================================================================
--- team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h (added)
+++ team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h Fri Oct 21 15:14:17 2011
@@ -1,0 +1,226 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2011, Digium, Inc.
+ *
+ * Mark Spencer <markster at digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*!
+ * \file
+ * \author Matt Jordan <mjordan at digium.com>
+ * \brief Comedian Mail - Voicemail System
+ *
+ */
+
+#ifndef VOICEMAIL_H_
+#define VOICEMAIL_H_
+
+#include "asterisk.h"
+#include "asterisk/channel.h"
+
+
+#define MAX_ADSI_FIELDS 4
+
+#define MAX_DTMF_LENGTH 16
+
+#define MAX_FORMAT_LENGTH 64
+
+#define MAX_NUM_CID_CONTEXTS 10
+
+enum vm_global_flags {
+	VM_REVIEW		 = (1 << 0),   /*!< After recording, permit the caller to review the recording before saving */
+	VM_OPERATOR      = (1 << 1),   /*!< Allow 0 to be pressed to go to 'o' extension */
+	VM_SAYCID        = (1 << 2),   /*!< Repeat the CallerID info during envelope playback */
+	VM_SVMAIL        = (1 << 3),   /*!< Allow the user to compose a new VM from within VoicemailMain */
+	VM_ENVELOPE      = (1 << 4),   /*!< Play the envelope information (who-from, time received, etc.) */
+	VM_SAYDURATION   = (1 << 5),   /*!< Play the length of the message during envelope playback */
+	VM_SKIPAFTERCMD  = (1 << 6),   /*!< After deletion, assume caller wants to go to the next message */
+	VM_FORCENAME     = (1 << 7),   /*!< Have new users record their name */
+	VM_FORCEGREET    = (1 << 8),   /*!< Have new users record their greetings */
+	VM_PBXSKIP       = (1 << 9),   /*!< Skip the [PBX] preamble in the Subject line of emails */
+	VM_DIRECFORWARD  = (1 << 10),  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
+	VM_ATTACH        = (1 << 11),  /*!< Attach message to voicemail notifications? */
+	VM_DELETE        = (1 << 12),  /*!< Delete message after sending notification */
+	VM_ALLOCED       = (1 << 13),  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
+	VM_SEARCH        = (1 << 14),  /*!< Search all contexts for a matching mailbox */
+	VM_TEMPGREETWARN = (1 << 15),  /*!< Remind user tempgreeting is set */
+	VM_MOVEHEARD     = (1 << 16),  /*!< Move a "heard" message to Old after listening to it */
+	VM_MESSAGEWRAP   = (1 << 17),  /*!< Wrap around from the last message to the first, and vice-versa */
+	VM_FWDURGAUTO    = (1 << 18),  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
+	VM_POLLMAILBOXES = (1 << 19)   /*!< If set, poll mailboxes for changes */
+};
+
+enum vm_passwordlocation {
+	VM_OPT_PWLOC_VOICEMAILCONF = 0,   /*!< The password should be obtained from voicemail.conf */
+	VM_OPT_PWLOC_SPOOLDIR      = 1,   /*!< The password should be obtained from secret.conf, in the user's spool directory */
+	VM_OPT_PWLOC_USERSCONF     = 2,   /*!< The password should be obtained from users.conf */
+};
+
+enum vm_password_change_flags {
+	VM_OPT_PWCHG_INTERNAL = (1 << 0),
+	VM_OPT_PWCHG_EXTERNAL = (1 << 1),
+};
+
+struct vm_sound_config {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(vm_password);
+		AST_STRING_FIELD(vm_newpassword);
+		AST_STRING_FIELD(vm_invalid_password);
+		AST_STRING_FIELD(vm_password_changed);
+		AST_STRING_FIELD(vm_reenter_password);
+		AST_STRING_FIELD(vm_mismatch);
+		AST_STRING_FIELD(vm_pls_try_again);
+	);
+};
+
+struct vm_listen_control_config {
+	char listen_control_forward_key[MAX_DTMF_LENGTH];
+	char listen_control_reverse_key[MAX_DTMF_LENGTH];
+	char listen_control_pause_key[MAX_DTMF_LENGTH];
+	char listen_control_restart_key[MAX_DTMF_LENGTH];
+	char listen_control_stop_key[MAX_DTMF_LENGTH];
+};
+
+struct vm_smdi_config {
+	unsigned int enabled;
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(smdi_interface);
+	);
+};
+
+struct vm_adsi_config {
+	unsigned char adsifdn[MAX_ADSI_FIELDS];
+	unsigned char adsisec[MAX_ADSI_FIELDS];
+	unsigned int adsiver;
+};
+
+/*! Voicemail time zones */
+struct vm_timezone {
+	AST_LIST_ENTRY(vm_zone) list;
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(name);					/*!< The name of the timezone */
+		AST_STRING_FIELD(timezone);				/*!< The timezone code */
+		AST_STRING_FIELD(msg_format);			/*!< Formatting code for the timezone */
+	);
+};
+
+struct ast_vm_user {
+	char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
+	char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
+	char language[MAX_LANGUAGE];     /*!< Config: Language setting */
+	char callback[AST_MAX_CONTEXT];				 /*!< Callback context */
+	char dialout[AST_MAX_CONTEXT];				 /*!< Dialout context */
+	char uniqueid[80];               /*!< Unique integer identifier TODO: make this an integer? */
+	char exit[AST_MAX_CONTEXT];					 /*!< Context to exit out to */
+	char attachfmt[MAX_FORMAT_LENGTH];              /*!< Attachment format */
+	int min_say_duration;				 /*!< Minimum number of seconds for say */
+	int min_message_duration;                     /*!< Minimum number of seconds per message for this mailbox */
+	int max_messages;                      /*!< Maximum number of msgs per folder for this mailbox */
+	int max_deleted_messages;               /*!< Maximum number of deleted msgs saved for this mailbox */
+	int max_message_duration;                     /*!< Maximum number of seconds per message for this mailbox */
+	int hash_key;
+	enum vm_passwordlocation password_location;           /*!< Storage location of the password */
+	double volgain;                  /*!< Volume gain for voicemails sent via email */
+	struct ast_flags flags;              /*!< VM_ flags */
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(fullname);
+		AST_STRING_FIELD(password);
+		AST_STRING_FIELD(email_address);
+		AST_STRING_FIELD(email_subject);
+		AST_STRING_FIELD(email_body);
+		AST_STRING_FIELD(pager_address);
+		AST_STRING_FIELD(pager_subject);
+		AST_STRING_FIELD(pager_body);
+		AST_STRING_FIELD(email_from_address);
+		AST_STRING_FIELD(mail_command);
+		AST_STRING_FIELD(zonetag);
+		AST_STRING_FIELD(timezone);
+	);
+};
+
+struct vm_config {
+	double volgain;
+	unsigned int max_silence;
+	unsigned int max_deleted_messages;
+	unsigned int max_messages;
+	unsigned int min_say_duration;
+	unsigned int min_message_duration;
+	unsigned int max_message_duration;
+	unsigned int max_message_greeting_length;
+	unsigned int skip_ms;
+	unsigned int min_password_length;
+	unsigned int silence_threshold;
+	unsigned int poll_frequency;
+	unsigned int max_logins;
+	enum vm_passwordlocation password_location;
+	char attach_format[MAX_FORMAT_LENGTH];
+	char format[MAX_FORMAT_LENGTH];
+	char dial_out_context[AST_MAX_CONTEXT];
+	char callback_context[AST_MAX_CONTEXT];
+	char op_exit_context[AST_MAX_CONTEXT];
+	char default_users_context[AST_MAX_CONTEXT];
+	char cid_internal_contexts[MAX_NUM_CID_CONTEXTS][AST_MAX_CONTEXT];
+	char extern_notify_cmd[PATH_MAX];
+	char extern_pass_cmd[PATH_MAX];
+	char extern_pass_check_cmd[PATH_MAX];
+	char directory_intro_path[PATH_MAX];
+	struct vm_sound_config * sound_config;
+	struct vm_listen_control_config * listen_control_config;
+	struct vm_smdi_config * smdi_config;
+	struct vm_adsi_config * adsi_config;
+	struct ast_flags globalflags;
+	struct ast_flags passwordflags;
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(mail_command);
+		AST_STRING_FIELD(email_from_string);
+		AST_STRING_FIELD(email_server);
+		AST_STRING_FIELD(email_date_format);
+		AST_STRING_FIELD(email_subject);
+		AST_STRING_FIELD(email_body);
+		AST_STRING_FIELD(pager_from_string);
+		AST_STRING_FIELD(pager_date_format);
+		AST_STRING_FIELD(pager_subject);
+		AST_STRING_FIELD(pager_body);
+		AST_STRING_FIELD(zonetag);
+		AST_STRING_FIELD(locale);
+		AST_STRING_FIELD(charset);
+	);
+};
+
+/*! \brief Obtain a reference to the current voicemail configuration
+ *
+ * \note The vm_config object is a ref counted object.  Calling this method automatically
+ * increases that reference count by 1 - when finished with the object, the caller of this
+ * method must decrease the reference count.
+ *
+ * \note The voicemail configuration must be loaded before calling this method.  See
+ * vm_get_config(void).
+ *
+ * \returns Pointer to the voicemail configuration object
+ */
+struct vm_config *vm_get_config(void);
+
+/*! \brief Load the voicemail configuration, the users, and timezones
+ * \param reload If 0, load the configuration for the first time.  If 1, reload the configuration.
+ *
+ * \returns 0 on success
+ * \returns 1 on error during load
+ */
+int vm_load_config(int reload);
+
+struct ast_vm_user *vm_get_user(const char *context, const char *mailbox);
+
+struct vm_timezone *vm_get_timezone(const char *timezone);
+
+#endif /* VOICEMAIL_H_ */

Propchange: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h?view=auto&rev=341860
==============================================================================
--- team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h (added)
+++ team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h Fri Oct 21 15:14:17 2011
@@ -1,0 +1,59 @@
+/*
+ * voicemail_test.h
+ *
+ *  Created on: Oct 21, 2011
+ *      Author: mjordan
+ */
+
+#ifndef TEST_VOICEMAIL_H_
+#define TEST_VOICEMAIL_H_
+
+#include "asterisk.h"
+#include "asterisk/test.h"
+
+#define VM_TEST_MEM_EQUAL(expected, actual, upperlimit) \
+	do { \
+	  int i = 0; \
+	  for (; i < upperlimit; i++) { \
+		  if (memcmp(&(expected[i]), &(actual[i]), 1)) { \
+			  ast_test_status_update(test, "Test failed for parameter %s: Expected [%02X], Actual [%02X] (Position %d)\n", #actual, expected[i], actual[i], i); \
+			  result = AST_TEST_FAIL; \
+			  break; \
+		  }}} while (0)
+
+#define VM_TEST_STRING_EQUAL(expected, actual) \
+	do { \
+		if (strncmp(expected, actual, sizeof(expected))) { \
+			ast_test_status_update(test, "Test failed for parameter %s: Expected [%s], Actual [%s]\n", #actual, expected, actual); \
+			result = AST_TEST_FAIL; \
+		} } while (0)
+
+#define VM_TEST_STRING_NOT_EQUAL(expected, actual) \
+	do { \
+		if (!strncmp(expected, actual, sizeof(expected))) { \
+			ast_test_status_update(test, "Test failed for parameter %s: Expected NOT [%s], Actual [%s]\n", #actual, expected, actual); \
+			result = AST_TEST_FAIL; \
+		} } while (0)
+
+#define VM_TEST_NUMERIC_NOT_EQUAL(expected, actual) \
+	do { \
+		if (expected == actual) { \
+			ast_test_status_update(test, "Test failed for parameter %s: Expected NOT [%d], Actual [%d]\n", #actual, (int)expected, (int)actual); \
+			result = AST_TEST_FAIL; \
+		} } while (0)
+
+#define VM_TEST_NUMERIC_EQUAL(expected, actual) \
+	do { \
+		if (expected != actual) { \
+			ast_test_status_update(test, "Test failed for parameter %s: Expected [%d], Actual [%d]\n", #actual, (int)expected, (int)actual); \
+			result = AST_TEST_FAIL; \
+		} } while (0)
+
+void vm_register_config_tests(void);
+
+void vm_unregister_config_tests(void);
+
+/* TODO: add other backends / test entry points */
+
+#endif /* TEST_VOICEMAIL_H_ */
+

Propchange: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/include/voicemail_test.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/vm_config_parser.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/vm_config_parser.c?view=auto&rev=341860
==============================================================================
--- team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/vm_config_parser.c (added)
+++ team/mjordan/voicemail_refactor_11_10_19/apps/voicemail/vm_config_parser.c Fri Oct 21 15:14:17 2011
@@ -1,0 +1,1746 @@
+
+#include "asterisk.h"
+
+#include "asterisk/lock.h"
+#include "asterisk/paths.h"
+#include "asterisk/dsp.h"
+#include "include/voicemail.h"
+#include "include/voicemail_test.h"
+
+#define VOICEMAIL_CONFIG "voicemail.conf"
+#define USER_CONFIG "users.conf"
+#define SECRET_CONFIG "secret.conf"
+
+/* Forward declarations */
+static int timezone_hash_fn(const void *obj, const int flags);
+static int timezone_cmp_fn(void *obj, void *arg, int flags);
+static int voicemail_user_hash_fn(const void *obj, const int flags);
+static int voicemail_user_cmp_fn(void *obj, void *arg, int flags);
+static int init_configuration(void);
+static int is_valid_dtmf(const char *key);
+static int load_voicemail_options(struct vm_config *vmcfg, struct ast_config *cfg);
+static int load_timezones(const struct vm_config * const vmcfg, struct ast_config *cfg);
+static int load_voicemail_users(const struct vm_config * const vmcfg, struct ast_config *ucfg, int load_from_variable);
+static void populate_default_vm_config_options(struct vm_config *vmcfg);
+static void populate_default_listen_control(struct vm_config *vmcfg);
+static void populate_default_sounds(struct vm_config *vmcfg);
+static void populate_default_adsi(struct vm_config *vmcfg);
+static void populate_default_user_options(const struct vm_config * const vmcfg, struct ast_vm_user *vmu);
+static void populate_vmu_from_variable(const struct vm_config * const vmcfg, struct ast_vm_user *vmu, const char *context, struct ast_variable *var);
+static void populate_vmu_from_category(const struct vm_config * const vmcfg, struct ast_vm_user *vmu, struct ast_config *ucfg, const char *cat);
+static void apply_user_option(const struct vm_config * const vmcfg, struct ast_vm_user *vmu, const char *name, const char *value);
+static int validate_user(const struct vm_config * const vmcfg, struct ast_vm_user *vmu);
+static struct vm_config * voicemail_config_new(void);
+static void voicemail_config_destroy(void *obj);
+static struct ast_vm_user *voicemail_user_new(void);
+static void voicemail_user_destroy(void *obj);
+static struct vm_timezone *timezone_new(void);
+static void timezone_destroy(void *obj);
+
+/* custom password sounds */
+static const char *default_sound_vm_password = "vm-password";
+static const char *default_sound_vm_newpassword = "vm-newpassword";
+static const char *default_sound_vm_password_changed = "vm-passchanged";
+static const char *default_sound_vm_reenter_password = "vm-reenterpassword";
+static const char *default_sound_vm_mismatch = "vm-mismatch";
+static const char *default_sound_vm_invalid_password = "vm-invalid-password";
+static const char *default_sound_vm_pls_try_again = "vm-pls-try-again";
+
+/* DTMF control defaults */
+static const char *default_listen_control_forward_key = "#";
+static const char *default_listen_control_reverse_key = "*";
+static const char *default_listen_control_pause_key = "0";
+static const char *default_listen_control_restart_key = "2";
+static const char *default_listen_control_stop_key = "13456789";
+static const char *valid_dtmf = "1234567890*#ABCD";
+
+/*static const char *default_character_set = "ISO-8859-1";*/
+static const char *default_general_context = "general";
+static const char *default_timezone_context = "zonemessages";
+/*static const char *default_smdi_interface = "/dev/ttyS0";*/
+static const char *default_format_string = "wav";
+static const char *default_asterisk_username = "asterisk";
+static const char *default_users_context = "default";
+static char default_email_date_format[32] = "%A, %B %d, %Y at %r";
+static char default_pager_date_format[32] = "%A, %B %d, %Y at %r";
+static const char *default_mail_command = "/usr/sbin/sendmail -t";
+
+static const unsigned int default_skip_ms = 3000;
+static const unsigned int default_max_logins = 3;
+static const unsigned int default_min_say_duration = 2;
+static const unsigned int default_poll_frequency = 30;
+static const unsigned int default_max_messages = 100;
+static const unsigned int default_min_password_length = 0;
+static const unsigned int default_max_messages_limit = 9999;
+
+static unsigned char default_adsifdn[4] = "\x00\x00\x00\x0F";
+static unsigned char default_adsisec[4] = "\x9B\xDB\xF7\xAC";
+static unsigned int default_adsiver = 1;
+
+/*! \internal \brief The base directory for voicemail user's files */
+static char voicemail_spool_directory[PATH_MAX];
+
+/*! \internal \brief The voicemail configuration object */
+static struct vm_config *config = NULL;
+
+/*! \internal \brief The timezones container */
+static struct ao2_container *timezones = NULL;
+
+/*! \internal \brief The voicemail users container */
+static struct ao2_container *voicemail_users = NULL;
+
+/*! \internal \brief The mutex that protects access to the config object */
+AST_MUTEX_DEFINE_STATIC(config_lock);
+
+struct ast_vm_user *vm_get_user(const char *context, const char *mailbox)
+{
+	struct ast_vm_user *tmp_vmu;
+	struct ast_vm_user *vmu = NULL;
+
+	if (!(tmp_vmu = voicemail_user_new())) {
+		ast_log(AST_LOG_ERROR, "Failed to allocate sufficient memory for ast_vm_user object\n");
+	} else {
+		ast_copy_string(tmp_vmu->context, context, sizeof(tmp_vmu->context));
+		ast_copy_string(tmp_vmu->mailbox, mailbox, sizeof(tmp_vmu->mailbox));
+		vmu = ao2_find(voicemail_users, tmp_vmu, OBJ_POINTER);
+		ao2_ref(tmp_vmu, -1);
+	}
+
+	return vmu;
+}
+
+struct vm_timezone *vm_get_timezone(const char *timezone)
+{
+	struct vm_timezone *tmp_tmz;
+	struct vm_timezone *tmz = NULL;
+
+	if (!(tmp_tmz = timezone_new())) {
+		ast_log(AST_LOG_ERROR, "Failed to allocate sufficient memory for vm_timezone object\n");
+	} else {
+		ast_string_field_set(tmp_tmz, name, timezone);
+		tmz = ao2_find(timezones, tmp_tmz, OBJ_POINTER);
+		ao2_ref(tmz, -1);
+	}
+
+	return tmz;
+}
+
+struct vm_config *vm_get_config(void)
+{
+	struct vm_config *vmcfg;
+
+	ast_mutex_lock(&config_lock);
+	if (!config) {
+		ast_log(AST_LOG_WARNING, "Attempted to retrieve NULL voicemail configuration object\n");
+		vmcfg = NULL;
+	} else {
+		ao2_ref(config, 1);
+		vmcfg = config;
+	}
+	ast_mutex_unlock(&config_lock);
+
+	return vmcfg;
+}
+
+int vm_load_config(int reload)
+{
+	int res = 0;
+	struct vm_config *vmcfg = NULL;
+	struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
+	struct ast_config *cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
+	struct ast_config *ucfg = ast_config_load(USER_CONFIG, config_flags);
+
+	if (!(vmcfg = voicemail_config_new())) {
+		ast_log(AST_LOG_ERROR, "Failed to allocate sufficient memory for vm_config object\n");
+		return 1;
+	}
+
+	if (!reload) {
+		if (init_configuration()) {
+			return 1;
+		}
+	}
+
+	/* If both configs are unchanged exit; otherwise load both */
+	if (cfg == CONFIG_STATUS_FILEUNCHANGED && ucfg == CONFIG_STATUS_FILEUNCHANGED) {
+		return 0;
+	} else if (cfg == CONFIG_STATUS_FILEUNCHANGED && ucfg != CONFIG_STATUS_FILEUNCHANGED) {
+		ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+		cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
+	} else if (cfg != CONFIG_STATUS_FILEUNCHANGED && ucfg == CONFIG_STATUS_FILEUNCHANGED) {
+		ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
+		ucfg = ast_config_load(USER_CONFIG, config_flags);
+	}
+
+	if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) {
+		if (ucfg && ucfg != CONFIG_STATUS_FILEINVALID) {
+			ast_config_destroy(ucfg);
+		}
+		ast_log(AST_LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
+		return 1;
+	}
+
+	if (ucfg == CONFIG_STATUS_FILEINVALID) {
+		ast_log(AST_LOG_WARNING, "Config file " USER_CONFIG " is in an invalid format.  Avoiding.\n");
+		ucfg = NULL;
+	}
+
+	populate_default_vm_config_options(vmcfg);
+
+	if (load_voicemail_options(vmcfg, cfg)) {
+		ast_log(AST_LOG_ERROR, "Failed to parse voicemail configuration\n");
+		ast_config_destroy(cfg);
+		ast_config_destroy(ucfg);
+		return 1;
+	}
+
+	/* From here on, the loading becomes destructive to the configuration that was previously loaded */
+	res |= load_timezones(vmcfg, cfg);
+
+	res |= load_voicemail_users(vmcfg, cfg, 1);
+	/* If a user config exists, load users from there as well */
+	if (ucfg) {
+		res |= load_voicemail_users(vmcfg, ucfg, 0);
+	}
+
+	/* Set the global config object to the one we just created */
+	ast_mutex_lock(&config_lock);
+	if (reload) {
+		ao2_ref(config, -1);
+	}
+	config = vmcfg;
+	ast_mutex_unlock(&config_lock);
+
+	ast_config_destroy(cfg);
+	ast_config_destroy(ucfg);
+
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief Hash function used for timezones
+ */
+static int timezone_hash_fn(const void *obj, const int flags)
+{
+	const struct vm_timezone *tmz = obj;
+	return ast_str_case_hash(tmz->name);
+}
+
+/*!
+ * \internal
+ * \brief Comparison function used for timezones
+ */
+static int timezone_cmp_fn(void *obj, void *arg, int flags)
+{
+	const struct vm_timezone *tmz_left = obj;
+	const struct vm_timezone *tmz_right = arg;
+
+	return (!strcasecmp(tmz_left->name, tmz_right->name) ? CMP_MATCH | CMP_STOP : 0);
+}
+
+/*!
+ * \internal
+ * \brief Hash function used for voicemail users
+ */
+static int voicemail_user_hash_fn(const void *obj, const int flags)
+{
+	const struct ast_vm_user *vmu = obj;
+	return vmu->hash_key;
+}
+
+/*!
+ * \internal
+ * \brief Comparison function used for voicemail users
+ */
+static int voicemail_user_cmp_fn(void *obj, void *arg, int flags)
+{
+	const struct ast_vm_user *vmu_left = obj;
+	const struct ast_vm_user *vmu_right = arg;
+
+	if (vmu_left->hash_key == vmu_right->hash_key) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Determines if a DTMF key entered is valid.
+ * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
+ *
+ * Tests the character entered against the set of valid DTMF characters.
+ * \return 1 if the character entered is a valid DTMF digit
+ * \return 0 if the character is invalid.
+ */
+static int is_valid_dtmf(const char *key)
+{
+	int i;
+	char *local_key = ast_strdupa(key);
+
+	for (i = 0; i < strlen(key); ++i) {
+		if (!strchr(valid_dtmf, *local_key)) {
+			ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" detected\n", *local_key);
+			return 0;
+		}
+		local_key++;
+	}
+	return 1;
+}
+
+/*! \internal \brief Initialize the containers and other objects needed for loading the configuration
+ * \returns 1 on failure
+ * \returns 0 on success
+ */
+static int init_configuration(void)
+{
+	snprintf(voicemail_spool_directory, sizeof(voicemail_spool_directory), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
+
+	if (!(timezones = ao2_container_alloc(283, timezone_hash_fn, timezone_cmp_fn))) {
+		ast_log(AST_LOG_ERROR, "Failed to allocate sufficient memory for timezones container\n");
+		return 1;
+	}
+	if (!(voicemail_users = ao2_container_alloc(283, voicemail_user_hash_fn, voicemail_user_cmp_fn))) {
+		ast_log(AST_LOG_ERROR, "Failed to allocate sufficient memory for voicemail users container\n");
+		return 1;
+	}
+	return 0;
+}
+
+/*! \internal \brief Load the voicemail configuration options
+ * \param vmcfg The voicemail configuration object to populate
+ * \param cfg The Asterisk configuration object containing the values
+ * \returns 1 on failure
+ * \returns 0 on success
+ */
+static int load_voicemail_options(struct vm_config *vmcfg, struct ast_config *cfg)
+{
+	struct ast_variable *var;
+	int cidcounter = 0;
+	char * tempcontext;
+	char * tempvalue;
+	int i;
+	int tmp_adsi_buffer[MAX_ADSI_FIELDS];
+	struct ast_category *cat = ast_category_get(cfg, default_general_context);
+
+	if (!cat) {
+		ast_log(AST_LOG_ERROR, "No %s category defined in voicemail configuration\n", default_general_context);
+		return 1;
+	}
+
+	for (var = ast_category_first(cat); var; var = var->next) {
+		ast_debug(5, "Parsing voicemail configuration parameter [%s]: value [%s]", var->name, var->value);
+		if (!strcasecmp(var->name, "userscontext")) {
+			ast_copy_string(vmcfg->default_users_context, var->value, sizeof(vmcfg->default_users_context));
+		} else if (!strcasecmp(var->name, "attachfmt")) {
+			ast_copy_string(vmcfg->attach_format, var->value, sizeof(vmcfg->attach_format));
+		} else if (!strcasecmp(var->name, "directoryintro")) {
+			ast_copy_string(vmcfg->directory_intro_path, var->value, sizeof(vmcfg->directory_intro_path));
+		} else if (!strcasecmp(var->name, "attach")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_ATTACH);
+		} else if (!strcasecmp(var->name, "searchcontexts")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_SEARCH);
+		} else if (!strcasecmp(var->name, "volgain")) {
+			if (sscanf(var->value, "%30lf", &(vmcfg->volgain)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter volgain\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "mailcmd")) {
+			ast_string_field_set(vmcfg, mail_command, var->value);
+		} else if (!strcasecmp(var->name, "maxsilence")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->max_silence)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter max_silence\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "maxmsg")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->max_messages)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter max_messages\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "backupdeleted")) {
+			/* Field can be either an absolute number of messages to delete, or a 'yes' / 'no' value */
+			if (sscanf(var->value, "%30ud", &(vmcfg->max_deleted_messages)) < 1) {
+				if (ast_true(var->value)) {
+					vmcfg->max_deleted_messages = default_max_messages;
+				} else {
+					vmcfg->max_deleted_messages = 0;
+				}
+			}
+		} else if (!strcasecmp(var->name, "emaildateformat")) {
+			ast_string_field_set(vmcfg, email_date_format, var->value);
+		} else if (!strcasecmp(var->name, "emailsubject")) {
+			ast_string_field_set(vmcfg, email_subject, var->value);
+		} else if (!strcasecmp(var->name, "emailbody")) {
+			ast_string_field_set(vmcfg, email_body, var->value);
+		} else if (!strcasecmp(var->name, "fromstring")) {
+			ast_string_field_set(vmcfg, email_from_string, var->value);
+		} else if (!strcasecmp(var->name, "charset")) {
+			ast_string_field_set(vmcfg, charset, var->value);
+		} else if (!strcasecmp(var->name, "tz")) {
+			ast_string_field_set(vmcfg, zonetag, var->value);
+		} else if (!strcasecmp(var->name, "locale")) {
+			ast_string_field_set(vmcfg, locale, var->value);
+		} else if (!strcasecmp(var->name, "pagerdateformat")) {
+			ast_string_field_set(vmcfg, pager_date_format, var->value);
+		} else if (!strcasecmp(var->name, "pagerfromstring")) {
+			ast_string_field_set(vmcfg, pager_from_string, var->value);
+		} else if (!strcasecmp(var->name, "pagersubject")) {
+			ast_string_field_set(vmcfg, pager_subject, var->value);
+		} else if (!strcasecmp(var->name, "pagerbody")) {
+			ast_string_field_set(vmcfg, pager_body, var->value);
+		} else if (!strcasecmp(var->name, "externpass")) {
+			ast_copy_string(vmcfg->extern_pass_cmd, var->value, sizeof(vmcfg->extern_pass_cmd));
+			ast_set2_flag(&(vmcfg->passwordflags), 1, VM_OPT_PWCHG_EXTERNAL);
+			ast_set2_flag(&(vmcfg->passwordflags), 0, VM_OPT_PWCHG_INTERNAL);
+		} else if (!strcasecmp(var->name, "externpassnotify")) {
+			ast_copy_string(vmcfg->extern_pass_cmd, var->value, sizeof(vmcfg->extern_pass_cmd));
+			ast_set2_flag(&(vmcfg->passwordflags), 1, VM_OPT_PWCHG_EXTERNAL);
+			ast_set2_flag(&(vmcfg->passwordflags), 1, VM_OPT_PWCHG_INTERNAL);
+		} else if (!strcasecmp(var->name, "externpasscheck")) {
+			ast_copy_string(vmcfg->extern_pass_check_cmd, var->value, sizeof(vmcfg->extern_pass_check_cmd));
+		} else if (!strcasecmp(var->name, "externnotify")) {
+			ast_copy_string(vmcfg->extern_notify_cmd, var->value, sizeof(vmcfg->extern_notify_cmd));
+		} else if (!strcasecmp(var->name, "silencethreshold")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->silence_threshold)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter silence_threshold\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "serveremail")) {
+			ast_string_field_set(vmcfg, email_server, var->value);
+		} else if (!strcasecmp(var->name, "maxsecs")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->max_message_duration)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter max_message_duration\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "maxmessage")) {
+			static int maxmessage_deprecate = 0;
+			if (!maxmessage_deprecate) {
+				maxmessage_deprecate = 1;
+				ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
+			}
+			if (sscanf(var->value, "%30ud", &(vmcfg->max_message_duration)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter max_message_duration\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "minsecs")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->min_message_duration)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter min_message_duration\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "minmessage")) {
+			static int minmessage_deprecate = 0;
+			if (!minmessage_deprecate) {
+				minmessage_deprecate = 1;
+				ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
+			}
+			if (sscanf(var->value, "%30ud", &(vmcfg->min_message_duration)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter min_message_duration\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "format")) {
+			ast_copy_string(vmcfg->format, var->value, sizeof(vmcfg->format));
+		} else if (!strcasecmp(var->name, "maxgreet")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->max_message_greeting_length)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter max_message_greeting_length\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "skipms")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->skip_ms)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter skip_ms\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "maxlogins")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->max_logins)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter max_logins\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "minpassword")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->min_password_length)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter min_password_length\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "forcename")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_FORCENAME);
+		} else if (!strcasecmp(var->name, "forcegreetings")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_FORCEGREET);
+		} else if (!strcasecmp(var->name, "cidinternalcontexts")) {
+			tempvalue = ast_strdupa(var->value);
+			if (!ast_strlen_zero(tempvalue)) {
+				while ((tempcontext = strsep(&tempvalue, ","))) {
+					tempcontext = ast_strip(tempcontext);
+					if (cidcounter >= MAX_NUM_CID_CONTEXTS) {
+						ast_log(AST_LOG_WARNING, "CID Context #%d [%s] ignored; a maximum of %d contexts are supported\n", cidcounter, tempcontext, MAX_NUM_CID_CONTEXTS);
+					} else {
+						ast_copy_string(vmcfg->cid_internal_contexts[cidcounter], tempcontext, sizeof(vmcfg->cid_internal_contexts[cidcounter]));
+					}
+					++cidcounter;
+				}
+			}
+		} else if (!strcasecmp(var->name, "review")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_REVIEW);
+		} else if (!strcasecmp(var->name, "tempgreetwarn")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_TEMPGREETWARN);
+		} else if (!strcasecmp(var->name, "messagewrap")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_MESSAGEWRAP);
+		} else if (!strcasecmp(var->name, "operator")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_OPERATOR);
+		} else if (!strcasecmp(var->name, "saycid")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_SAYCID);
+		} else if (!strcasecmp(var->name, "sendvoicemail")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_SVMAIL);
+		} else if (!strcasecmp(var->name, "envelope")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_ENVELOPE);
+		} else if (!strcasecmp(var->name, "moveheard")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_MOVEHEARD);
+		} else if (!strcasecmp(var->name, "forward_urgent_auto")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_FWDURGAUTO);
+		} else if (!strcasecmp(var->name, "sayduration")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_SAYDURATION);
+		} else if (!strcasecmp(var->name, "saydurationm")) {
+			if (sscanf(var->value, "%30ud", &vmcfg->min_say_duration) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter min_say_duration\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "nextaftercmd")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_SKIPAFTERCMD);
+		} else if (!strcasecmp(var->name, "dialout")) {
+			ast_copy_string(vmcfg->dial_out_context, var->value, sizeof(vmcfg->dial_out_context));
+		} else if (!strcasecmp(var->name, "callback")) {
+			ast_copy_string(vmcfg->callback_context, var->value, sizeof(vmcfg->callback_context));
+		} else if (!strcasecmp(var->name, "exitcontext")) {
+			ast_copy_string(vmcfg->op_exit_context, var->value, sizeof(vmcfg->op_exit_context));
+		} else if (!strcasecmp(var->name, "usedirectory")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_DIRECFORWARD);
+		} else if (!strcasecmp(var->name, "pbxskip")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_PBXSKIP);
+		}else if (!strcasecmp(var->name, "passwordlocation")) {
+			if (!strcasecmp(var->value, "voicemail.conf")) {
+				vmcfg->password_location = VM_OPT_PWLOC_VOICEMAILCONF;
+			} else if (!strcasecmp(var->value, "spooldir")) {
+				vmcfg->password_location = VM_OPT_PWLOC_SPOOLDIR;
+			} else {
+				ast_log(AST_LOG_WARNING, "Unsupported password location [%s], using default of voicemail.conf\n", var->value);
+				vmcfg->password_location = VM_OPT_PWLOC_VOICEMAILCONF;
+			}
+		} else if (!strcasecmp(var->name, "pollfreq")) {
+			if (sscanf(var->value, "%30ud", &(vmcfg->poll_frequency)) < 1) {
+				ast_log(AST_LOG_WARNING, "Failed to parse %s with value %s into parameter poll_frequency\n", var->name, var->value);
+			}
+		} else if (!strcasecmp(var->name, "pollmailboxes")) {
+			ast_set2_flag(&(vmcfg->globalflags), ast_true(var->value), VM_POLLMAILBOXES);
+		} else if (!strcasecmp(var->name, "vm-password")) {
+			ast_string_field_set(vmcfg->sound_config, vm_password, var->value);
+		} else if (!strcasecmp(var->name, "vm-newpassword")) {
+			ast_string_field_set(vmcfg->sound_config, vm_newpassword, var->value);
+		} else if (!strcasecmp(var->name, "vm-invalid-password")) {
+			ast_string_field_set(vmcfg->sound_config, vm_invalid_password, var->value);
+		} else if (!strcasecmp(var->name, "vm-passchanged")) {
+			ast_string_field_set(vmcfg->sound_config, vm_password_changed, var->value);
+		} else if (!strcasecmp(var->name, "vm-reenterpassword")) {
+			ast_string_field_set(vmcfg->sound_config, vm_reenter_password, var->value);
+		} else if (!strcasecmp(var->name, "vm-mismatch")) {
+			ast_string_field_set(vmcfg->sound_config, vm_mismatch, var->value);
+		} else if (!strcasecmp(var->name, "vm-pls-try-again")) {
+			ast_string_field_set(vmcfg->sound_config, vm_pls_try_again, var->value);
+		} else if (!strcasecmp(var->name, "listen-control-forward-key")) {
+			if (is_valid_dtmf(var->value)) {
+				ast_copy_string(vmcfg->listen_control_config->listen_control_forward_key, var->value, sizeof(vmcfg->listen_control_config->listen_control_forward_key));
+			}
+		} else if (!strcasecmp(var->name, "listen-control-reverse-key")) {
+			if (is_valid_dtmf(var->value)) {

[... 1221 lines stripped ...]



More information about the svn-commits mailing list