Index: chan_sip.c =================================================================== --- chan_sip.c (revision 168625) +++ chan_sip.c (working copy) @@ -2190,6 +2190,7 @@ static int transmit_message_with_text(struct sip_pvt *p, const char *text); static int transmit_refer(struct sip_pvt *p, const char *dest); static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten); +static int transmit_notify_with_mwi_ex(struct sip_pvt *p, int newmsgs, int oldmsgs, int newurgentmsgs, int oldurgentmsgs, const char *vmexten); static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate); static int transmit_notify_custom(struct sip_pvt *p, struct ast_variable *vars); static int transmit_register(struct sip_registry *r, int sipmethod, const char *auth, const char *authheader); @@ -2279,6 +2280,7 @@ static void ast_quiet_chan(struct ast_channel *chan); static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target); static int do_magic_pickup(struct ast_channel *channel, const char *extension, const char *context); +static int get_mwi_parameters(struct sip_request *req, int* messages_waiting, int* new_voicemail_messages, int* old_voicemail_messages, int* new_urgent_voicemail_messages, int* old_urgent_voicemail_messages); /*! * \brief generic function for determining if a correct transport is being @@ -6786,9 +6788,17 @@ /* We do support REFER, but not outside of a dialog yet */ transmit_response_using_temp(callid, sin, 1, intended_method, req, "603 Declined (no dialog)"); } else if (intended_method == SIP_NOTIFY) { - /* We do not support out-of-dialog NOTIFY either, - like voicemail notification, so cancel that early */ - transmit_response_using_temp(callid, sin, 1, intended_method, req, "489 Bad event"); + /* We can support out-of-dialog NOTIFY for voicemail notifications, so do not cancel that early */ + + ast_debug(1, "***** Received a unsolicited NOTIFY request. Creating a dialog\n"); + /* Ok, time to create a new SIP dialog object, a pvt */ + if ((p = sip_alloc(callid, sin, 1, intended_method))) { + /* Ok, we've created a dialog, let's go and process it */ + sip_pvt_lock(p); + } else { + /* Should the reponse be 500 Internal Server Error? */ + transmit_response_using_temp(callid, sin, 1, intended_method, req, "489 Bad event"); + } } else { /* Ok, time to create a new SIP dialog object, a pvt */ if ((p = sip_alloc(callid, sin, 1, intended_method))) { @@ -10211,7 +10221,7 @@ - We use the SIP Event package message-summary MIME type defaults to "application/simple-message-summary"; */ -static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten) +static int transmit_notify_with_mwi_ex(struct sip_pvt *p, int newmsgs, int oldmsgs, int newurgentmsgs, int oldurgentmsgs, const char *vmexten) { struct sip_request req; struct ast_str *out = ast_str_alloca(500); @@ -10226,9 +10236,18 @@ /* Cisco has a bug in the SIP stack where it can't accept the (0/0) notification. This can temporarily be disabled in sip.conf with the "buggymwi" option */ + char urgentmsgs[50]; + urgentmsgs[0] = '\0'; + if(ast_test_flag(&p->flags[1], SIP_PAGE2_BUGGY_MWI)) { + strcpy(urgentmsgs, ""); + } else { + sprintf(urgentmsgs, " (%d/%d)",newurgentmsgs,oldurgentmsgs); + } + ast_str_append(&out, 0, "Voice-Message: %d/%d%s\r\n", - newmsgs, oldmsgs, (ast_test_flag(&p->flags[1], SIP_PAGE2_BUGGY_MWI) ? "" : " (0/0)")); + newmsgs, oldmsgs, urgentmsgs); + if (p->subscribed) { if (p->expiry) add_header(&req, "Subscription-State", "active"); @@ -10244,6 +10263,18 @@ return send_request(p, &req, XMIT_RELIABLE, p->ocseq); } + +/*! \brief Notify user of messages waiting in voicemail +\note - Notification only works for registered peers with mailbox= definitions + in sip.conf + - We use the SIP Event package message-summary + MIME type defaults to "application/simple-message-summary"; + */ +static int transmit_notify_with_mwi(struct sip_pvt *p, int newmsgs, int oldmsgs, const char *vmexten) +{ + return transmit_notify_with_mwi_ex(p, newmsgs, oldmsgs, 0, 0, vmexten); +} + /*! \brief Notify a transferring party of the status of transfer (RFC3515) */ static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate) { @@ -17732,6 +17763,69 @@ return NULL; } +/*! \brief Parse the NOTIFY SIP request body to extract MWI related parameters */ +static int get_mwi_parameters(struct sip_request *req, int* messages_waiting, int* new_voicemail_messages, int* old_voicemail_messages, int* new_urgent_voicemail_messages, int* old_urgent_voicemail_messages) +{ + int response_code = 400; /* Bad request*/ + int line_index = 0; + for (line_index = 0; line_index < req->lines; line_index++) { + char body_line[255]; + ast_copy_string(body_line,req->line[line_index],255); + + /*Example: Messages-Waiting: yes [no] */ + char* msg_waiting; + if( (msg_waiting = strstr(body_line,"Messages-Waiting")) ) { + if( (msg_waiting = strstr(body_line,"yes")) ) { + *messages_waiting = 1; + *new_voicemail_messages = 1; + response_code = 200; + } else if( (msg_waiting = strstr(body_line,"no")) ) { + *messages_waiting = 0; + response_code = 200; + } + } + if (sipdebug) + ast_debug(2, "messages_waiting=%d, new_voicemail_messages=%d, response_code=%d\n", + *messages_waiting, *new_voicemail_messages, response_code); + + /*Example: Voice-Message: 1/3 (0/1)*/ + const char* voice_messages; + if( (voice_messages = strstr(body_line,"Voice-Message")) ) { + char* delimiter; + if( (delimiter = strchr(voice_messages, '/')) ) { + char new_voice_messages[10]=""; + strncpy(new_voice_messages,voice_messages,(delimiter-voice_messages)-1); + *new_voicemail_messages = atoi(new_voice_messages); + *messages_waiting = (new_voicemail_messages) ? 1 : 0; + response_code = 200; + + char* delimiter2; + if( (delimiter2 = strchr(delimiter, '(')) ) { + char old_voice_messages[10]=""; + strncpy(old_voice_messages,delimiter,(delimiter2-delimiter)-1); + *old_voicemail_messages = atoi(old_voice_messages); + + char* delimiter3; + if( (delimiter3 = strchr(delimiter2, '/')) ) { + char new_urgent_voice_messages[10]=""; + strncpy(new_urgent_voice_messages,delimiter2,(delimiter3-delimiter2)-1); + *new_urgent_voicemail_messages = atoi(new_urgent_voice_messages); + + char* delimiter4; + if( (delimiter4 = strchr(delimiter3, ')')) ) { + char old_urgent_voice_messages[10]=""; + strncpy(old_urgent_voice_messages,delimiter3,(delimiter4-delimiter3)-1); + *old_urgent_voicemail_messages = atoi(old_urgent_voice_messages); + } + } + } + } + } + } + return response_code; +} + + /*! \brief Handle incoming notifications */ static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, char *e) { @@ -17850,28 +17944,148 @@ /* Confirm that we received this packet */ transmit_response(p, "200 OK", req); - } else if (p->mwi && !strcmp(event, "message-summary")) { - char *c = ast_strdupa(get_body(req, "Voice-Message", ':')); + } else if (!strcmp(event, "message-summary")) { + if (p->mwi) { + char *c = ast_strdupa(get_body(req, "Voice-Message", ':')); - if (!ast_strlen_zero(c)) { - char *old = strsep(&c, " "); - char *new = strsep(&old, "/"); - struct ast_event *event; + if (!ast_strlen_zero(c)) { + char *old = strsep(&c, " "); + char *new = strsep(&old, "/"); + struct ast_event *event; - if ((event = ast_event_new(AST_EVENT_MWI, - AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, p->mwi->mailbox, - AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "SIP_Remote", - AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(new), - AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(old), - AST_EVENT_IE_END))) { - ast_event_queue_and_cache(event, - AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, - AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, - AST_EVENT_IE_END); + if ((event = ast_event_new(AST_EVENT_MWI, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, p->mwi->mailbox, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, "SIP_Remote", + AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(new), + AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, atoi(old), + AST_EVENT_IE_END))) { + ast_event_queue_and_cache(event, + AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, + AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, + AST_EVENT_IE_END); + } } + + transmit_response(p, "200 OK", req); } + else { + int response_code = 489; /* Bad event*/ + int messages_waiting = 0; + int new_voicemail_messages = 0; + int old_voicemail_messages = 0; + int new_urgent_voicemail_messages = 0; + int old_urgent_voicemail_messages = 0; - transmit_response(p, "200 OK", req); + const char *Content_Type; + Content_Type = get_header(req, "Content-Type"); + if (!strcasecmp(Content_Type, "application/simple-message-summary")) { + response_code = get_mwi_parameters(req, &messages_waiting, &new_voicemail_messages, &old_voicemail_messages, &new_urgent_voicemail_messages, &old_urgent_voicemail_messages); + } + + if (sipdebug) + ast_debug(2, "Processing : %s, Content-Type: %s, messages_waiting=%d, new_voicemail_messages=%d, old_voicemail_messages=%d\n", + event,Content_Type,messages_waiting,new_voicemail_messages,old_voicemail_messages); + + if (sipdebug) + ast_debug(2, "Processing : %s, response_code=%d\n", event,response_code); + + if(response_code == 200) { + struct sip_peer *peer; + struct sip_pvt *pvt = NULL; + + char* header_value; + char to[256]; + char peername[50]; + ast_copy_string(to, get_header(req, "To"), sizeof(to)); + + if(!ast_strlen_zero(to)) { + header_value = to+3; + memset(peername, 0, sizeof(peername)); + get_calleridname(to, peername, sizeof(peername)); + + if(!strlen(peername)) { + char* peer_start; + if( (peer_start = strstr(to,"sips:")) ) { + peer_start += 5; + } else if( (peer_start = strstr(to,"sip:")) ) { + peer_start += 4; + } + + if(peer_start) { + char* peer_end; + if( (peer_end = strstr(peer_start,"@")) ) { + strncpy(peername,peer_start,(peer_end-peer_start)); + } + } + } + } + + if (sipdebug) + ast_debug(2, "Finding peer for : %s\n", peername); + + peer = find_peer(peername, NULL, TRUE, FINDALLDEVICES, FALSE); + /* Found the peer and it does not have an active suncription */ + if (peer && !peer->mwipvt) { + res = -1; + response_code = 500; + /* Build temporary dialog for this message*/ + if ((pvt = sip_alloc(NULL, NULL, 0, SIP_NOTIFY))) { + if (!create_addr_from_peer(pvt, peer)) { + /* Recalculate our side, and recalculate Call ID*/ + ast_sip_ouraddrfor(&pvt->sa.sin_addr, &pvt->ourip); + build_via(pvt); + build_callid_pvt(pvt); + /* Destroy this session after 32 secs*/ + sip_scheddestroy(pvt, DEFAULT_TRANS_TIMEOUT); + + res = 0; + response_code = 200; + } + else { + /* Maybe they're not registered, etc.*/ + sip_destroy(pvt); + } + } + } + + if(response_code == 200) { + /* Send MWI NOTIFY*/ + ast_set_flag(&pvt->flags[0], SIP_OUTGOING); + transmit_notify_with_mwi_ex(pvt, new_voicemail_messages, old_voicemail_messages, new_urgent_voicemail_messages, old_urgent_voicemail_messages, peer->vmexten); + } else { + if (sipdebug) + ast_debug(2, "NOTIFY not sent, response_code=%d\n", response_code); + } + } + + char response_str[255]; + switch(response_code) { + case 400: + ast_copy_string(response_str,"400 Bad Request Asterisk",255); + break; + case 404: + ast_copy_string(response_str,"404 Not Found",255); + break; + case 500: + ast_copy_string(response_str,"500 Internal Server Error",255); + break; + case 200: + ast_copy_string(response_str,"200 OK",255); + break; + case 489: + default: + ast_copy_string(response_str,"489 Bad Event",255); + break; + } + + if (sipdebug) + ast_debug(2, "Responding to incoming NOTIFY with %d\n",response_code); + + transmit_response(p, (const char*)response_str, req); + if(res == 0) { + res = (response_code != 200) ? -1 : 0; + } + } } else { /* We don't understand this event. */ transmit_response(p, "489 Bad event", req);