mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-30 10:33:13 +00:00
res_pjsip: Add handling for incoming unsolicited MWI NOTIFY
A new endpoint parameter "incoming_mwi_mailbox" allows Asterisk to receive unsolicited MWI NOTIFY requests and make them available to other modules via the stasis message bus. res_pjsip_pubsub has a new handler "pubsub_on_rx_mwi_notify_request" that parses a simple-message-summary body and, if endpoint->incoming_mwi_account is set, calls ast_publish_mwi_state with the voice-message counts from the message. Change-Id: I08bae3d16e77af48fcccc2c936acce8fc0ef0f3c
This commit is contained in:
4
CHANGES
4
CHANGES
@@ -54,6 +54,10 @@ res_pjsip
|
|||||||
when dnsmgr refreshes are enabled will be automatically updated with the new
|
when dnsmgr refreshes are enabled will be automatically updated with the new
|
||||||
IP address of a given hostname.
|
IP address of a given hostname.
|
||||||
|
|
||||||
|
* A new endpoint parameter "incoming_mwi_mailbox" allows Asterisk to receive
|
||||||
|
unsolicited MWI NOTIFY requests and make them available to other modules via
|
||||||
|
the stasis message bus.
|
||||||
|
|
||||||
res_musiconhold
|
res_musiconhold
|
||||||
------------------
|
------------------
|
||||||
* By default, when res_musiconhold reloads or unloads, it sends a HUP signal
|
* By default, when res_musiconhold reloads or unloads, it sends a HUP signal
|
||||||
|
@@ -798,6 +798,12 @@
|
|||||||
; dtls_setup=actpass
|
; dtls_setup=actpass
|
||||||
; A dtls_cert_file and a dtls_ca_file still need to be specified.
|
; A dtls_cert_file and a dtls_ca_file still need to be specified.
|
||||||
; Default for this option is "no"
|
; Default for this option is "no"
|
||||||
|
;incoming_mwi_mailbox = ; Mailbox name to use when incoming MWI NOTIFYs are
|
||||||
|
; received.
|
||||||
|
; If an MWI NOTIFY is received FROM this endpoint,
|
||||||
|
; this mailbox will be used when notifying other modules
|
||||||
|
; of MWI status changes. If not set, incoming MWI
|
||||||
|
; NOTIFYs are ignored.
|
||||||
|
|
||||||
;==========================AUTH SECTION OPTIONS=========================
|
;==========================AUTH SECTION OPTIONS=========================
|
||||||
;[auth]
|
;[auth]
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
"""add_incoming_mwi_mailbox
|
||||||
|
|
||||||
|
Revision ID: a1698e8bb9c5
|
||||||
|
Revises: b83645976fdd
|
||||||
|
Create Date: 2017-09-08 13:45:18.937571
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'a1698e8bb9c5'
|
||||||
|
down_revision = 'b83645976fdd'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('ps_endpoints', sa.Column('incoming_mwi_mailbox', sa.String(40)))
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('ps_endpoints', 'incoming_mwi_mailbox')
|
@@ -804,6 +804,8 @@ struct ast_sip_endpoint {
|
|||||||
unsigned int refer_blind_progress;
|
unsigned int refer_blind_progress;
|
||||||
/*! Whether to notifies dialog-info 'early' on INUSE && RINGING state */
|
/*! Whether to notifies dialog-info 'early' on INUSE && RINGING state */
|
||||||
unsigned int notify_early_inuse_ringing;
|
unsigned int notify_early_inuse_ringing;
|
||||||
|
/*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
|
||||||
|
AST_STRING_FIELD_EXTENDED(incoming_mwi_mailbox);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*! URI parameter for symmetric transport */
|
/*! URI parameter for symmetric transport */
|
||||||
|
@@ -1383,4 +1383,24 @@ char *ast_generate_random_string(char *buf, size_t size);
|
|||||||
*/
|
*/
|
||||||
int ast_strings_match(const char *left, const char *op, const char *right);
|
int ast_strings_match(const char *left, const char *op, const char *right);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Read lines from a string buffer
|
||||||
|
* \since 13.18.0
|
||||||
|
*
|
||||||
|
* \param buffer [IN/OUT] A pointer to a char * string with either Unix or Windows line endings
|
||||||
|
*
|
||||||
|
* \return The "next" line
|
||||||
|
*
|
||||||
|
* \warning The original string and *buffer will be modified.
|
||||||
|
*
|
||||||
|
* \details
|
||||||
|
* Both '\n' and '\r\n' are treated as single delimiters but consecutive occurrances of
|
||||||
|
* the delimiters are NOT considered to be a single delimiter. This preserves blank
|
||||||
|
* lines in the input.
|
||||||
|
*
|
||||||
|
* MacOS line endings ('\r') are not supported at this time.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
char *ast_read_line_from_buffer(char **buffer);
|
||||||
|
|
||||||
#endif /* _ASTERISK_STRINGS_H */
|
#endif /* _ASTERISK_STRINGS_H */
|
||||||
|
@@ -369,4 +369,23 @@ equals:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *ast_read_line_from_buffer(char **buffer)
|
||||||
|
{
|
||||||
|
char *start = *buffer;
|
||||||
|
|
||||||
|
if (!buffer || !*buffer || *(*buffer) == '\0') {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*(*buffer) && *(*buffer) != '\n' ) {
|
||||||
|
(*buffer)++;
|
||||||
|
}
|
||||||
|
|
||||||
|
*(*buffer) = '\0';
|
||||||
|
if (*(*buffer - 1) == '\r') {
|
||||||
|
*(*buffer - 1) = '\0';
|
||||||
|
}
|
||||||
|
(*buffer)++;
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
@@ -1025,6 +1025,14 @@
|
|||||||
<para>dtls_setup=actpass</para>
|
<para>dtls_setup=actpass</para>
|
||||||
</description>
|
</description>
|
||||||
</configOption>
|
</configOption>
|
||||||
|
<configOption name="incoming_mwi_mailbox">
|
||||||
|
<synopsis>Mailbox name to use when incoming MWI NOTIFYs are received</synopsis>
|
||||||
|
<description><para>
|
||||||
|
If an MWI NOTIFY is received <emphasis>from</emphasis> this endpoint,
|
||||||
|
this mailbox will be used when notifying other modules of MWI status
|
||||||
|
changes. If not set, incoming MWI NOTIFYs are ignored.
|
||||||
|
</para></description>
|
||||||
|
</configOption>
|
||||||
</configObject>
|
</configObject>
|
||||||
<configObject name="auth">
|
<configObject name="auth">
|
||||||
<synopsis>Authentication type</synopsis>
|
<synopsis>Authentication type</synopsis>
|
||||||
|
@@ -1995,6 +1995,7 @@ int ast_res_pjsip_initialize_configuration(void)
|
|||||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_video_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_video_streams));
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "max_video_streams", "1", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.max_video_streams));
|
||||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "bundle", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.bundle));
|
||||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "webrtc", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_endpoint, media.webrtc));
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "webrtc", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_endpoint, media.webrtc));
|
||||||
|
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "incoming_mwi_mailbox", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, incoming_mwi_mailbox));
|
||||||
|
|
||||||
if (ast_sip_initialize_sorcery_transport()) {
|
if (ast_sip_initialize_sorcery_transport()) {
|
||||||
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
|
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
|
||||||
@@ -2159,6 +2160,9 @@ void *ast_sip_endpoint_alloc(const char *name)
|
|||||||
ao2_cleanup(endpoint);
|
ao2_cleanup(endpoint);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ast_string_field_init_extended(endpoint, incoming_mwi_mailbox);
|
||||||
|
|
||||||
if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
|
if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
|
||||||
ao2_cleanup(endpoint);
|
ao2_cleanup(endpoint);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@@ -31,6 +31,7 @@
|
|||||||
#include <pjsip_simple.h>
|
#include <pjsip_simple.h>
|
||||||
#include <pjlib.h>
|
#include <pjlib.h>
|
||||||
|
|
||||||
|
#include "asterisk/app.h"
|
||||||
#include "asterisk/res_pjsip_pubsub.h"
|
#include "asterisk/res_pjsip_pubsub.h"
|
||||||
#include "asterisk/module.h"
|
#include "asterisk/module.h"
|
||||||
#include "asterisk/linkedlists.h"
|
#include "asterisk/linkedlists.h"
|
||||||
@@ -3402,12 +3403,144 @@ end:
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct simple_message_summary {
|
||||||
|
int messages_waiting;
|
||||||
|
int voice_messages_new;
|
||||||
|
int voice_messages_old;
|
||||||
|
int voice_messages_urgent_new;
|
||||||
|
int voice_messages_urgent_old;
|
||||||
|
char message_account[PJSIP_MAX_URL_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
static int parse_simple_message_summary(char *body,
|
||||||
|
struct simple_message_summary *summary)
|
||||||
|
{
|
||||||
|
char *line;
|
||||||
|
char *buffer;
|
||||||
|
int found_counts = 0;
|
||||||
|
|
||||||
|
if (ast_strlen_zero(body) || !summary) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = ast_strdupa(body);
|
||||||
|
memset(summary, 0, sizeof(*summary));
|
||||||
|
|
||||||
|
while ((line = ast_read_line_from_buffer(&buffer))) {
|
||||||
|
line = ast_str_to_lower(line);
|
||||||
|
|
||||||
|
if (sscanf(line, "voice-message: %d/%d (%d/%d)",
|
||||||
|
&summary->voice_messages_new, &summary->voice_messages_old,
|
||||||
|
&summary->voice_messages_urgent_new, &summary->voice_messages_urgent_old)) {
|
||||||
|
found_counts = 1;
|
||||||
|
} else {
|
||||||
|
sscanf(line, "message-account: %s", summary->message_account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !found_counts;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pj_bool_t pubsub_on_rx_mwi_notify_request(pjsip_rx_data *rdata)
|
||||||
|
{
|
||||||
|
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
|
||||||
|
struct simple_message_summary summary;
|
||||||
|
const char *endpoint_name;
|
||||||
|
char *atsign;
|
||||||
|
char *context;
|
||||||
|
char *body;
|
||||||
|
char *mailbox;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
endpoint = ast_pjsip_rdata_get_endpoint(rdata);
|
||||||
|
if (!endpoint) {
|
||||||
|
ast_debug(1, "Incoming MWI: Endpoint not found in rdata (%p)\n", rdata);
|
||||||
|
rc = 404;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoint_name = ast_sorcery_object_get_id(endpoint);
|
||||||
|
ast_debug(1, "Incoming MWI: Found endpoint: %s\n", endpoint_name);
|
||||||
|
if (ast_strlen_zero(endpoint->incoming_mwi_mailbox)) {
|
||||||
|
ast_debug(1, "Incoming MWI: No incoming mailbox specified for endpoint '%s'\n", endpoint_name);
|
||||||
|
ast_test_suite_event_notify("PUBSUB_NO_INCOMING_MWI_MAILBOX",
|
||||||
|
"Endpoint: %s", endpoint_name);
|
||||||
|
rc = 404;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
mailbox = ast_strdupa(endpoint->incoming_mwi_mailbox);
|
||||||
|
atsign = strchr(mailbox, '@');
|
||||||
|
if (!atsign) {
|
||||||
|
ast_debug(1, "Incoming MWI: No '@' found in endpoint %s's incoming mailbox '%s'. Can't parse context\n",
|
||||||
|
endpoint_name, endpoint->incoming_mwi_mailbox);
|
||||||
|
rc = 404;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
*atsign = '\0';
|
||||||
|
context = atsign + 1;
|
||||||
|
|
||||||
|
body = ast_alloca(rdata->msg_info.msg->body->len + 1);
|
||||||
|
rdata->msg_info.msg->body->print_body(rdata->msg_info.msg->body, body,
|
||||||
|
rdata->msg_info.msg->body->len + 1);
|
||||||
|
|
||||||
|
if (parse_simple_message_summary(body, &summary) != 0) {
|
||||||
|
ast_debug(1, "Incoming MWI: Endpoint: '%s' There was an issue getting message info from body '%s'\n",
|
||||||
|
ast_sorcery_object_get_id(endpoint), body);
|
||||||
|
rc = 404;
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ast_publish_mwi_state(mailbox, context,
|
||||||
|
summary.voice_messages_new, summary.voice_messages_old)) {
|
||||||
|
ast_log(LOG_ERROR, "Incoming MWI: Endpoint: '%s' Could not publish MWI to stasis. "
|
||||||
|
"Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
|
||||||
|
endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
|
||||||
|
summary.voice_messages_new, summary.voice_messages_old,
|
||||||
|
summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
|
||||||
|
rc = 404;
|
||||||
|
} else {
|
||||||
|
ast_debug(1, "Incoming MWI: Endpoint: '%s' Mailbox: %s Message-Account: %s Voice-Messages: %d/%d (%d/%d)\n",
|
||||||
|
endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
|
||||||
|
summary.voice_messages_new, summary.voice_messages_old,
|
||||||
|
summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
|
||||||
|
ast_test_suite_event_notify("PUBSUB_INCOMING_MWI_PUBLISH",
|
||||||
|
"Endpoint: %s\r\n"
|
||||||
|
"Mailbox: %s\r\n"
|
||||||
|
"MessageAccount: %s\r\n"
|
||||||
|
"VoiceMessagesNew: %d\r\n"
|
||||||
|
"VoiceMessagesOld: %d\r\n"
|
||||||
|
"VoiceMessagesUrgentNew: %d\r\n"
|
||||||
|
"VoiceMessagesUrgentOld: %d",
|
||||||
|
endpoint_name, endpoint->incoming_mwi_mailbox, summary.message_account,
|
||||||
|
summary.voice_messages_new, summary.voice_messages_old,
|
||||||
|
summary.voice_messages_urgent_new, summary.voice_messages_urgent_old);
|
||||||
|
rc = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
pjsip_endpt_respond_stateless(ast_sip_get_pjsip_endpoint(), rdata, rc, NULL, NULL, NULL);
|
||||||
|
return PJ_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pj_bool_t pubsub_on_rx_notify_request(pjsip_rx_data *rdata)
|
||||||
|
{
|
||||||
|
if (pj_stricmp2(&rdata->msg_info.msg->body->content_type.type, "application") == 0 &&
|
||||||
|
pj_stricmp2(&rdata->msg_info.msg->body->content_type.subtype, "simple-message-summary") == 0) {
|
||||||
|
return pubsub_on_rx_mwi_notify_request(rdata);
|
||||||
|
}
|
||||||
|
return PJ_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
|
static pj_bool_t pubsub_on_rx_request(pjsip_rx_data *rdata)
|
||||||
{
|
{
|
||||||
if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) {
|
if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, pjsip_get_subscribe_method())) {
|
||||||
return pubsub_on_rx_subscribe_request(rdata);
|
return pubsub_on_rx_subscribe_request(rdata);
|
||||||
} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_publish_method)) {
|
} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_publish_method)) {
|
||||||
return pubsub_on_rx_publish_request(rdata);
|
return pubsub_on_rx_publish_request(rdata);
|
||||||
|
} else if (!pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, &pjsip_notify_method)) {
|
||||||
|
return pubsub_on_rx_notify_request(rdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PJ_FALSE;
|
return PJ_FALSE;
|
||||||
|
Reference in New Issue
Block a user