Index: app.c =================================================================== RCS file: /usr/cvsroot/asterisk/app.c,v retrieving revision 1.58 diff -u -r1.58 app.c --- app.c 29 Apr 2005 17:00:33 -0000 1.58 +++ app.c 17 May 2005 17:13:53 -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.209 diff -u -r1.209 app_voicemail.c --- apps/app_voicemail.c 29 Apr 2005 17:00:33 -0000 1.209 +++ apps/app_voicemail.c 17 May 2005 17:13:53 -0000 @@ -85,6 +85,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. @@ -142,6 +145,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 */ @@ -271,6 +275,12 @@ "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n" "be set with the authenticated mailbox.\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"; @@ -279,6 +289,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; @@ -324,6 +335,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); @@ -2917,6 +2968,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; } @@ -3118,6 +3170,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++; @@ -4859,6 +4912,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) @@ -4934,13 +4988,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); @@ -5017,6 +5079,72 @@ } } +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_obj *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_obj(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 ]\n" " Lists all mailboxes currently set up\n"; @@ -5387,6 +5515,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); @@ -5506,6 +5638,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 { @@ -5528,6 +5666,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(); @@ -5541,6 +5680,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 17 May 2005 17:13:53 -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.277 diff -u -r1.277 chan_iax2.c --- channels/chan_iax2.c 2 May 2005 01:34:21 -0000 1.277 +++ channels/chan_iax2.c 17 May 2005 17:13:54 -0000 @@ -38,6 +38,7 @@ #include "asterisk/localtime.h" #include "asterisk/aes.h" #include "asterisk/dnsmgr.h" +#include "asterisk/register_event.h" #include #include #include @@ -265,6 +266,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; @@ -859,6 +862,38 @@ return tmp; } +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 = 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_obj change_mailbox_function = { + .name = "IAX_CHANGE_MAILBOX", + .desc = "Changes the mailbox on a IAX channel", + .syntax = "SIP_HEADER(=)", + .read = func_change_mailbox, +}; + static int get_samples(struct ast_frame *f) { int samples=0; @@ -2960,6 +2995,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); @@ -5432,6 +5524,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); @@ -5512,6 +5605,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, @@ -5519,6 +5613,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 */ @@ -6283,7 +6378,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 */ @@ -6563,6 +6658,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)) @@ -7942,6 +8065,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")) { strncpy(peer->zonetag, v->value, sizeof(peer->zonetag)-1); }/* else if (strcasecmp(v->name,"type")) */ @@ -8932,6 +9058,7 @@ if (iaxs[x]) iax2_destroy(x); ast_manager_unregister( "IAXpeers" ); + ast_custom_function_unregister(&change_mailbox_function); ast_unregister_application(papp); ast_cli_unregister(&cli_show_users); ast_cli_unregister(&cli_show_channels); @@ -9039,6 +9166,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" ); Index: channels/chan_sip.c =================================================================== RCS file: /usr/cvsroot/asterisk/channels/chan_sip.c,v retrieving revision 1.718 diff -u -r1.718 chan_sip.c --- channels/chan_sip.c 2 May 2005 13:44:38 -0000 1.718 +++ channels/chan_sip.c 17 May 2005 17:13:55 -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 @@ -537,6 +538,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 */ @@ -4721,6 +4724,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)) { @@ -4923,6 +4927,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; @@ -5000,6 +5005,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 */ @@ -5371,6 +5377,7 @@ transmit_response_with_date(p, "200 OK", req); peer->lastmsgssent = -1; res = 0; + register_event(channeltype, peer->on_register_context, peer->name, 1); } } } @@ -7825,6 +7832,31 @@ 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 = 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_cli_entry cli_notify = { { "sip", "notify", NULL }, sip_notify, "Send a notify packet to a SIP peer", notify_usage, complete_sipnotify }; static struct ast_cli_entry cli_show_objects = @@ -7878,6 +7910,13 @@ .read = func_header_read, }; +static struct ast_custom_function_obj change_mailbox_function = { + .name = "SIP_CHANGE_MAILBOX", + .desc = "Changes the mailbox on a SIP channel", + .syntax = "SIP_HEADER(=)", + .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) { @@ -10267,6 +10306,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; @@ -11092,6 +11134,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(); @@ -11110,6 +11153,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.42 diff -u -r1.42 iax2-parser.c --- channels/iax2-parser.c 21 Apr 2005 06:02:44 -0000 1.42 +++ channels/iax2-parser.c 17 May 2005 17:13:55 -0000 @@ -193,6 +193,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 }, @@ -543,6 +546,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; @@ -696,6 +702,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 17 May 2005 17:13:55 -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 17 May 2005 17:13:55 -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: include/asterisk/app.h =================================================================== RCS file: /usr/cvsroot/asterisk/include/asterisk/app.h,v retrieving revision 1.34 diff -u -r1.34 app.h --- include/asterisk/app.h 12 Mar 2005 05:37:32 -0000 1.34 +++ include/asterisk/app.h 17 May 2005 17:13:55 -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-17 12:36:56.000000000 -0400 @@ -0,0 +1,276 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Simple Channel + * + * Copyright (C) 1999, Mark Spencer + * + * Marcin Pycko + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include +#include +#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 +#include +#include +#include +#include + + +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 : "", 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-17 12:29:34.000000000 -0400 @@ -0,0 +1,32 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Register/Unregister event functions + * + * Copyright (C) 1999, Mark Spencer + * + * Marcin Pycko + * + * 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; +} +