Index: app.c
===================================================================
RCS file: /usr/cvsroot/asterisk/app.c,v
retrieving revision 1.60
diff -u -r1.60 app.c
--- app.c	4 May 2005 15:26:03 -0000	1.60
+++ app.c	25 May 2005 02:18:26 -0000
@@ -209,6 +209,86 @@
 	return 0;
 }
 
+struct external_voicemail {
+	char mailbox[80];
+	int newmsgs;
+	int oldmsgs;
+	int hasvoicemail;
+	struct external_voicemail *next;
+} *extvlist = NULL, *extvlist_end = NULL;
+
+AST_MUTEX_DEFINE_STATIC(extvm_lock);
+
+int (*ast_request_messagecount)(const char *peername, const char *mailbox) = NULL;
+
+int (*ast_send_messagecount)(const char *peername, const char *mailbox, int newmsgs, int oldmsgs, int hasvoicemail) = NULL;
+
+static struct external_voicemail *new_ext_vm(const char *mailbox)
+{
+	struct external_voicemail *tmp = malloc(sizeof(struct external_voicemail));
+	if (!tmp)
+		return NULL;
+	memset(tmp, 0, sizeof(struct external_voicemail));
+	strncpy(tmp->mailbox, mailbox, sizeof(tmp->mailbox) - 1);
+	/* insert to our list */
+	/* no need for mutex since the parent function should care */
+	if (!extvlist) {
+		extvlist = tmp;
+	} else {
+		extvlist_end->next = tmp;
+	}
+	extvlist_end = tmp;
+	return tmp;
+}
+
+#if 0
+static int destroy_all_vm(void)
+{
+	struct external_voicemail *tmp, *tmp2;
+	ast_mutex_lock(&extvm_lock);
+	tmp = extvlist;
+	while (tmp) {
+		tmp2 = tmp;
+		tmp = tmp->next;
+		free(tmp2);
+	}
+	extvlist = NULL;
+	extvlist_end = NULL;
+	ast_mutex_unlock(&extvm_lock);
+	return 0;
+}
+#endif
+
+int update_message_count(const char *mailbox, int newmsgs, int oldmsgs, int hasvoicemail)
+{
+	struct external_voicemail *tmp;
+	int found = 0;
+	ast_mutex_lock(&extvm_lock);
+	tmp = extvlist;
+	while (tmp) {
+		if (!strcasecmp(tmp->mailbox, mailbox)) {
+			found = 1;
+			break;
+		}
+		tmp = tmp->next;
+	}
+	if (!found) {
+		if (!(tmp = new_ext_vm(mailbox))) {
+			ast_mutex_unlock(&extvm_lock);
+			return -1;
+		}
+	}
+	if (tmp) {
+		if (newmsgs > -1)
+			tmp->newmsgs = newmsgs;
+		if (oldmsgs > -1)
+			tmp->oldmsgs = oldmsgs;
+		tmp->hasvoicemail = hasvoicemail;
+	}
+	ast_mutex_unlock(&extvm_lock);
+	return 0;
+}
+
 static int (*ast_has_voicemail_func)(const char *mailbox, const char *folder) = NULL;
 static int (*ast_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL;
 
@@ -225,9 +305,48 @@
 	ast_messagecount_func = NULL;
 }
 
+static int is_remote_mailbox(const char *mailbox)
+{
+	int res = 0;
+	if (strchr(mailbox, ':') && strchr(mailbox, '@')) {
+		ast_mutex_lock(&extvm_lock);
+		new_ext_vm(mailbox);
+		ast_mutex_unlock(&extvm_lock);
+		if (ast_request_messagecount) {
+			char tmp[256], *stringp;
+			char *address;
+			strncpy(tmp, mailbox, sizeof(tmp) - 1);
+			stringp = tmp;
+			strsep(&stringp, "@");
+			address = stringp;
+			if (address)
+				ast_request_messagecount(address, mailbox);
+		}
+		res = 1;
+	}
+	return res;
+}
+
 int ast_app_has_voicemail(const char *mailbox, const char *folder)
 {
 	static int warned = 0;
+	if (extvlist) {
+		struct external_voicemail *tmp;
+		ast_mutex_lock(&extvm_lock);
+		tmp = extvlist;
+		while (tmp) {
+			if (!strcasecmp(tmp->mailbox, mailbox)) {
+				int hasvoicemail = tmp->hasvoicemail;
+				ast_mutex_unlock(&extvm_lock);
+				return hasvoicemail;
+			}
+			tmp = tmp->next;
+		}
+		ast_mutex_unlock(&extvm_lock);
+	}
+	if (is_remote_mailbox(mailbox))
+		return 0;
+
 	if (ast_has_voicemail_func)
 		return ast_has_voicemail_func(mailbox, folder);
 
@@ -246,6 +365,23 @@
 		*newmsgs = 0;
 	if (oldmsgs)
 		*oldmsgs = 0;
+	if (extvlist) {
+		struct external_voicemail *tmp;
+		ast_mutex_lock(&extvm_lock);
+		tmp = extvlist;
+		while (tmp) {
+			if (!strcasecmp(tmp->mailbox, mailbox)) {
+				*newmsgs = tmp->newmsgs;
+				*oldmsgs = tmp->oldmsgs;
+				ast_mutex_unlock(&extvm_lock);
+				return 0;
+			}
+			tmp = tmp->next;
+		}
+		ast_mutex_unlock(&extvm_lock);
+	}
+	if (is_remote_mailbox(mailbox))
+		return 0;
 	if (ast_messagecount_func)
 		return ast_messagecount_func(mailbox, newmsgs, oldmsgs);
 
Index: apps/app_voicemail.c
===================================================================
RCS file: /usr/cvsroot/asterisk/apps/app_voicemail.c,v
retrieving revision 1.215
diff -u -r1.215 app_voicemail.c
--- apps/app_voicemail.c	17 May 2005 17:19:53 -0000	1.215
+++ apps/app_voicemail.c	25 May 2005 02:18:26 -0000
@@ -86,6 +86,9 @@
 #define VM_DELETE		(1 << 12)
 #define VM_ALLOCED		(1 << 13)
 
+/* are we a voicemail server */
+static char voicemail_server[256] = "";
+
 static int load_config(void);
 
 /* Syntaxes supported, not really language codes.
@@ -158,6 +161,7 @@
 struct ast_vm_user {
 	char context[80];		/* Voicemail context */
 	char mailbox[80];		/* Mailbox id, unique within vm context */
+	char peername[80];		/* used if we're voicemail_server */
 	char password[80];		/* Secret pin code, numbers only */
 	char fullname[80];		/* Full name, for directory app */
 	char email[80];			/* E-mail address */
@@ -288,6 +292,12 @@
 "be set with the authenticated mailbox.\n"
 "If the options contain 's' then no initial prompts will be played.\n";
 
+static char *synopsis_change_mailbox =
+"Changes the mailbox";
+
+static char *descrip_change_mailbox =
+"  ChangeMailbox([mailbox][@context]): Changes the mailbox.\n";
+
 /* Leave a message */
 static char *app = "VoiceMail";
 
@@ -296,6 +306,7 @@
 
 static char *app3 = "MailboxExists";
 static char *app4 = "VMAuthenticate";
+static char *app_change_mailbox = "ChangeMailbox";
 
 AST_MUTEX_DEFINE_STATIC(vmlock);
 struct ast_vm_user *users;
@@ -341,6 +352,46 @@
 
 LOCAL_USER_DECL;
 
+static int handle_voicemail_server(struct ast_vm_user *vmu, int newmsgs, int oldmsgs, char *ext_context)
+{
+	int res = 0;
+	if (strlen(voicemail_server) && strlen(vmu->peername) && ast_send_messagecount) {
+		char tmp[256];
+		snprintf(tmp, sizeof(tmp), "%s:%s@%s", vmu->mailbox, vmu->context, voicemail_server);
+		if (newmsgs == -1 || oldmsgs == -1) {
+			ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
+		}
+		ast_send_messagecount(vmu->peername, tmp, newmsgs, oldmsgs, ast_app_has_voicemail(ext_context, NULL));
+	} else
+		res = -1;
+	return res;
+}
+
+static void *voicemail_server_init(void *data)
+{
+	struct ast_vm_user *user;
+	/* wait at least 10 seconds */
+	sleep(10);
+	for (;;) {
+		/* someone has to register the sending function */
+		if (ast_send_messagecount)
+			break;
+		sleep(1);
+	}
+	ast_mutex_lock(&vmlock);
+	user = users;
+	while (user) {
+		if (strlen(user->peername)) {
+			char ext_context[256] = "";
+			snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
+			handle_voicemail_server(user, -1, -1, ext_context);
+		}
+		user = user->next;
+	}
+	ast_mutex_unlock(&vmlock);
+	return NULL;
+}
+
 static void populate_defaults(struct ast_vm_user *vmu)
 {
 	ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);	
@@ -2931,6 +2982,7 @@
 		ast_app_messagecount(ext_context, &newmsgs, &oldmsgs);
 	}
 	manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
+	handle_voicemail_server(vmu, newmsgs, oldmsgs, ext_context);
 	run_externnotify(chan->context, ext_context);
 	return 0;
 }
@@ -3132,6 +3184,7 @@
 				}
 				/* Leave voicemail for someone */
 				manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
+				handle_voicemail_server(vmtmp, -1, -1, ext_context);
 				run_externnotify(chan->context, ext_context);
 	
 				saved_messages++;
@@ -4969,6 +5022,7 @@
 	if (valid) {
 		snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
 		manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
+		handle_voicemail_server(vmu, -1, -1, ext_context);
 		run_externnotify(chan->context, ext_context);
 	}
 	if (vmu)
@@ -5044,13 +5098,21 @@
 	char *stringp;
 	char *s;
 	struct ast_vm_user *vmu;
-	strncpy(tmp, data, sizeof(tmp) - 1);
 	vmu = malloc(sizeof(struct ast_vm_user));
 	if (vmu) {
 		memset(vmu, 0, sizeof(struct ast_vm_user));
 		strncpy(vmu->context, context, sizeof(vmu->context) - 1);
+		if (strchr(mbox, '@') && voicemail_server) {
+			char *peername;
+			strncpy(tmp, mbox, sizeof(tmp) - 1);
+			stringp = tmp;
+			mbox = strsep(&stringp, "@");
+			peername = stringp;
+			strncpy(vmu->peername, peername, sizeof(vmu->peername) - 1);
+		}
 		strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox) - 1);
 		populate_defaults(vmu);
+		strncpy(tmp, data, sizeof(tmp) - 1);
 		stringp = tmp;
 		if ((s = strsep(&stringp, ","))) 
 			strncpy(vmu->password, s, sizeof(vmu->password) - 1);
@@ -5140,6 +5202,72 @@
 	return res;
 }
 
+static int vm_change_mailbox(struct ast_channel *chan, void *data) {
+	struct localuser *u;
+	char workspace[256], *stringp;
+	char *tech, *args;
+	char function[256];
+	struct ast_custom_function *fun;
+	LOCAL_USER_ADD(u);
+	strncpy(workspace, (char *)data, sizeof(workspace) - 1);
+	stringp = workspace;
+	tech = strsep(&stringp, "|");
+	args = strsep(&stringp, "|");
+	if (!tech || !args) {
+		ast_log(LOG_ERROR, "%s: you're not calling this application properly: %s(%s)\n", app_change_mailbox, app_change_mailbox, (char *)data);
+		return -1;
+	}
+	if (!strcasecmp(tech, app)) {
+		/* change the mailbox taken from voicemail.conf */
+		struct ast_vm_user svm, *user;
+		char *mailbox, *context, *peername;
+		if (!voicemail_server) {
+			ast_log(LOG_ERROR, "%s: this server doesn't have a status of a voicemail_server so you can't use 'Voicemail' technology; refer to voicemail.conf\n", app_change_mailbox);
+			LOCAL_USER_REMOVE(u);
+			return -1;
+		}
+		stringp = args;
+		mailbox = strsep(&stringp, "=");
+		peername = strsep(&stringp, "=");
+		stringp = mailbox;
+		strsep(&stringp, "@");
+		context = stringp;
+		if (!context)
+			context = "default";
+		if (!mailbox || !peername) {
+			ast_log(LOG_ERROR, "%s: no mailbox or peername specified with: %s(%s)\n", app_change_mailbox, app_change_mailbox, (char *)data);
+			LOCAL_USER_REMOVE(u);
+			return -1;
+		}
+		user = find_user(&svm, context, mailbox);
+		ast_mutex_lock(&vmlock);
+		user = users;
+		while (user) {
+			if (!strcasecmp(context, user->context) && !strcasecmp(mailbox, user->mailbox))
+				break;
+			user=user->next;
+		}
+		if (!user) {
+			ast_log(LOG_ERROR, "%s: can't find that mailbox: %s%s%s\n", app_change_mailbox, mailbox, context ? "@" : "", context ? context : "");
+			LOCAL_USER_REMOVE(u);
+			return -1;
+		}
+		strncpy(user->peername, peername, sizeof(user->peername) - 1);
+	} else {
+		/* pass it to the channel driver function */
+		snprintf(function, sizeof(function), "%s_CHANGE_MAILBOX", tech);
+		fun = ast_custom_function_find(function);
+		if (!fun) {
+			ast_log(LOG_ERROR, "%s: can't find the CHANGE_MAILBOX function for your technology: %s\n", app_change_mailbox, tech);
+			LOCAL_USER_REMOVE(u);
+			return -1;
+		}
+		fun->read(chan, function, args, NULL, 0);
+	}
+	LOCAL_USER_REMOVE(u);
+	return 0;
+}
+
 static char show_voicemail_users_help[] =
 "Usage: show voicemail users [for <context>]\n"
 "       Lists all mailboxes currently set up\n";
@@ -5510,6 +5638,10 @@
 
 		if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
 			astdirfwd = "no";
+
+		if ((s = ast_variable_retrieve(cfg, "general", "voicemail_server")))
+			strncpy(voicemail_server, s, sizeof(voicemail_server));
+	
 		ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);	
 
 		cat = ast_category_browse(cfg, NULL);
@@ -5629,6 +5761,12 @@
 			}
 		}
 		ast_config_destroy(cfg);
+		if (strlen(voicemail_server)) {
+			pthread_t th;
+			if (!ast_pthread_create(&th, NULL, voicemail_server_init, NULL))
+				ast_log(LOG_ERROR, "Can't start the voicemail server init thread!\n");
+		}
+
 		ast_mutex_unlock(&vmlock);
 		return 0;
 	} else {
@@ -5651,6 +5789,7 @@
 	res |= ast_unregister_application(app2);
 	res |= ast_unregister_application(app3);
 	res |= ast_unregister_application(app4);
+	res |= ast_unregister_application(app_change_mailbox);
 	ast_cli_unregister(&show_voicemail_users_cli);
 	ast_cli_unregister(&show_voicemail_zones_cli);
 	ast_uninstall_vm_functions();
@@ -5664,6 +5803,7 @@
 	res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
 	res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
 	res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
+	res |= ast_register_application(app_change_mailbox, vm_change_mailbox, synopsis_change_mailbox, descrip_change_mailbox);
 	if (res)
 		return(res);
 
Index: channels/Makefile
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/Makefile,v
retrieving revision 1.69
diff -u -r1.69 Makefile
--- channels/Makefile	29 Apr 2005 04:37:02 -0000	1.69
+++ channels/Makefile	25 May 2005 02:18:26 -0000
@@ -26,7 +26,7 @@
 	     chan_modem_aopen.so \
              chan_modem_bestdata.so \
              chan_agent.so chan_mgcp.so chan_iax2.so \
-	     chan_local.so chan_skinny.so chan_features.so
+	     chan_local.so chan_skinny.so chan_features.so chan_simple.so
 
 ifeq (${OSARCH},OpenBSD)
 CFLAGS+=-I$(CROSS_COMPILE_TARGET)/usr/local/include
@@ -190,6 +190,8 @@
 	$(CC) $(SOLINK) -o $@ $< h323/libchanh323.a $(CHANH323LIB) -L$(PWLIBDIR)/lib $(PTLIB) -L$(OPENH323DIR)/lib $(H323LIB) -L/usr/lib -lcrypto -lssl -lexpat
 endif
 
+chan_simple.so: chan_simple.o
+	$(CC) $(SOLINK) -o $@ chan_simple.o
 
 #chan_modem.so : chan_modem.o
 #	$(CC) -rdynamic -shared -Xlinker -x -o $@ $<
Index: channels/chan_iax2.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_iax2.c,v
retrieving revision 1.294
diff -u -r1.294 chan_iax2.c
--- channels/chan_iax2.c	19 May 2005 04:31:02 -0000	1.294
+++ channels/chan_iax2.c	25 May 2005 02:18:27 -0000
@@ -38,6 +38,7 @@
 #include "asterisk/localtime.h"
 #include "asterisk/aes.h"
 #include "asterisk/dnsmgr.h"
+#include "asterisk/register_event.h"
 #include <sys/mman.h>
 #include <arpa/inet.h>
 #include <dirent.h>
@@ -274,6 +275,8 @@
 	char regexten[AST_MAX_EXTENSION];		/* Extension to register (if regcontext is used) */
 	char peercontext[AST_MAX_EXTENSION];		/* Context to pass to peer */
 	char mailbox[AST_MAX_EXTENSION];		/* Mailbox */
+	char on_register_context[AST_MAX_EXTENSION];	/* context to start pbx on register event */
+	char on_unregister_context[AST_MAX_EXTENSION];	/* context to start pbx on unregister event */
 	struct ast_codec_pref prefs;
 	struct ast_dnsmgr_entry *dnsmgr;		/* DNS refresh manager */
 	struct sockaddr_in addr;
@@ -1086,6 +1089,38 @@
 	return 0;
 }
 
+static char *func_change_mailbox(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+	struct iax2_peer *peer = NULL;
+	char workspace[256], *stringp;
+	char *peername, *mailbox;
+	if (!data) {
+		ast_log(LOG_ERROR, "%s <peer>=<mailbox> argument expected\n", cmd);
+		return NULL;
+	}
+	strncpy(workspace, data, sizeof(workspace) - 1);
+	stringp = workspace;
+	peername = strsep(&stringp, "=");
+	mailbox = strsep(&stringp, "=");
+	if (!peername || !mailbox) {
+		ast_log(LOG_ERROR, "%s: peer or mailbox are not correct: (%s)\n", cmd, data);
+		return NULL;
+	}
+	peer = find_peer(peername, 1);
+	if (peer)
+		strncpy(peer->mailbox, mailbox, sizeof(peer->mailbox));
+	else
+		ast_log(LOG_ERROR, "Can't find peer %s from the %s channel driver\n", peername, channeltype);
+	return "";
+}
+
+static struct ast_custom_function change_mailbox_function = {
+	.name = "IAX_CHANGE_MAILBOX",
+	.synopsis = "Changes the mailbox on a IAX channel",
+	.syntax = "SIP_HEADER(<peer>=<mailbox>)",
+	.read = func_change_mailbox,
+};
+
 static void destroy_firmware(struct iax_firmware *cur)
 {
 	/* Close firmware */
@@ -2977,6 +3012,63 @@
 	return 0;
 }
 
+static int iax2_new_callno(const char *peername)
+{
+	struct iax2_peer *peer = find_peer(peername, 1);
+	if (!peer) {
+		ast_log(LOG_WARNING, "Can't find peername '%s' ...\n", peername);
+		return -1;
+	}
+	return find_callno(0, 0, &peer->addr, NEW_FORCE, 1, defaultsockfd);
+}
+
+static int iax2_vm_request_messagecount(const char *peername, const char *mailbox)
+{
+	struct iax_ie_data ied;
+	char *lmailbox;
+	int callno = iax2_new_callno(peername);
+	if (!mailbox || callno <= 0) {
+		ast_log(LOG_WARNING, "The mailbox or IAX peername is invalid.\n");
+		return -1;
+	}
+	lmailbox = ast_strdupa(mailbox);
+	memset(&ied, 0, sizeof(ied));
+	iax_ie_append_str(&ied, IAX_IE_USERNAME, lmailbox);
+	send_command_locked(callno, AST_FRAME_IAX, IAX_COMMAND_VMSERVER, 0, ied.buf, ied.pos, -1);
+	return 0;
+}
+
+static int __iax2_vm_send_messagecount(unsigned short callno, const char *mailbox, int newmsgs, int oldmsgs, int hasvoicemail)
+{
+	struct iax_ie_data ied;
+	char *lmailbox;
+	if (!mailbox)
+		return -1;
+	lmailbox = ast_strdupa(mailbox);
+	memset(&ied, 0, sizeof(ied));
+	iax_ie_append_str(&ied, IAX_IE_USERNAME, lmailbox);
+	if (newmsgs > -1)
+		iax_ie_append_short(&ied, IAX_IE_VM_NEWMSGS, newmsgs);
+	if (oldmsgs > -1)
+		iax_ie_append_short(&ied, IAX_IE_VM_OLDMSGS, oldmsgs);
+	iax_ie_append_short(&ied, IAX_IE_VM_HASVM, hasvoicemail);
+	ast_mutex_lock(&iaxsl[callno]);
+	send_command_final(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_VMSERVER, 0, ied.buf, ied.pos, -1);
+	ast_mutex_unlock(&iaxsl[callno]);
+	return 0;
+}
+
+static int iax2_vm_send_messagecount(const char *peername, const char *mailbox, int newmsgs, int oldmsgs, int hasvoicemail)
+{
+	int callno = iax2_new_callno(peername);
+	int res = 0;
+	if (callno > 0)
+		res = __iax2_vm_send_messagecount(callno, mailbox, newmsgs, oldmsgs, hasvoicemail);
+	else
+		ast_log(LOG_WARNING, "IAX peername is invalid.\n");
+	return res;
+}
+
 static int iax2_hangup(struct ast_channel *c) 
 {
 	unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
@@ -5513,6 +5605,7 @@
 	if (!ast_test_flag(p, IAX_TEMPONLY))
 		ast_db_del("IAX/Registry", p->name);
 	register_peer_exten(p, 0);
+	register_event(channeltype, p->on_unregister_context, p->name, 0);
 	ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
 	if (iax2_regfunk)
 		iax2_regfunk(p->name, 0);
@@ -5593,6 +5686,7 @@
 				manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Registered\r\n", p->name);
 				ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
 				register_peer_exten(p, 1);
+				register_event(channeltype, p->on_register_context, p->name, 1);
 			} else if (!ast_test_flag(p, IAX_TEMPONLY)) {
 				if  (option_verbose > 2)
 				ast_verbose(VERBOSE_PREFIX_3 "Unregistered IAX2 '%s' (%s)\n", p->name, 
@@ -5600,6 +5694,7 @@
 				manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: IAX2/%s\r\nPeerStatus: Unregistered\r\n", p->name);
 				ast_device_state_changed("IAX2/%s", p->name); /* Activate notification */
 				register_peer_exten(p, 0);
+				register_event(channeltype, p->on_register_context, p->name, 0);
 				ast_db_del("IAX/Registry", p->name);
 			}
 			/* Update the host */
@@ -6375,7 +6470,7 @@
 			f.subclass = uncompress_subclass(fh->csub);
 		}
 		if ((f.frametype == AST_FRAME_IAX) && ((f.subclass == IAX_COMMAND_NEW) || (f.subclass == IAX_COMMAND_REGREQ)
-				|| (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL)))
+				|| (f.subclass == IAX_COMMAND_POKE) || (f.subclass == IAX_COMMAND_FWDOWNL) || (f.subclass == IAX_COMMAND_VMSERVER)))
 			new = NEW_ALLOW;
 	} else {
 		/* Don't know anything about it yet */
@@ -6671,6 +6766,34 @@
 					iaxs[fr.callno]->transferring = TRANSFER_READY;
 				}
 				break;
+			case IAX_COMMAND_VMSERVER:
+			{
+				if (ies.hasvm == -1) {
+					/* request for voicemail info */
+					int res = 0;
+					int newmsgs = -1, oldmsgs = -1;
+					char mailbox[256]="", *tmp, *address = NULL;
+					strncpy(mailbox, ies.username, sizeof(mailbox) - 1);
+					if ((tmp = strchr(mailbox, ':')))
+						*tmp = '@';
+					if ((tmp = strchr(tmp + 1, '@'))) {
+						*tmp = '\0';
+						address = tmp + 1;
+					}
+					if (!strlen(mailbox) || !address)
+						break;
+					res = ast_app_messagecount(mailbox, &newmsgs, &oldmsgs);
+					if (!res && address) {
+						__iax2_vm_send_messagecount(fr.callno, ies.username, newmsgs, oldmsgs, newmsgs ? 1 : 0);
+					}
+				} else {
+					/* voicemail info */
+					update_message_count(ies.username, ies.newmsgs, ies.oldmsgs, ies.hasvm);
+					send_command_immediate(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_ACK, fr.ts, NULL, 0,fr.iseqno);
+					iax2_destroy_nolock(fr.callno);
+				}
+				break;
+			}
 			case IAX_COMMAND_NEW:
 				/* Ignore if it's already up */
 				if (iaxs[fr.callno]->state & (IAX_STATE_STARTED | IAX_STATE_TBD))
@@ -8060,6 +8183,9 @@
 					ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of iax.conf\n", peer->name, v->lineno);
 					peer->maxms = 0;
 				}
+			} else if (!strcasecmp(v->name, "onregister-context")) {
+				strncpy(peer->on_register_context, v->value, sizeof(peer->on_register_context));
+				snprintf(peer->on_unregister_context, sizeof(peer->on_unregister_context), "unreg-%s", peer->on_register_context);
 			} else if (!strcasecmp(v->name, "timezone")) {
 				ast_copy_string(peer->zonetag, v->value, sizeof(peer->zonetag));
 			}/* else if (strcasecmp(v->name,"type")) */
@@ -9131,6 +9257,7 @@
 			iax2_destroy(x);
 	ast_manager_unregister( "IAXpeers" );
 	ast_manager_unregister( "IAXnetstats" );
+ 	ast_custom_function_unregister(&change_mailbox_function);
 	ast_unregister_application(papp);
 	ast_cli_unregister(&cli_show_users);
 	ast_cli_unregister(&cli_show_channels);
@@ -9251,6 +9378,11 @@
 	ast_cli_register(&cli_reload);
 
 	ast_register_application(papp, iax2_prov_app, psyn, pdescrip);
+	ast_custom_function_register(&change_mailbox_function);
+	if (!ast_request_messagecount)
+		ast_request_messagecount = iax2_vm_request_messagecount;
+	if (!ast_send_messagecount)
+		ast_send_messagecount = iax2_vm_send_messagecount;
 	
 	ast_manager_register( "IAXpeers", 0, manager_iax2_show_peers, "List IAX Peers" );
 	ast_manager_register( "IAXnetstats", 0, manager_iax2_show_netstats, "Show IAX Netstats" );
Index: channels/chan_sip.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v
retrieving revision 1.739
diff -u -r1.739 chan_sip.c
--- channels/chan_sip.c	20 May 2005 03:14:41 -0000	1.739
+++ channels/chan_sip.c	25 May 2005 02:18:27 -0000
@@ -42,6 +42,7 @@
 #include "asterisk/file.h"
 #include "asterisk/astobj.h"
 #include "asterisk/dnsmgr.h"
+#include "asterisk/register_event.h"
 #ifdef OSP_SUPPORT
 #include "asterisk/astosp.h"
 #endif
@@ -559,6 +560,8 @@
 	char language[MAX_LANGUAGE];	/* Default language for prompts */
 	char musicclass[MAX_LANGUAGE];  /* Music on Hold class */
 	char useragent[256];		/* User agent in SIP request (saved from registration) */
+	char on_register_context[AST_MAX_EXTENSION];	/* context to start pbx on register event */
+	char on_unregister_context[AST_MAX_EXTENSION];	/* context to start pbx on unregister event */
 	struct ast_codec_pref prefs;	/* codec prefs */
 	int lastmsgssent;
 	time_t	lastmsgcheck;		/* Last time we checked for MWI */
@@ -4873,6 +4876,7 @@
 	ast_db_del("SIP/Registry", peer->name);
 	manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "Peer: SIP/%s\r\nPeerStatus: Unregistered\r\nCause: Expired\r\n", peer->name);
 	register_peer_exten(peer, 0);
+	register_event(channeltype, peer->on_unregister_context, peer->name, 0);
 	peer->expire = -1;
 	ast_device_state_changed("SIP/%s", peer->name);
 	if (ast_test_flag(peer, SIP_SELFDESTRUCT) || ast_test_flag((&peer->flags_page2), SIP_PAGE2_RTAUTOCLEAR)) {
@@ -5075,6 +5079,7 @@
 		p->expire = -1;
 		ast_db_del("SIP/Registry", p->name);
 		register_peer_exten(p, 0);
+		register_event(channeltype, p->on_register_context, p->name, 0);
 		p->fullcontact[0] = '\0';
 		p->useragent[0] = '\0';
 		p->lastms = 0;
@@ -5152,6 +5157,7 @@
 		if (option_verbose > 2)
 			ast_verbose(VERBOSE_PREFIX_3 "Registered SIP '%s' at %s port %d expires %d\n", p->name, ast_inet_ntoa(iabuf, sizeof(iabuf), p->addr.sin_addr), ntohs(p->addr.sin_port), expiry);
 		register_peer_exten(p, 1);
+		register_event(channeltype, p->on_register_context, p->name, 1);
 	}
 
 	/* Save User agent */
@@ -5524,6 +5530,7 @@
 				transmit_response_with_date(p, "200 OK", req);
 				peer->lastmsgssent = -1;
 				res = 0;
+				register_event(channeltype, peer->on_register_context, peer->name, 1);
 			}
 		}
 	}
@@ -7986,6 +7993,30 @@
 	return buf;
 }
 
+static char *func_change_mailbox(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
+{
+	struct sip_peer *peer = NULL;
+	char workspace[256], *stringp;
+	char *peername, *mailbox;
+	if (!data) {
+		ast_log(LOG_ERROR, "%s <peer>=<mailbox> argument expected\n", cmd);
+		return NULL;
+	}
+	strncpy(workspace, data, sizeof(workspace) - 1);
+	stringp = workspace;
+	peername = strsep(&stringp, "=");
+	mailbox = strsep(&stringp, "=");
+	if (!peername || !mailbox) {
+		ast_log(LOG_ERROR, "%s: peer or mailbox are not correct: (%s)\n", cmd, data);
+		return NULL;
+	}
+	peer = find_peer(peername, NULL, 1);
+	if (peer)
+		strncpy(peer->mailbox, mailbox, sizeof(peer->mailbox));
+	else
+		ast_log(LOG_ERROR, "Can't find peer %s from the %s channel driver\n", peername, channeltype);
+	return "";
+}
 
 static struct ast_custom_function sip_header_function = {
 	.name = "SIP_HEADER",
@@ -7994,6 +8025,13 @@
 	.read = func_header_read,
 };
 
+static struct ast_custom_function change_mailbox_function = {
+	.name = "SIP_CHANGE_MAILBOX",
+	.synopsis = "Changes the mailbox on a SIP channel",
+	.syntax = "SIP_HEADER(<peer>=<mailbox>)",
+	.read = func_change_mailbox,
+};
+
 /*--- parse_moved_contact: Parse 302 Moved temporalily response */
 static void parse_moved_contact(struct sip_pvt *p, struct sip_request *req)
 {
@@ -10393,6 +10431,9 @@
 					peer->chanvars = tmpvar;
 				}
 			}
+		} else if (!strcasecmp(v->name, "onregister-context")) {
+			strncpy(peer->on_register_context, v->value, sizeof(peer->on_register_context));
+			snprintf(peer->on_unregister_context, sizeof(peer->on_unregister_context), "unreg-%s", peer->on_register_context);
 		} else if (!strcasecmp(v->name, "qualify")) {
 			if (!strcasecmp(v->value, "no")) {
 				peer->maxms = 0;
@@ -11223,6 +11264,7 @@
 			      "Show SIP peer (text format)", mandescr_show_peer);
 
 	ast_custom_function_register(&sip_header_function);
+	ast_custom_function_register(&change_mailbox_function);
 
 	sip_poke_all_peers();
 	sip_send_all_registers();
@@ -11241,6 +11283,7 @@
 	ast_channel_unregister(&sip_tech);
 
 	ast_custom_function_unregister(&sip_header_function);
+	ast_custom_function_unregister(&change_mailbox_function);
 
 	ast_unregister_application(app_dtmfmode);
 	ast_unregister_application(app_sipaddheader);
Index: channels/iax2-parser.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/iax2-parser.c,v
retrieving revision 1.43
diff -u -r1.43 iax2-parser.c
--- channels/iax2-parser.c	17 May 2005 19:41:09 -0000	1.43
+++ channels/iax2-parser.c	25 May 2005 02:18:27 -0000
@@ -209,6 +209,9 @@
 	{ IAX_IE_CAUSE, "CAUSE", dump_string },
 	{ IAX_IE_IAX_UNKNOWN, "UNKNOWN IAX CMD", dump_byte },
 	{ IAX_IE_MSGCOUNT, "MESSAGE COUNT", dump_short },
+	{ IAX_IE_VM_NEWMSGS, "NEW MESSAGES", dump_short },
+	{ IAX_IE_VM_OLDMSGS, "OLD MESSAGES", dump_short },
+	{ IAX_IE_VM_HASVM, "HAS VOICEMAIL", dump_short },
 	{ IAX_IE_AUTOANSWER, "AUTO ANSWER REQ" },
 	{ IAX_IE_TRANSFERID, "TRANSFER ID", dump_int },
 	{ IAX_IE_RDNIS, "REFERRING DNIS", dump_string },
@@ -560,6 +563,9 @@
 	char tmp[256];
 	memset(ies, 0, (int)sizeof(struct iax_ies));
 	ies->msgcount = -1;
+	ies->newmsgs = -1;
+	ies->oldmsgs = -1;
+	ies->hasvm = -1;
 	ies->firmwarever = -1;
 	ies->calling_ton = -1;
 	ies->calling_tns = -1;
@@ -713,6 +719,27 @@
 				errorf(tmp);
 			} else
 				ies->msgcount = ntohs(get_unaligned_uint16(data + 2));	
+			break;
+		case IAX_IE_VM_NEWMSGS:
+			if (len != (int)sizeof(unsigned short)) {
+				snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+				errorf(tmp);
+			} else
+				ies->newmsgs = ntohs(get_unaligned_uint16(data + 2));	
+			break;
+		case IAX_IE_VM_OLDMSGS:
+			if (len != (int)sizeof(unsigned short)) {
+				snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+				errorf(tmp);
+			} else
+				ies->oldmsgs = ntohs(get_unaligned_uint16(data + 2));	
+			break;
+		case IAX_IE_VM_HASVM:
+			if (len != (int)sizeof(unsigned short)) {
+				snprintf(tmp, (int)sizeof(tmp), "Expecting msgcount to be %d bytes long but was %d\n", (int)sizeof(unsigned short), len);
+				errorf(tmp);
+			} else
+				ies->hasvm = ntohs(get_unaligned_uint16(data + 2));	
 			break;
 		case IAX_IE_AUTOANSWER:
 			ies->autoanswer = 1;
Index: channels/iax2-parser.h
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/iax2-parser.h,v
retrieving revision 1.16
diff -u -r1.16 iax2-parser.h
--- channels/iax2-parser.h	12 Feb 2005 18:52:14 -0000	1.16
+++ channels/iax2-parser.h	25 May 2005 02:18:27 -0000
@@ -46,6 +46,9 @@
 	unsigned char causecode;
 	unsigned char iax_unknown;
 	int msgcount;
+	int newmsgs;
+	int oldmsgs;
+	int hasvm;
 	int autoanswer;
 	int musiconhold;
 	unsigned int transferid;
Index: channels/iax2.h
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/iax2.h,v
retrieving revision 1.23
diff -u -r1.23 iax2.h
--- channels/iax2.h	13 Apr 2005 23:33:47 -0000	1.23
+++ channels/iax2.h	25 May 2005 02:18:27 -0000
@@ -67,6 +67,7 @@
 #define IAX_COMMAND_PROVISION	35	/* Provision device */
 #define IAX_COMMAND_FWDOWNL	36	/* Download firmware */
 #define IAX_COMMAND_FWDATA	37	/* Firmware Data */
+#define IAX_COMMAND_VMSERVER	60	/* for use with voicemail server */
 
 #define IAX_DEFAULT_REG_EXPIRE  60	/* By default require re-registration once per minute */
 
@@ -127,6 +128,10 @@
 #define IAX_IE_RR_DELAY				49		/* Max playout delay for received frames (in ms) u16 */
 #define IAX_IE_RR_DROPPED			50		/* Dropped frames (presumably by jitterbuf) u32 */
 #define IAX_IE_RR_OOO				51		/* Frames received Out of Order u32 */
+
+#define IAX_IE_VM_NEWMSGS			60
+#define IAX_IE_VM_OLDMSGS			61
+#define IAX_IE_VM_HASVM				62
 
 
 #define IAX_AUTH_PLAINTEXT			(1 << 0)
Index: configs/sip.conf.sample
===================================================================
RCS file: /usr/cvsroot/asterisk/configs/sip.conf.sample,v
retrieving revision 1.62
diff -u -r1.62 sip.conf.sample
--- configs/sip.conf.sample	27 Apr 2005 17:04:17 -0000	1.62
+++ configs/sip.conf.sample	25 May 2005 02:18:27 -0000
@@ -230,6 +230,7 @@
 ;                             port
 ;                             qualify
 ;                             defaultip
+;                             onregister-context
 ;                             rtptimeout
 ;                             rtpholdtimeout
 
Index: configs/voicemail.conf.sample
===================================================================
RCS file: /usr/cvsroot/asterisk/configs/voicemail.conf.sample,v
retrieving revision 1.48
diff -u -r1.48 voicemail.conf.sample
--- configs/voicemail.conf.sample	6 Apr 2005 18:55:33 -0000	1.48
+++ configs/voicemail.conf.sample	25 May 2005 02:18:28 -0000
@@ -2,6 +2,10 @@
 ; Voicemail Configuration
 ;
 [general]
+; This keyword turnes on the voicemail notifing engine for voicemail clients that you define for each SIP/IAX2/etc entry with mailbox=box:context@[IP-of-the-voicemail-server | IAX2-peer-name-of-the-voicemail-server]
+; If this box is a voicemail server put here either IP of the box or IAX2 peer name unique throughout your switches; if you're unsure about what you're doing leave commented out
+;voicemail_server=unique
+
 ; Default formats for writing Voicemail
 ;format=g723sf|wav49|wav
 format=wav49|gsm|wav
Index: include/asterisk/app.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/app.h,v
retrieving revision 1.35
diff -u -r1.35 app.h
--- include/asterisk/app.h	4 May 2005 03:43:10 -0000	1.35
+++ include/asterisk/app.h	25 May 2005 02:18:28 -0000
@@ -101,6 +101,12 @@
 /* Full version with audiofd and controlfd.  NOTE: returns '2' on ctrlfd available, not '1' like other full functions */
 extern int ast_app_getdata_full(struct ast_channel *c, char *prompt, char *s, int maxlen, int timeout, int audiofd, int ctrlfd);
 
+int (*ast_request_messagecount)(const char *peername, const char *mailbox);
+
+int (*ast_send_messagecount)(const char *peername, const char *mailbox, int newmsgs, int oldmsgs, int mwi);
+
+int update_message_count(const char *mailbox, int newmsgs, int oldmsgs, int hasvoicemail);
+
 /*! Record voice (after playing prompt if specified), waiting for silence (in ms) up to a given timeout (in s) or '#' */
 int ast_app_getvoice(struct ast_channel *c, char *dest, char *dstfmt, char *prompt, int silence, int maxsec);
 
--- channels/chan_simple.c	2005-05-09 16:12:37.000000000 -0400
+++ channels/chan_simple.c	2005-05-24 21:47:18.000000000 -0400
@@ -0,0 +1,276 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Simple Channel
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Marcin Pycko <m78pl@yahoo.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/options.h"
+#include "asterisk/lock.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+
+static const char desc[] = "Simple Channel";
+static const char type[] = "Simple";
+static const char tdesc[] = "Simple Channel Driver";
+
+static int usecnt =0;
+AST_MUTEX_DEFINE_STATIC(usecnt_lock);
+
+/* Protect the interface list (of simple_pvt's) */
+AST_MUTEX_DEFINE_STATIC(simplelock);
+
+static struct simple_pvt {
+	ast_mutex_t lock;			/* Channel private lock */
+	struct ast_channel *owner;		/* owner of this pvt */
+	char channeltype[AST_MAX_EXTENSION];	/* where we keep channeltype of the event */
+	char context[AST_MAX_EXTENSION];	/* Context to abstract */
+	char dest[AST_MAX_EXTENSION];		/* Destination to abstract */
+	struct simple_pvt *next;		/* Next entity */
+} *simple = NULL;
+
+static struct ast_channel *simple_request(const char *type, int format, void *data, int *cause);
+static int simple_hangup(struct ast_channel *ast);
+
+static const struct ast_channel_tech simple_tech = {
+	.type = type,
+	.description = tdesc,
+	.capabilities = AST_FORMAT_SLINEAR,
+	.requester = simple_request,
+	.send_digit = NULL,
+	.call = NULL,
+	.hangup = simple_hangup,
+	.answer = NULL,
+	.read = NULL,
+	.write = NULL,
+	.exception = NULL,
+	.indicate = NULL,
+	.fixup = NULL,
+};
+
+static int simple_hangup(struct ast_channel *ast)
+{
+	struct simple_pvt *p = ast->tech_pvt;
+	struct simple_pvt *cur, *prev=NULL;
+
+	ast_mutex_lock(&p->lock);
+	ast->tech_pvt = NULL;
+	ast_mutex_unlock(&p->lock);
+	/* Remove from list */
+	ast_mutex_lock(&simplelock);
+	cur = simple;
+	while(cur) {
+		if (cur == p) {
+			if (prev)
+				prev->next = cur->next;
+			else
+				simple = cur->next;
+			break;
+		}
+		prev = cur;
+		cur = cur->next;
+	}
+
+	ast_mutex_unlock(&simplelock);
+	ast_mutex_destroy(&p->lock);
+	free(p);
+
+	/* update usecnt */
+	ast_mutex_lock(&usecnt_lock);
+	usecnt--;
+	ast_mutex_unlock(&usecnt_lock);
+	ast_update_use_count();
+	return 0;
+}
+
+static struct simple_pvt *simple_alloc(char *data, int format)
+{
+	struct simple_pvt *tmp;
+	char *stringp;
+	char *channeltype = NULL, *dest = NULL, *context = NULL;
+	if (!data)
+		return NULL;
+	stringp = ast_strdupa(data);
+	channeltype = strsep(&stringp, "/");
+	context = strsep(&stringp, "/");
+	dest = stringp;
+	if (!channeltype || !context || !dest) {
+		ast_log(LOG_NOTICE, "Format for simple channel is Simple/Context/Dest ('%s' not valid)!\n", data);
+		return NULL;
+	}
+	ast_mutex_lock(&simplelock);
+	tmp = simple;
+	while(tmp) {
+		if (!strcasecmp(tmp->channeltype, channeltype) && !strcasecmp(tmp->context, context) && !strcasecmp(tmp->dest, dest))
+			break;
+		tmp = tmp->next;
+	}
+	ast_mutex_unlock(&simplelock);
+	if (!tmp) {
+		tmp = malloc(sizeof(struct simple_pvt));
+		if (tmp) {
+			memset(tmp, 0, sizeof(struct simple_pvt));
+			ast_mutex_init(&tmp->lock);
+			strncpy(tmp->channeltype, channeltype, sizeof(tmp->channeltype) - 1);
+			strncpy(tmp->context, context, sizeof(tmp->context) - 1);
+			strncpy(tmp->dest, dest, sizeof(tmp->dest) - 1);
+			ast_mutex_lock(&simplelock);
+			tmp->next = simple;
+			simple = tmp;
+			ast_mutex_unlock(&simplelock);
+		}
+	}
+	return tmp;
+}
+
+static struct ast_channel *simple_new(struct simple_pvt *p, int state)
+{
+	struct ast_channel *tmp;
+	tmp = ast_channel_alloc(0);
+	if (!tmp)
+		return NULL;
+	if (tmp) {
+		tmp->tech = &simple_tech;
+		snprintf(tmp->name, sizeof(tmp->name), "Simple/%s/%s-%04x", p->context, p->dest,  rand() & 0xffff);
+		strncpy(tmp->context, p->context, sizeof(tmp->context) - 1);
+		strncpy(tmp->exten, p->dest, sizeof(tmp->exten) - 1);
+		tmp->type = type;
+		ast_setstate(tmp, state);
+		tmp->tech_pvt = p;
+		pbx_builtin_setvar_helper(tmp, "DEVICETECH", p->channeltype);
+		ast_mutex_lock(&usecnt_lock);
+		usecnt++;
+		ast_mutex_unlock(&usecnt_lock);
+		ast_update_use_count();
+		if (ast_pbx_start(tmp)) {
+			ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
+			ast_hangup(tmp);
+			tmp = NULL;
+		}
+	} else
+		ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
+	return tmp;
+}
+
+
+static struct ast_channel *simple_request(const char *type, int format, void *data, int *cause)
+{
+	struct simple_pvt *p;
+	struct ast_channel *chan = NULL;
+	p = simple_alloc(data, format);
+	if (p)
+		chan = simple_new(p, AST_STATE_DOWN);
+	return chan;
+}
+
+static int simple_show(int fd, int argc, char **argv)
+{
+	struct simple_pvt *p;
+
+	if (argc != 3)
+		return RESULT_SHOWUSAGE;
+	ast_mutex_lock(&simplelock);
+	p = simple;
+	while(p) {
+		ast_mutex_lock(&p->lock);
+		ast_cli(fd, "%s -- %s/%s\n", p->owner ? p->owner->name : "<unowned>", p->context, p->dest);
+		ast_mutex_unlock(&p->lock);
+		p = p->next;
+	}
+	if (!simple)
+		ast_cli(fd, "No simple channels in use\n");
+	ast_mutex_unlock(&simplelock);
+	return RESULT_SUCCESS;
+}
+
+static char show_simple_usage[] = 
+"Usage: simple show channels\n"
+"       Provides summary information on simple channels.\n";
+
+static struct ast_cli_entry cli_show_simple = {
+	{ "simple", "show", "channels", NULL }, simple_show, 
+	"Show status of simple channels", show_simple_usage, NULL };
+
+int load_module()
+{
+	/* Make sure we can register our simple channel type */
+	if (ast_channel_register(&simple_tech)) {
+		ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
+		return -1;
+	}
+	ast_cli_register(&cli_show_simple);
+	return 0;
+}
+
+int reload()
+{
+	return 0;
+}
+
+int unload_module()
+{
+	struct simple_pvt *p;
+	/* First, take us out of the channel loop */
+	ast_cli_unregister(&cli_show_simple);
+	ast_channel_unregister(&simple_tech);
+	if (!ast_mutex_lock(&simplelock)) {
+		/* Hangup all interfaces if they have an owner */
+		p = simple;
+		while(p) {
+			if (p->owner)
+				ast_softhangup(p->owner, AST_SOFTHANGUP_APPUNLOAD);
+			p = p->next;
+		}
+		simple = NULL;
+		ast_mutex_unlock(&simplelock);
+	} else {
+		ast_log(LOG_WARNING, "Unable to lock the monitor\n");
+		return -1;
+	}		
+	return 0;
+}
+
+int usecount()
+{
+	int res;
+	ast_mutex_lock(&usecnt_lock);
+	res = usecnt;
+	ast_mutex_unlock(&usecnt_lock);
+	return res;
+}
+
+char *key()
+{
+	return ASTERISK_GPL_KEY;
+}
+
+char *description()
+{
+	return (char *)desc;
+}
+
--- include/asterisk/register_event.h	2005-05-09 16:12:37.000000000 -0400
+++ include/asterisk/register_event.h	2005-05-24 21:47:18.000000000 -0400
@@ -0,0 +1,32 @@
+/*
+ * Asterisk -- A telephony toolkit for Linux.
+ *
+ * Register/Unregister event functions
+ * 
+ * Copyright (C) 1999, Mark Spencer
+ *
+ * Marcin Pycko <m78pl@yahoo.com>
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License
+ */
+
+static inline int register_event(const char *channeltype, const char *context, const char *name, int isregister)
+{
+	int exists = -1;
+	char buf[256];
+	int res = 0;
+	char *event_name = isregister ? "Register" : "Unregister";
+
+	if (strlen(context) && (exists = ast_exists_extension(NULL, context, name, 1, NULL))) {
+		snprintf(buf, sizeof(buf), "%s/%s/%s", channeltype, context, name);
+		if (!ast_request("Simple", AST_FORMAT_SLINEAR, buf, NULL))
+			ast_log(LOG_ERROR, "%s event: can't spawn the pbx channel on (context,extension)=(%s,%s) channel\n", event_name, context, name);
+	} else {
+		if (exists == 0)
+			ast_log(LOG_ERROR, "%s event: the context and/or extension to execute the (un)register event doesn't exist: %s,%s\n", event_name, context, name);
+		res = -1;
+	}
+	return res;
+}
+
