Index: res/res_config_odbc.c
===================================================================
RCS file: /usr/cvsroot/asterisk/res/res_config_odbc.c,v
retrieving revision 1.4
diff -u -r1.4 res_config_odbc.c
--- res/res_config_odbc.c	8 Jul 2004 20:16:28 -0000	1.4
+++ res/res_config_odbc.c	8 Jul 2004 22:57:49 -0000
@@ -96,7 +96,7 @@
 	SQLBindCol (stmt, 7, SQL_C_CHAR, &var_name, sizeof (var_name), &err);
 	SQLBindCol (stmt, 8, SQL_C_CHAR, &var_val, sizeof (var_val), &err);
 
-	sprintf (sql, "select * from %s where filename='%s' and commented=0 order by filename,cat_metric desc,var_metric asc,id", table, file);
+	sprintf (sql, "select id, cat_metric, var_metric, commented, filename, category, var_name, var_val from %s where filename='%s' and commented=0 order by filename,cat_metric desc,var_metric asc,id", table, file);
 	res = SQLExecDirect (stmt, sql, SQL_NTS);
 
 	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
@@ -172,6 +172,136 @@
 
 }
 
+/* these functions are dl'd by app_voicemail */
+int config_odbc_get_cfg_id(char *, char *, char *);
+int config_odbc_update_cfg_by_id(int, char *);
+/*
+ * Ugh, everytime these functions run, it shows that the config is getting
+ * loaded, not a desired effect at all.  :-(
+ *
+ * Short of parsing the config file manually, or saving the values to a
+ * global, I don't know how to retrieve the config values.
+ *
+ * Fortunately, voicemail password changes are far and few.
+ */
+int config_odbc_get_cfg_id(char *file, char *category, char *variable)
+{
+	int rowid = -1;
+	int configured = 0;
+	struct ast_config *config;
+	struct ast_variable *v;
+	char connection[128];
+	char table[128];
+	odbc_obj *obj;
+	char sql[255];
+	SQLHSTMT stmt;
+	SQLSMALLINT rowcount;
+	SQLINTEGER err;
+	int res;
+
+	config = ast_load("res_config_odbc.conf");
+
+	if(config) {
+		for(v = ast_variable_browse(config,"settings");v;v=v->next) {
+			if(!strcmp(v->name,"table")) {
+				strncpy(table,v->value,sizeof(table));
+				configured++;
+			}
+			else if(!strcmp(v->name,"connection")) {
+				strncpy(connection,v->value,sizeof(connection));
+				configured++;
+			}
+		}
+		ast_destroy(config);
+	}
+	if(configured < 2)
+		return -1;
+
+	obj = fetch_odbc_obj(connection);
+	if(!obj)
+		return -1;
+
+  	res=SQLAllocHandle(SQL_HANDLE_STMT,obj->con, &stmt);
+	SQLBindCol(stmt,1,SQL_C_ULONG,&rowid,sizeof(rowid),&err);
+	sprintf(sql,"select id from %s where filename='%s' and category='%s' and var_name='%s' and commented=0 order by filename,cat_metric,var_metric,id",table,file,category,variable);
+	res = SQLExecDirect(stmt,sql,SQL_NTS);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING,"SQL select error!\n[%s]\n\n",sql);
+		SQLFreeHandle(SQL_HANDLE_STMT,stmt);
+		return -1;
+	}
+	
+	res=SQLNumResultCols(stmt,&rowcount);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING,"SQL select error!\n[%s]\n\n",sql);
+		SQLFreeHandle(SQL_HANDLE_STMT,stmt);
+		return -1;
+	}
+
+	if (!rowcount)
+		ast_log(LOG_WARNING, "No rows found\n");
+	else
+		SQLFetch(stmt);
+
+	SQLFreeHandle(SQL_HANDLE_STMT,stmt);
+	return rowid;
+}
+
+int config_odbc_update_cfg_by_id(int rowid, char *newval)
+{
+	int configured = 0;
+	struct ast_config *config;
+	struct ast_variable *v;
+	char connection[128];
+	char table[128];
+	odbc_obj *obj;
+	char sql[255];
+	SQLHSTMT stmt;
+	SQLLEN rowcount;
+	int res;
+
+	config = ast_load("res_config_odbc.conf");
+	if(config) {
+		for(v = ast_variable_browse(config,"settings");v;v=v->next) {
+			if(!strcmp(v->name,"table")) {
+				strncpy(table,v->value,sizeof(table));
+				configured++;
+			}
+			else if(!strcmp(v->name,"connection")) {
+				strncpy(connection,v->value,sizeof(connection));
+				configured++;
+			}
+		}
+		ast_destroy(config);
+	}
+	if(configured < 2)
+		return 0;
+
+	obj = fetch_odbc_obj(connection);
+	if(!obj)
+		return 0;
+
+	res=SQLAllocHandle(SQL_HANDLE_STMT,obj->con, &stmt);
+	sprintf(sql,"update %s set var_val = ? where id = ?",table);
+	SQLPrepare(stmt,sql, SQL_NTS);
+	SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, newval, 0, NULL);
+	SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &rowid, 0, NULL);
+	res = SQLExecute(stmt);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING,"SQL update error!\n[%s]\n\n",sql);
+		SQLFreeHandle(SQL_HANDLE_STMT,stmt);
+		return 0;
+	}
+	res=SQLRowCount(stmt,&rowcount);
+	if (!rowcount)
+		ast_log(LOG_WARNING,"Failed to update any rows!\n");
+	else if (rowcount > 1)
+            ast_log(LOG_WARNING,"Updated more than one (%d) row???\n",(int)rowcount);
+
+	SQLFreeHandle(SQL_HANDLE_STMT,stmt);
+	return (int) rowcount;
+}
+
 int unload_module (void)
 {
 	ast_cust_config_deregister (&reg1);
Index: apps/app_voicemail.c
===================================================================
RCS file: /usr/cvsroot/asterisk/apps/app_voicemail.c,v
retrieving revision 1.131
diff -u -r1.131 app_voicemail.c
--- apps/app_voicemail.c	8 Jul 2004 16:24:16 -0000	1.131
+++ apps/app_voicemail.c	8 Jul 2004 22:57:53 -0000
@@ -177,7 +177,34 @@
 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration);
 static int vm_delete(char *file);
 
-
+#include <dlfcn.h> /* to dlopen res_config_odbc.so */
+/* defined in config_pvt.h, re-declare and prototype here */
+#define CONFIG_KEYWORD_STRLEN 128
+#define CONFIG_KEYWORD_ARRAYLEN 512
+struct ast_category {
+	char name[80];
+	struct ast_variable *root;
+	struct ast_category *next;
+#ifdef PRESERVE_COMMENTS
+	struct ast_comment *precomments;
+	struct ast_comment *sameline;
+#endif
+};
+struct ast_config_reg {
+	char name[CONFIG_KEYWORD_STRLEN];
+	struct ast_config *(*func)(char *, struct ast_config *,struct ast_category **,struct ast_variable **,int
+#ifdef PRESERVE_COMMENTS
+	,struct ast_comment_struct *
+#endif
+	);
+	char keywords[CONFIG_KEYWORD_STRLEN][CONFIG_KEYWORD_ARRAYLEN];
+	int keycount;
+	struct ast_config_reg *next;
+};
+struct ast_config_reg *get_config_registrations(void);
+/* end stuff copied from config_pvt.h */
+int vm_is_config_odbc(void); /* new function to test for use of odbc config */
+static void vm_odbc_change_password(struct ast_vm_user *, char *);
 
 static char *tdesc = "Comedian Mail (Voicemail System)";
 
@@ -241,6 +268,7 @@
 static char *app3 = "MailboxExists";
 
 AST_MUTEX_DEFINE_STATIC(vmlock);
+AST_MUTEX_DEFINE_STATIC(ast_cust_config_lock);
 struct ast_vm_user *users;
 struct ast_vm_user *usersl;
 struct vm_zone *zones = NULL;
@@ -561,6 +589,100 @@
 	return res;
 }
 
+int vm_is_config_odbc()
+{
+        struct ast_config_reg *key;
+        int x, is_odbc = 0;
+
+	ast_mutex_lock(&ast_cust_config_lock);
+	for (key=get_config_registrations();key;key=key->next) {
+		if (!strcmp("odbc", key->name)) {
+			for (x=0;x<key->keycount;x++)
+				if (!strcmp(VOICEMAIL_CONFIG,key->keywords[x]))
+					is_odbc = 1;
+		}
+	}
+	ast_mutex_unlock(&ast_cust_config_lock);
+	return is_odbc;
+}
+
+
+static void vm_odbc_change_password(struct ast_vm_user *vmu, char *newpassword)
+{
+	struct ast_config *cfg;
+	char *vmu_config;
+	void *res_config;
+	int rowid;
+	char *dlsym_err;
+	char *rest;
+	char newval[128];
+	char fn[256];
+
+	/* return row id of config item specified: filename, category, var */
+	int (*get_config_id)(char*, char*, char*);
+	/* same args as above, add new value to the end */
+	int (*update_config_by_id)(int, char*);
+
+	snprintf(fn, sizeof(fn), "%s/%s", (char *)ast_config_AST_MODULE_DIR, "res_config_odbc.so");
+	res_config = dlopen(fn, RTLD_LAZY);
+	if (!res_config) {
+		ast_log(LOG_ERROR, "Unable to open res_config_odbc.so: %s\n", dlerror());
+		return;
+	}
+
+	get_config_id = dlsym(res_config, "config_odbc_get_cfg_id");
+	if ((dlsym_err = dlerror()) != NULL) {
+		ast_log(LOG_ERROR, "Unable to load symbol config_odbc_get_cfg_id: %s\n", dlsym_err);
+		dlclose(res_config);
+		return;
+	}
+	update_config_by_id = dlsym(res_config, "config_odbc_update_cfg_by_id");
+	if ((dlsym_err = dlerror()) != NULL) {
+		ast_log(LOG_ERROR, "Unable to load symbol config_odbc_update_cfg_by_id: %s\n", dlsym_err);
+		dlclose(res_config);
+		return;
+	}
+
+	rowid = (*get_config_id)(VOICEMAIL_CONFIG, vmu->context, vmu->mailbox);
+	if (rowid == -1) {
+		ast_log(LOG_WARNING, "VM mailbox %s@%s is not found\n", vmu->mailbox, vmu->context);
+		dlclose(res_config);
+		return;
+	}
+
+	cfg = ast_load(VOICEMAIL_CONFIG);
+	if (!cfg) {
+		ast_log(LOG_WARNING, "Unable to load voicemail.conf\n");
+		dlclose(res_config);
+		return;
+	}
+	vmu_config = ast_variable_retrieve(cfg, vmu->context, vmu->mailbox);
+	/* parse vmu_config and set updated password */
+	rest = strchr(vmu_config,',');
+	if (rest) {
+		*rest = '\0';
+		rest++;
+	} else
+		rest = NULL;
+
+	if (rest)
+		snprintf(newval, 128, "%s,%s", newpassword,rest);
+	else
+		snprintf(newval, 128, "%s", newpassword);
+
+	if (!(*update_config_by_id)(rowid, newval)) {
+		ast_log(LOG_ERROR, "Unable to update VM config for %s@%s\n", vmu->mailbox, vmu->context);
+		dlclose(res_config);
+		ast_destroy(cfg);
+		return;
+	}
+
+	dlclose(res_config);
+	ast_destroy(cfg);
+	reset_user_pw(vmu->context, vmu->mailbox, newpassword);
+	strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
+}
+
 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
 {
         /*  There's probably a better way of doing this. */
@@ -576,6 +698,12 @@
 		char tmpin[AST_CONFIG_MAX_PATH];
 		char tmpout[AST_CONFIG_MAX_PATH];
 		char *user, *pass, *rest, *trim, *tempcontext;
+
+		if (vm_is_config_odbc()) {
+			vm_odbc_change_password(vmu, newpassword);
+			return;
+		}
+
 		tempcontext = NULL;
 		snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
 		snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
