--- asterisk/channels/chan_iax2.c.PMWI 2005-05-10 15:06:31.000000000 -0500 +++ asterisk/channels/chan_iax2.c 2005-05-10 18:41:25.000000000 -0500 @@ -506,6 +506,11 @@ /* IAX_ flags */ unsigned int flags; + /* Remote message waiting information */ + int remotemwi; /* if this is a remote mwi request */ + int rmwicomm; /* part of a pipe to wake up waiting mwi query task */ + int rmwicount; /* message count for remote MWI */ + /* Transferring status */ int transferring; /* Transfer identifier */ @@ -6096,6 +6101,178 @@ iaxs[fr->callno]->remote_rr.ooo = ies->rr_ooo; } +static int iax2_req_remotemwi(const char *mailbox, int *newmsgs, int *oldmsgs) +{ + int callno; + int iowp[2]; + int timeout = 500; /* Wait 500ms for reply */ + struct pollfd pfd; + int ret; + struct iax_ie_data ied; + struct sockaddr_in sin; + + char wksp[256], *s, p_user[256], p_pass[256]; + char *username = NULL; + char *password = NULL; + char *host = NULL; + char *portno = NULL; + char *extenctx = NULL; + int found; + int sockfd = defaultsockfd; + + ast_log(LOG_DEBUG, "Performing IAX2 remote MWI for '%s'\n", mailbox); + + if (newmsgs) + *newmsgs = 0; + + if (oldmsgs) + *oldmsgs = 0; + + /* + * Parse out the mailbox spec, of the form: + * + * --------- IAX spec ------------- ---- Mailbox ---- + * [username[:password]@]host[:port]/mailbox[@context] + * + * Where host can also be an IAX peer name from iax.conf + */ + memset(wksp, 0, 256); + strncpy(wksp, mailbox, sizeof(wksp)-1); + + s = strchr(wksp, '/'); + if (s) { + *s++ = '\0'; + extenctx = s; + } else + return -1; + + s = strchr(wksp, '@'); + if (s) { + *s++ = '\0'; + host = s; + + username = wksp; + + password = strchr(username, ':'); + if (password) + *password++ = '\0'; + } else + host = wksp; + + portno = strchr(host, ':'); + if (portno) + *portno++ = '\0'; + + /* + * Find peer by peername or hostname (host can be either, + * depending on whether it appears as a peer in iax.conf) + */ + + if (create_addr(&sin, NULL, NULL, NULL, host, NULL, NULL, NULL, NULL, NULL, NULL, p_user, 256, p_pass, 256, &found, NULL, NULL, 0, NULL, 0, &sockfd)) + return -1; + + if (portno) + sin.sin_port = htons(atoi(portno)); + + callno = find_callno(0, 0, &sin, NEW_ALLOW, 1, sockfd); + if (!callno) { + ast_log(LOG_WARNING, "Unable to generate call for remotemwi to '%s'\n", host); + return -1; + } + + ast_mutex_lock(&iaxsl[callno]); + memset(&ied, 0, sizeof(ied)); + + iax_ie_append_short(&ied, IAX_IE_VERSION, IAX_PROTO_VERSION); + if (username) { + iax_ie_append_str(&ied, IAX_IE_USERNAME, username); + if (password) + strncpy(iaxs[callno]->secret, password, sizeof(iaxs[callno]->secret)-1); + } else if (found) { + iax_ie_append_str(&ied, IAX_IE_USERNAME, p_user); + strncpy(iaxs[callno]->secret, p_pass, sizeof(iaxs[callno]->secret)-1); + } + + send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_POKE, 0, ied.buf, ied.pos, -1); + + if (iaxs[callno]) { + if (pipe(iowp)) { + ast_log(LOG_WARNING, "Unable to create pipe for remote MWI I/O wait\n"); + ast_mutex_unlock(&iaxsl[callno]); + return -1; + } + + ast_log(LOG_DEBUG, "IAX2 remote MWI callno %d valid\n", callno); + iaxs[callno]->remotemwi = 1; + iaxs[callno]->rmwicomm = iowp[1]; + + memset(&ied, 0, sizeof(ied)); + iax_ie_append_str(&ied, IAX_IE_CALLED_NUMBER, extenctx); + send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_MWI, 0, ied.buf, ied.pos, -1); + ast_mutex_unlock(&iaxsl[callno]); + } else { + ast_log(LOG_DEBUG, "IAX2 remote MWI callno %d no longer valid\n", callno); + ast_mutex_unlock(&iaxsl[callno]); + return -1; + } + + pfd.fd = iowp[0]; + pfd.events = POLLIN | POLLPRI; + pfd.revents = 0; + + ret = -1; + while (ret < 0) { + ret = poll(&pfd, 1, timeout); + if (ret < 0 || errno != EINTR) + break; + } + + close(iowp[1]); + close(iowp[0]); + + if (ret == 0) { + ast_log(LOG_WARNING, "Timeout on pipe waiting for reponse to remote MWI to '%s'\n", host); + return -1; + } else if (ret < 0) { + ast_log(LOG_WARNING, "Error %d on pipe waiting for reponse to remote MWI to '%s'\n", errno, host); + return -1; + } + + if (iaxs[callno]) { + ast_mutex_lock(&iaxsl[callno]); + ast_log(LOG_DEBUG, "Success IAX2 remote MWI to '%s' for '%s' = %d/%d\n", host, extenctx, + iaxs[callno]->rmwicount >> 8, iaxs[callno]->rmwicount & 0xff); + if (oldmsgs) + *oldmsgs = iaxs[callno]->rmwicount >> 8; + if (newmsgs) + *newmsgs = iaxs[callno]->rmwicount & 0xff; + iaxs[callno]->remotemwi = 0; + iaxs[callno]->rmwicomm = 0; + iax2_destroy_nolock(callno); + ast_mutex_unlock(&iaxsl[callno]); + } + + return 0; +} + +static int iax_has_voicemail(const char *mailbox, const char *folder) +{ + int newmsgs, ret; + + /* XXX folder is ignored right now... */ + ret = iax2_req_remotemwi(mailbox, &newmsgs, NULL); + + if (ret || newmsgs == 0) + return 0; + else + return 1; +} + +static int iax_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs) +{ + return iax2_req_remotemwi(mailbox, newmsgs, oldmsgs); +} + static int socket_read(int *id, int fd, short events, void *cbdata) { struct sockaddr_in sin; @@ -7280,6 +7457,43 @@ else send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_FWDATA, 0, ied0.buf, ied0.pos, -1); break; + case IAX_COMMAND_MWI: + if (ies.called_number) { + /* + * means this is a request from another + * PBX for local mwi information + */ + int new, old, msgcount; + struct iax_ie_data ied; + + ast_log(LOG_DEBUG, "IAX2 MWI frame received, called number %s\n", ies.called_number); + + memset(&ied, 0, sizeof(ied)); + (void)ast_app_messagecount(ies.called_number, &new, &old); + if (new > 255) + new = 255; + if (old > 255) + old = 255; + + msgcount = (old << 8) | new; + iax_ie_append_short(&ied, IAX_IE_MSGCOUNT, msgcount); + + if (iaxs[fr.callno]) + send_command(iaxs[fr.callno], AST_FRAME_IAX, IAX_COMMAND_MWI, 0, ied.buf, ied.pos, -1); + } else { + ast_log(LOG_DEBUG, "IAX2 MWI frame received - response\n"); + /* + * means this should be a reply from a previously + * requested MWI query on this PBX + */ + if (iaxs[fr.callno] && iaxs[fr.callno]->remotemwi) { + const char *anything = "x"; + + iaxs[fr.callno]->rmwicount = ies.msgcount; + (void)write(iaxs[fr.callno]->rmwicomm, anything, sizeof *anything); + } + } + break; default: ast_log(LOG_DEBUG, "Unknown IAX command %d on %d/%d\n", f.subclass, fr.callno, iaxs[fr.callno]->peercallno); memset(&ied0, 0, sizeof(ied0)); @@ -8959,6 +9173,7 @@ int unload_module() { + ast_uninstall_iax_vm_functions(); ast_mutex_destroy(&iaxq.lock); ast_mutex_destroy(&userl.lock); ast_mutex_destroy(&peerl.lock); @@ -9084,6 +9299,7 @@ ast_mutex_unlock(&peerl.lock); reload_firmware(); iax_provision_reload(); + ast_install_iax_vm_functions(iax_has_voicemail, iax_messagecount); return res; } --- asterisk/apps/app_voicemail.c.PMWI 2005-05-10 15:06:30.000000000 -0500 +++ asterisk/apps/app_voicemail.c 2005-05-10 18:30:01.000000000 -0500 @@ -1794,26 +1794,12 @@ struct dirent *de; char fn[256]; char tmp[256]=""; - char *mb, *cur; char *context; - int ret; if (!folder) folder = "INBOX"; /* If no mailbox, return immediately */ if (ast_strlen_zero(mailbox)) return 0; - if (strchr(mailbox, ',')) { - strncpy(tmp, mailbox, sizeof(tmp) - 1); - mb = tmp; - ret = 0; - while((cur = strsep(&mb, ","))) { - if (!ast_strlen_zero(cur)) { - if (has_voicemail(cur, folder)) - return 1; - } - } - return 0; - } strncpy(tmp, mailbox, sizeof(tmp) - 1); context = strchr(tmp, '@'); if (context) { @@ -1841,9 +1827,7 @@ struct dirent *de; char fn[256]; char tmp[256]=""; - char *mb, *cur; char *context; - int ret; if (newmsgs) *newmsgs = 0; if (oldmsgs) @@ -1851,25 +1835,6 @@ /* If no mailbox, return immediately */ if (ast_strlen_zero(mailbox)) return 0; - if (strchr(mailbox, ',')) { - int tmpnew, tmpold; - strncpy(tmp, mailbox, sizeof(tmp) - 1); - mb = tmp; - ret = 0; - while((cur = strsep(&mb, ", "))) { - if (!ast_strlen_zero(cur)) { - if (messagecount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL)) - return -1; - else { - if (newmsgs) - *newmsgs += tmpnew; - if (oldmsgs) - *oldmsgs += tmpold; - } - } - } - return 0; - } strncpy(tmp, mailbox, sizeof(tmp) - 1); context = strchr(tmp, '@'); if (context) { --- asterisk/include/asterisk/app.h.PMWI 2005-05-10 15:06:35.000000000 -0500 +++ asterisk/include/asterisk/app.h 2005-05-10 18:02:10.000000000 -0500 @@ -109,6 +109,11 @@ void ast_uninstall_vm_functions(void); +void ast_install_iax_vm_functions(int (*iax_has_voicemail_func)(const char *mailbox, const char *folder), + int (*iax_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs)); + +void ast_uninstall_iax_vm_functions(void); + /*! Determine if a given mailbox has any voicemail */ int ast_app_has_voicemail(const char *mailbox, const char *folder); --- asterisk/app.c.PMWI 2005-05-10 15:06:26.000000000 -0500 +++ asterisk/app.c 2005-05-10 18:32:19.000000000 -0500 @@ -211,6 +211,8 @@ 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; +static int (*ast_iax_has_voicemail_func)(const char *mailbox, const char *folder) = NULL; +static int (*ast_iax_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs) = NULL; void ast_install_vm_functions(int (*has_voicemail_func)(const char *mailbox, const char *folder), int (*messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs)) @@ -225,35 +227,123 @@ ast_messagecount_func = NULL; } +void ast_install_iax_vm_functions(int (*iax_has_voicemail_func)(const char *mailbox, const char *folder), + int (*iax_messagecount_func)(const char *mailbox, int *newmsgs, int *oldmsgs)) +{ + ast_iax_has_voicemail_func = iax_has_voicemail_func; + ast_iax_messagecount_func = iax_messagecount_func; +} + +void ast_uninstall_iax_vm_functions(void) +{ + ast_iax_has_voicemail_func = NULL; + ast_iax_messagecount_func = NULL; +} + + int ast_app_has_voicemail(const char *mailbox, const char *folder) { static int warned = 0; - if (ast_has_voicemail_func) - return ast_has_voicemail_func(mailbox, folder); + char *workspace, *mbp, *comma; + int ret = 0; - if ((option_verbose > 2) && !warned) { - ast_verbose(VERBOSE_PREFIX_3 "Message check requested for mailbox %s/folder %s but voicemail not loaded.\n", mailbox, folder ? folder : "INBOX"); - warned++; + if (!mailbox) + return 0; + + if (!ast_has_voicemail_func && !ast_iax_has_voicemail_func) { + if ((option_verbose > 2) && !warned) { + ast_verbose(VERBOSE_PREFIX_3 "Message check requested for mailbox %s/folder %s but voicemail checks not loaded.\n", mailbox, folder ? folder : "INBOX"); + warned++; + } + return 0; } - return 0; + + workspace = mbp = strdup(mailbox); + if (!workspace) + return 0; + + while (*mbp) { + if ((comma = strchr(mbp, ','))) + *comma++ = '\0'; + + if (ast_iax_has_voicemail_func && + strncasecmp("iax2/", mbp, 5) == 0) { + ret = ast_iax_has_voicemail_func(mbp + 5, folder); + } else + ret = ast_has_voicemail_func(mbp, folder); + + if (ret) + break; + + if (comma) + mbp = comma; + else + break; + } + + free(workspace); + return ret; } int ast_app_messagecount(const char *mailbox, int *newmsgs, int *oldmsgs) { static int warned = 0; - if (newmsgs) + char *workspace, *mbp, *comma; + int tmpnew = 0, tmpold = 0, ret = 0; + int *ptmpnew = NULL, *ptmpold = NULL; + + if (newmsgs) { *newmsgs = 0; - if (oldmsgs) + ptmpnew = &tmpnew; + } + + if (oldmsgs) { *oldmsgs = 0; - if (ast_messagecount_func) - return ast_messagecount_func(mailbox, newmsgs, oldmsgs); + ptmpold = &tmpold; + } + + if (!mailbox) + return 0; - if (!warned && (option_verbose > 2)) { - warned++; - ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s but voicemail not loaded.\n", mailbox); + if (!ast_has_voicemail_func && !ast_iax_has_voicemail_func) { + if (!warned && (option_verbose > 2)) { + warned++; + ast_verbose(VERBOSE_PREFIX_3 "Message count requested for mailbox %s but voicemail checks not loaded.\n", mailbox); + } + return 0; + } + + workspace = mbp = strdup(mailbox); + if (!workspace) + return 0; + + while (*mbp) { + if ((comma = strchr(mbp, ','))) + *comma++ = '\0'; + + if (ast_iax_has_voicemail_func && + strncasecmp("iax2/", mbp, 5) == 0) { + ret = ast_iax_messagecount_func(mbp + 5, ptmpnew, ptmpold); + } else + ret = ast_messagecount_func(mbp, ptmpnew, ptmpold); + + /* XXX messagecount function always returns zero.... */ + if (ret == 0) { + if (newmsgs) + *newmsgs += tmpnew; + + if (oldmsgs) + *oldmsgs += tmpold; + } + + if (comma) + mbp = comma; + else + break; } + free(workspace); return 0; }