Implement ticket #982: Support for SIP Message Summary/Message Waiting Indication (MWI, RFC 3842)
- PJSIP-SIMPLE: - implement MWI - PJSUA-LIB: - added "mwi_enabled" flag in account config - added "on_mwi_info" callback - pjsua app: - added "--mwi" option to enable MWI on account - added simple callback to log the NOTIFY message - other: - added SIPp scenario files to simulate UAS side - build: - added MWI support on VS6, VS2005, MMP, and Makefile git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2968 74dad513-b988-da41-8d7b-12977e46ad98remotes/origin/iphone
parent
610973a068
commit
4dd961b2fb
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,208 @@
|
||||
/* $Id$ */
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef __PJSIP_SIMPLE_MWI_H__
|
||||
#define __PJSIP_SIMPLE_MWI_H__
|
||||
|
||||
/**
|
||||
* @file mwi.h
|
||||
* @brief SIP Extension for MWI (RFC 3842)
|
||||
*/
|
||||
#include <pjsip-simple/evsub.h>
|
||||
#include <pjsip/sip_msg.h>
|
||||
|
||||
|
||||
PJ_BEGIN_DECL
|
||||
|
||||
|
||||
/**
|
||||
* @defgroup mwi SIP Message Summary and Message Waiting Indication (RFC 3842)
|
||||
* @ingroup PJSIP_SIMPLE
|
||||
* @brief Support for SIP MWI Extension (RFC 3842)
|
||||
* @{
|
||||
*
|
||||
* This module implements RFC 3842: A Message Summary and Message Waiting
|
||||
* Indication Event Package for the Session Initiation Protocol (SIP).
|
||||
* It uses the SIP Event Notification framework (evsub.h) and extends the
|
||||
* framework by implementing "message-summary" event package.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the MWI module and register it as endpoint module and
|
||||
* package to the event subscription module.
|
||||
*
|
||||
* @param endpt The endpoint instance.
|
||||
* @param mod_evsub The event subscription module instance.
|
||||
*
|
||||
* @return PJ_SUCCESS if the module is successfully
|
||||
* initialized and registered to both endpoint
|
||||
* and the event subscription module.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_init_module(pjsip_endpoint *endpt,
|
||||
pjsip_module *mod_evsub);
|
||||
|
||||
/**
|
||||
* Get the MWI module instance.
|
||||
*
|
||||
* @return The MWI module instance.
|
||||
*/
|
||||
PJ_DECL(pjsip_module*) pjsip_mwi_instance(void);
|
||||
|
||||
/**
|
||||
* Create MWI client subscription session.
|
||||
*
|
||||
* @param dlg The underlying dialog to use.
|
||||
* @param user_cb Pointer to callbacks to receive MWI subscription
|
||||
* events.
|
||||
* @param options Option flags. Currently only PJSIP_EVSUB_NO_EVENT_ID
|
||||
* is recognized.
|
||||
* @param p_evsub Pointer to receive the MWI subscription
|
||||
* session.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg,
|
||||
const pjsip_evsub_user *user_cb,
|
||||
unsigned options,
|
||||
pjsip_evsub **p_evsub );
|
||||
|
||||
/**
|
||||
* Create MWI server subscription session.
|
||||
*
|
||||
* @param dlg The underlying dialog to use.
|
||||
* @param user_cb Pointer to callbacks to receive MWI subscription
|
||||
* events.
|
||||
* @param rdata The incoming SUBSCRIBE request that creates the event
|
||||
* subscription.
|
||||
* @param p_evsub Pointer to receive the MWI subscription
|
||||
* session.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg,
|
||||
const pjsip_evsub_user *user_cb,
|
||||
pjsip_rx_data *rdata,
|
||||
pjsip_evsub **p_evsub );
|
||||
|
||||
/**
|
||||
* Forcefully destroy the MWI subscription. This function should only
|
||||
* be called on special condition, such as when the subscription
|
||||
* initialization has failed. For other conditions, application MUST terminate
|
||||
* the subscription by sending the appropriate un(SUBSCRIBE) or NOTIFY.
|
||||
*
|
||||
* @param sub The MWI subscription.
|
||||
* @param notify Specify whether the state notification callback
|
||||
* should be called.
|
||||
*
|
||||
* @return PJ_SUCCESS if subscription session has been destroyed.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub,
|
||||
pj_bool_t notify );
|
||||
|
||||
/**
|
||||
* Call this function to create request to initiate MWI subscription, to
|
||||
* refresh subcription, or to request subscription termination.
|
||||
*
|
||||
* @param sub Client subscription instance.
|
||||
* @param expires Subscription expiration. If the value is set to zero,
|
||||
* this will request unsubscription.
|
||||
* @param p_tdata Pointer to receive the request.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub,
|
||||
pj_int32_t expires,
|
||||
pjsip_tx_data **p_tdata);
|
||||
|
||||
/**
|
||||
* Accept the incoming subscription request by sending 2xx response to
|
||||
* incoming SUBSCRIBE request.
|
||||
*
|
||||
* @param sub Server subscription instance.
|
||||
* @param rdata The incoming subscription request message.
|
||||
* @param st_code Status code, which MUST be final response.
|
||||
* @param hdr_list Optional list of headers to be added in the response.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub,
|
||||
pjsip_rx_data *rdata,
|
||||
int st_code,
|
||||
const pjsip_hdr *hdr_list );
|
||||
|
||||
/**
|
||||
* For notifier, create NOTIFY request to subscriber, and set the state
|
||||
* of the subscription.
|
||||
*
|
||||
* @param sub The server subscription (notifier) instance.
|
||||
* @param state New state to set.
|
||||
* @param state_str The state string name, if state contains value other
|
||||
* than active, pending, or terminated. Otherwise this
|
||||
* argument is ignored.
|
||||
* @param reason Specify reason if new state is terminated, otherwise
|
||||
* put NULL.
|
||||
* @param mime_type MIME type/content type of the message body.
|
||||
* @param body Message body to be included in the NOTIFY request.
|
||||
* @param p_tdata Pointer to receive the request.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_notify( pjsip_evsub *sub,
|
||||
pjsip_evsub_state state,
|
||||
const pj_str_t *state_str,
|
||||
const pj_str_t *reason,
|
||||
const pjsip_media_type *mime_type,
|
||||
const pj_str_t *body,
|
||||
pjsip_tx_data **p_tdata);
|
||||
|
||||
/**
|
||||
* Create NOTIFY request containing message body from the last NOITFY
|
||||
* message created.
|
||||
*
|
||||
* @param sub Server subscription object.
|
||||
* @param p_tdata Pointer to receive request.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub,
|
||||
pjsip_tx_data **p_tdata );
|
||||
|
||||
|
||||
/**
|
||||
* Send request message that was previously created with initiate(), notify(),
|
||||
* or current_notify(). Application may also send request created with other
|
||||
* functions, e.g. authentication. But the request MUST be either request
|
||||
* that creates/refresh subscription or NOTIFY request.
|
||||
*
|
||||
* @param sub The subscription object.
|
||||
* @param tdata Request message to be sent.
|
||||
*
|
||||
* @return PJ_SUCCESS on success.
|
||||
*/
|
||||
PJ_DECL(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub,
|
||||
pjsip_tx_data *tdata );
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
PJ_END_DECL
|
||||
|
||||
|
||||
#endif /* __PJSIP_SIMPLE_MWI_H__ */
|
@ -0,0 +1,599 @@
|
||||
/* $Id$ */
|
||||
/*
|
||||
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
|
||||
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <pjsip-simple/mwi.h>
|
||||
#include <pjsip-simple/errno.h>
|
||||
#include <pjsip-simple/evsub_msg.h>
|
||||
#include <pjsip/sip_module.h>
|
||||
#include <pjsip/sip_endpoint.h>
|
||||
#include <pjsip/sip_dialog.h>
|
||||
#include <pj/assert.h>
|
||||
#include <pj/guid.h>
|
||||
#include <pj/log.h>
|
||||
#include <pj/os.h>
|
||||
#include <pj/pool.h>
|
||||
#include <pj/string.h>
|
||||
|
||||
|
||||
#define THIS_FILE "mwi.c"
|
||||
#define MWI_DEFAULT_EXPIRES 3600
|
||||
|
||||
/*
|
||||
* MWI module (mod-mdi)
|
||||
*/
|
||||
static struct pjsip_module mod_mwi =
|
||||
{
|
||||
NULL, NULL, /* prev, next. */
|
||||
{ "mod-mwi", 7 }, /* Name. */
|
||||
-1, /* Id */
|
||||
PJSIP_MOD_PRIORITY_DIALOG_USAGE,/* Priority */
|
||||
NULL, /* load() */
|
||||
NULL, /* start() */
|
||||
NULL, /* stop() */
|
||||
NULL, /* unload() */
|
||||
NULL, /* on_rx_request() */
|
||||
NULL, /* on_rx_response() */
|
||||
NULL, /* on_tx_request. */
|
||||
NULL, /* on_tx_response() */
|
||||
NULL, /* on_tsx_state() */
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* This structure describe an mwi agent (both client and server)
|
||||
*/
|
||||
typedef struct pjsip_mwi
|
||||
{
|
||||
pjsip_evsub *sub; /**< Event subscribtion record. */
|
||||
pjsip_dialog *dlg; /**< The dialog. */
|
||||
pjsip_evsub_user user_cb; /**< The user callback. */
|
||||
|
||||
/* These are for server subscriptions */
|
||||
pj_pool_t *body_pool; /**< Pool to save message body */
|
||||
pjsip_media_type mime_type; /**< MIME type of last msg body */
|
||||
pj_str_t body; /**< Last sent message body */
|
||||
} pjsip_mwi;
|
||||
|
||||
|
||||
/*
|
||||
* Forward decl for evsub callbacks.
|
||||
*/
|
||||
static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
|
||||
static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
|
||||
pjsip_event *event);
|
||||
static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub,
|
||||
pjsip_rx_data *rdata,
|
||||
int *p_st_code,
|
||||
pj_str_t **p_st_text,
|
||||
pjsip_hdr *res_hdr,
|
||||
pjsip_msg_body **p_body);
|
||||
static void mwi_on_evsub_rx_notify( pjsip_evsub *sub,
|
||||
pjsip_rx_data *rdata,
|
||||
int *p_st_code,
|
||||
pj_str_t **p_st_text,
|
||||
pjsip_hdr *res_hdr,
|
||||
pjsip_msg_body **p_body);
|
||||
static void mwi_on_evsub_client_refresh(pjsip_evsub *sub);
|
||||
static void mwi_on_evsub_server_timeout(pjsip_evsub *sub);
|
||||
|
||||
|
||||
/*
|
||||
* Event subscription callback for mwi.
|
||||
*/
|
||||
static pjsip_evsub_user mwi_user =
|
||||
{
|
||||
&mwi_on_evsub_state,
|
||||
&mwi_on_evsub_tsx_state,
|
||||
&mwi_on_evsub_rx_refresh,
|
||||
&mwi_on_evsub_rx_notify,
|
||||
&mwi_on_evsub_client_refresh,
|
||||
&mwi_on_evsub_server_timeout,
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Some static constants.
|
||||
*/
|
||||
static const pj_str_t STR_EVENT = { "Event", 5 };
|
||||
static const pj_str_t STR_MWI = { "message-summary", 15 };
|
||||
static const pj_str_t STR_APP_SIMPLE_SMS = { "application/simple-message-summary", 34};
|
||||
|
||||
/*
|
||||
* Init mwi module.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_init_module( pjsip_endpoint *endpt,
|
||||
pjsip_module *mod_evsub)
|
||||
{
|
||||
pj_status_t status;
|
||||
pj_str_t accept[1];
|
||||
|
||||
/* Check arguments. */
|
||||
PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
|
||||
|
||||
/* Must have not been registered */
|
||||
PJ_ASSERT_RETURN(mod_mwi.id == -1, PJ_EINVALIDOP);
|
||||
|
||||
/* Register to endpoint */
|
||||
status = pjsip_endpt_register_module(endpt, &mod_mwi);
|
||||
if (status != PJ_SUCCESS)
|
||||
return status;
|
||||
|
||||
accept[0] = STR_APP_SIMPLE_SMS;
|
||||
|
||||
/* Register event package to event module. */
|
||||
status = pjsip_evsub_register_pkg( &mod_mwi, &STR_MWI,
|
||||
MWI_DEFAULT_EXPIRES,
|
||||
PJ_ARRAY_SIZE(accept), accept);
|
||||
if (status != PJ_SUCCESS) {
|
||||
pjsip_endpt_unregister_module(endpt, &mod_mwi);
|
||||
return status;
|
||||
}
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Get mwi module instance.
|
||||
*/
|
||||
PJ_DEF(pjsip_module*) pjsip_mwi_instance(void)
|
||||
{
|
||||
return &mod_mwi;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create client subscription.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_create_uac( pjsip_dialog *dlg,
|
||||
const pjsip_evsub_user *user_cb,
|
||||
unsigned options,
|
||||
pjsip_evsub **p_evsub )
|
||||
{
|
||||
pj_status_t status;
|
||||
pjsip_mwi *mwi;
|
||||
pjsip_evsub *sub;
|
||||
|
||||
PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
|
||||
|
||||
PJ_UNUSED_ARG(options);
|
||||
|
||||
pjsip_dlg_inc_lock(dlg);
|
||||
|
||||
/* Create event subscription */
|
||||
status = pjsip_evsub_create_uac( dlg, &mwi_user, &STR_MWI,
|
||||
options, &sub);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_return;
|
||||
|
||||
/* Create mwi */
|
||||
mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
|
||||
mwi->dlg = dlg;
|
||||
mwi->sub = sub;
|
||||
if (user_cb)
|
||||
pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
|
||||
|
||||
/* Attach to evsub */
|
||||
pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
|
||||
|
||||
*p_evsub = sub;
|
||||
|
||||
on_return:
|
||||
pjsip_dlg_dec_lock(dlg);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create server subscription.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_create_uas( pjsip_dialog *dlg,
|
||||
const pjsip_evsub_user *user_cb,
|
||||
pjsip_rx_data *rdata,
|
||||
pjsip_evsub **p_evsub )
|
||||
{
|
||||
pjsip_accept_hdr *accept;
|
||||
pjsip_event_hdr *event;
|
||||
pjsip_evsub *sub;
|
||||
pjsip_mwi *mwi;
|
||||
char obj_name[PJ_MAX_OBJ_NAME];
|
||||
pj_status_t status;
|
||||
|
||||
/* Check arguments */
|
||||
PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
|
||||
|
||||
/* Must be request message */
|
||||
PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
|
||||
PJSIP_ENOTREQUESTMSG);
|
||||
|
||||
/* Check that request is SUBSCRIBE */
|
||||
PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
|
||||
&pjsip_subscribe_method)==0,
|
||||
PJSIP_SIMPLE_ENOTSUBSCRIBE);
|
||||
|
||||
/* Check that Event header contains "mwi" */
|
||||
event = (pjsip_event_hdr*)
|
||||
pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
|
||||
if (!event) {
|
||||
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
|
||||
}
|
||||
if (pj_stricmp(&event->event_type, &STR_MWI) != 0) {
|
||||
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
|
||||
}
|
||||
|
||||
/* Check that request contains compatible Accept header. */
|
||||
accept = (pjsip_accept_hdr*)
|
||||
pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
|
||||
if (accept) {
|
||||
unsigned i;
|
||||
for (i=0; i<accept->count; ++i) {
|
||||
if (pj_stricmp(&accept->values[i], &STR_APP_SIMPLE_SMS)==0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i==accept->count) {
|
||||
/* Nothing is acceptable */
|
||||
return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
|
||||
}
|
||||
|
||||
} else {
|
||||
/* No Accept header.
|
||||
* Assume client supports "application/simple-message-summary"
|
||||
*/
|
||||
}
|
||||
|
||||
/* Lock dialog */
|
||||
pjsip_dlg_inc_lock(dlg);
|
||||
|
||||
|
||||
/* Create server subscription */
|
||||
status = pjsip_evsub_create_uas( dlg, &mwi_user, rdata, 0, &sub);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_return;
|
||||
|
||||
/* Create server mwi subscription */
|
||||
mwi = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_mwi);
|
||||
mwi->dlg = dlg;
|
||||
mwi->sub = sub;
|
||||
if (user_cb)
|
||||
pj_memcpy(&mwi->user_cb, user_cb, sizeof(pjsip_evsub_user));
|
||||
|
||||
pj_ansi_snprintf(obj_name, PJ_MAX_OBJ_NAME, "mwibd%p", dlg->pool);
|
||||
mwi->body_pool = pj_pool_create(dlg->pool->factory, obj_name,
|
||||
512, 512, NULL);
|
||||
|
||||
/* Attach to evsub */
|
||||
pjsip_evsub_set_mod_data(sub, mod_mwi.id, mwi);
|
||||
|
||||
/* Done: */
|
||||
*p_evsub = sub;
|
||||
|
||||
on_return:
|
||||
pjsip_dlg_dec_lock(dlg);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Forcefully terminate mwi.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_terminate( pjsip_evsub *sub,
|
||||
pj_bool_t notify )
|
||||
{
|
||||
return pjsip_evsub_terminate(sub, notify);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create SUBSCRIBE
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_initiate( pjsip_evsub *sub,
|
||||
pj_int32_t expires,
|
||||
pjsip_tx_data **p_tdata)
|
||||
{
|
||||
return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
|
||||
p_tdata);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Accept incoming subscription.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_accept( pjsip_evsub *sub,
|
||||
pjsip_rx_data *rdata,
|
||||
int st_code,
|
||||
const pjsip_hdr *hdr_list )
|
||||
{
|
||||
return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
|
||||
}
|
||||
|
||||
/*
|
||||
* Create message body and attach it to the (NOTIFY) request.
|
||||
*/
|
||||
static pj_status_t mwi_create_msg_body( pjsip_mwi *mwi,
|
||||
pjsip_tx_data *tdata)
|
||||
{
|
||||
pjsip_msg_body *body;
|
||||
pj_str_t dup_text;
|
||||
|
||||
PJ_ASSERT_RETURN(mwi->mime_type.type.slen && mwi->body.slen, PJ_EINVALIDOP);
|
||||
|
||||
/* Clone the message body and mime type */
|
||||
pj_strdup(tdata->pool, &dup_text, &mwi->body);
|
||||
|
||||
/* Create the message body */
|
||||
body = PJ_POOL_ZALLOC_T(tdata->pool, pjsip_msg_body);
|
||||
pjsip_media_type_cp(tdata->pool, &body->content_type, &mwi->mime_type);
|
||||
body->data = dup_text.ptr;
|
||||
body->len = (unsigned)dup_text.slen;
|
||||
body->print_body = &pjsip_print_text_body;
|
||||
body->clone_data = &pjsip_clone_text_data;
|
||||
|
||||
/* Attach to tdata */
|
||||
tdata->msg->body = body;
|
||||
|
||||
return PJ_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create NOTIFY
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_notify( pjsip_evsub *sub,
|
||||
pjsip_evsub_state state,
|
||||
const pj_str_t *state_str,
|
||||
const pj_str_t *reason,
|
||||
const pjsip_media_type *mime_type,
|
||||
const pj_str_t *body,
|
||||
pjsip_tx_data **p_tdata)
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
pjsip_tx_data *tdata;
|
||||
pj_status_t status;
|
||||
|
||||
/* Check arguments. */
|
||||
PJ_ASSERT_RETURN(sub && mime_type && body && p_tdata, PJ_EINVAL);
|
||||
|
||||
/* Get the mwi object. */
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP);
|
||||
|
||||
/* Lock object. */
|
||||
pjsip_dlg_inc_lock(mwi->dlg);
|
||||
|
||||
/* Create the NOTIFY request. */
|
||||
status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_return;
|
||||
|
||||
/* Update the cached message body */
|
||||
if (mime_type || body)
|
||||
pj_pool_reset(mwi->body_pool);
|
||||
if (mime_type)
|
||||
pjsip_media_type_cp(mwi->body_pool, &mwi->mime_type, mime_type);
|
||||
if (body)
|
||||
pj_strdup(mwi->body_pool, &mwi->body, body);
|
||||
|
||||
/* Create message body */
|
||||
status = mwi_create_msg_body( mwi, tdata );
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_return;
|
||||
|
||||
/* Done. */
|
||||
*p_tdata = tdata;
|
||||
|
||||
on_return:
|
||||
pjsip_dlg_dec_lock(mwi->dlg);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create NOTIFY that reflect current state.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_current_notify( pjsip_evsub *sub,
|
||||
pjsip_tx_data **p_tdata )
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
pjsip_tx_data *tdata;
|
||||
pj_status_t status;
|
||||
|
||||
/* Check arguments. */
|
||||
PJ_ASSERT_RETURN(sub && p_tdata, PJ_EINVAL);
|
||||
|
||||
/* Get the mwi object. */
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_RETURN(mwi != NULL, PJ_EINVALIDOP);
|
||||
|
||||
/* Lock object. */
|
||||
pjsip_dlg_inc_lock(mwi->dlg);
|
||||
|
||||
/* Create the NOTIFY request. */
|
||||
status = pjsip_evsub_current_notify( sub, &tdata);
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_return;
|
||||
|
||||
|
||||
/* Create message body to reflect the mwi status. */
|
||||
status = mwi_create_msg_body( mwi, tdata );
|
||||
if (status != PJ_SUCCESS)
|
||||
goto on_return;
|
||||
|
||||
/* Done. */
|
||||
*p_tdata = tdata;
|
||||
|
||||
on_return:
|
||||
pjsip_dlg_dec_lock(mwi->dlg);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send request.
|
||||
*/
|
||||
PJ_DEF(pj_status_t) pjsip_mwi_send_request( pjsip_evsub *sub,
|
||||
pjsip_tx_data *tdata )
|
||||
{
|
||||
return pjsip_evsub_send_request(sub, tdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* This callback is called by event subscription when subscription
|
||||
* state has changed.
|
||||
*/
|
||||
static void mwi_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
|
||||
|
||||
if (mwi->user_cb.on_evsub_state)
|
||||
(*mwi->user_cb.on_evsub_state)(sub, event);
|
||||
|
||||
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
|
||||
if (mwi->body_pool) {
|
||||
pj_pool_release(mwi->body_pool);
|
||||
mwi->body_pool = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when transaction state has changed.
|
||||
*/
|
||||
static void mwi_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
|
||||
pjsip_event *event)
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
|
||||
|
||||
if (mwi->user_cb.on_tsx_state)
|
||||
(*mwi->user_cb.on_tsx_state)(sub, tsx, event);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called when SUBSCRIBE is received.
|
||||
*/
|
||||
static void mwi_on_evsub_rx_refresh( pjsip_evsub *sub,
|
||||
pjsip_rx_data *rdata,
|
||||
int *p_st_code,
|
||||
pj_str_t **p_st_text,
|
||||
pjsip_hdr *res_hdr,
|
||||
pjsip_msg_body **p_body)
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
|
||||
|
||||
if (mwi->user_cb.on_rx_refresh) {
|
||||
(*mwi->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
|
||||
res_hdr, p_body);
|
||||
|
||||
} else {
|
||||
/* Implementors MUST send NOTIFY if it implements on_rx_refresh */
|
||||
pjsip_tx_data *tdata;
|
||||
pj_str_t timeout = { "timeout", 7};
|
||||
pj_status_t status;
|
||||
|
||||
if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
|
||||
status = pjsip_mwi_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
|
||||
NULL, &timeout, NULL, NULL, &tdata);
|
||||
} else {
|
||||
status = pjsip_mwi_current_notify(sub, &tdata);
|
||||
}
|
||||
|
||||
if (status == PJ_SUCCESS)
|
||||
pjsip_mwi_send_request(sub, tdata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called when NOTIFY is received.
|
||||
*/
|
||||
static void mwi_on_evsub_rx_notify( pjsip_evsub *sub,
|
||||
pjsip_rx_data *rdata,
|
||||
int *p_st_code,
|
||||
pj_str_t **p_st_text,
|
||||
pjsip_hdr *res_hdr,
|
||||
pjsip_msg_body **p_body)
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
|
||||
|
||||
/* Just notify application. */
|
||||
if (mwi->user_cb.on_rx_notify) {
|
||||
(*mwi->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
|
||||
res_hdr, p_body);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when it's time to send SUBSCRIBE.
|
||||
*/
|
||||
static void mwi_on_evsub_client_refresh(pjsip_evsub *sub)
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
|
||||
|
||||
if (mwi->user_cb.on_client_refresh) {
|
||||
(*mwi->user_cb.on_client_refresh)(sub);
|
||||
} else {
|
||||
pj_status_t status;
|
||||
pjsip_tx_data *tdata;
|
||||
|
||||
status = pjsip_mwi_initiate(sub, -1, &tdata);
|
||||
if (status == PJ_SUCCESS)
|
||||
pjsip_mwi_send_request(sub, tdata);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when no refresh is received after the interval.
|
||||
*/
|
||||
static void mwi_on_evsub_server_timeout(pjsip_evsub *sub)
|
||||
{
|
||||
pjsip_mwi *mwi;
|
||||
|
||||
mwi = (pjsip_mwi*) pjsip_evsub_get_mod_data(sub, mod_mwi.id);
|
||||
PJ_ASSERT_ON_FAIL(mwi!=NULL, {return;});
|
||||
|
||||
if (mwi->user_cb.on_server_timeout) {
|
||||
(*mwi->user_cb.on_server_timeout)(sub);
|
||||
} else {
|
||||
pj_status_t status;
|
||||
pjsip_tx_data *tdata;
|
||||
pj_str_t reason = { "timeout", 7 };
|
||||
|
||||
status = pjsip_mwi_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
|
||||
NULL, &reason, NULL, NULL, &tdata);
|
||||
if (status == PJ_SUCCESS)
|
||||
pjsip_mwi_send_request(sub, tdata);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<!-- This program is free software; you can redistribute it and/or -->
|
||||
<!-- modify it under the terms of the GNU General Public License as -->
|
||||
<!-- published by the Free Software Foundation; either version 2 of the -->
|
||||
<!-- License, or (at your option) any later version. -->
|
||||
<!-- -->
|
||||
<!-- This program is distributed in the hope that it will be useful, -->
|
||||
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
|
||||
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
|
||||
<!-- GNU General Public License for more details. -->
|
||||
<!-- -->
|
||||
<!-- You should have received a copy of the GNU General Public License -->
|
||||
<!-- along with this program; if not, write to the -->
|
||||
<!-- Free Software Foundation, Inc., -->
|
||||
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
|
||||
<!-- -->
|
||||
<!-- Late NOTIFY scenario: -->
|
||||
<!-- - UAC sends SUBSCRIBE, we reply with 200 -->
|
||||
<!-- - we send NOTIFY, expect 200 -->
|
||||
<!-- - UAC sends SUBSCRIBE, we ignore -->
|
||||
<!-- - we send NOTIFY -->
|
||||
<!-- See http://trac.pjsip.org/repos/ticket/911 -->
|
||||
<!-- -->
|
||||
|
||||
<scenario name="MWI server with immediate final notify">
|
||||
<recv request="SUBSCRIBE" crlf="true">
|
||||
<action>
|
||||
<ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
|
||||
<ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
|
||||
<assign assign_to="4" variable="5" />
|
||||
</action>
|
||||
</recv>
|
||||
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:];tag=[call_number]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:sipp@[local_ip]:[local_port]>
|
||||
Content-Length: 0
|
||||
Expires: 600
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<!-- initial notify is final notify -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
NOTIFY sip:[$5] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];rport;branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]>;tag=[call_number]
|
||||
To[$3]
|
||||
Call-ID: [call_id]
|
||||
Cseq: 1 NOTIFY
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Event: message-summary
|
||||
Subscription-State: terminated;reason=goinghome
|
||||
Content-Type: application/simple-message-summary
|
||||
Content-Length: [len]
|
||||
|
||||
Messages-Waiting: yes
|
||||
Voice-Message: 4/8 (1/2)
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200">
|
||||
</recv>
|
||||
|
||||
<pause milliseconds="5000"/>
|
||||
|
||||
<!-- definition of the response time repartition table (unit is ms) -->
|
||||
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
|
||||
|
||||
<!-- definition of the call length repartition table (unit is ms) -->
|
||||
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
|
||||
|
||||
</scenario>
|
||||
|
@ -0,0 +1,112 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE scenario SYSTEM "sipp.dtd">
|
||||
|
||||
<!-- This program is free software; you can redistribute it and/or -->
|
||||
<!-- modify it under the terms of the GNU General Public License as -->
|
||||
<!-- published by the Free Software Foundation; either version 2 of the -->
|
||||
<!-- License, or (at your option) any later version. -->
|
||||
<!-- -->
|
||||
<!-- This program is distributed in the hope that it will be useful, -->
|
||||
<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of -->
|
||||
<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -->
|
||||
<!-- GNU General Public License for more details. -->
|
||||
<!-- -->
|
||||
<!-- You should have received a copy of the GNU General Public License -->
|
||||
<!-- along with this program; if not, write to the -->
|
||||
<!-- Free Software Foundation, Inc., -->
|
||||
<!-- 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -->
|
||||
<!-- -->
|
||||
<!-- Late NOTIFY scenario: -->
|
||||
<!-- - UAC sends SUBSCRIBE, we reply with 200 -->
|
||||
<!-- - we send NOTIFY, expect 200 -->
|
||||
<!-- - UAC sends SUBSCRIBE, we ignore -->
|
||||
<!-- - we send NOTIFY -->
|
||||
<!-- See http://trac.pjsip.org/repos/ticket/911 -->
|
||||
<!-- -->
|
||||
|
||||
<scenario name="MWI server">
|
||||
<recv request="SUBSCRIBE" crlf="true">
|
||||
<action>
|
||||
<ereg regexp=".*" search_in="hdr" header="From" assign_to="3"/>
|
||||
<ereg regexp="sip:(.*)>" search_in="hdr" header="Contact" assign_to="4,5"/>
|
||||
<assign assign_to="4" variable="5" />
|
||||
</action>
|
||||
</recv>
|
||||
|
||||
|
||||
<send>
|
||||
<![CDATA[
|
||||
|
||||
SIP/2.0 200 OK
|
||||
[last_Via:]
|
||||
[last_From:]
|
||||
[last_To:];tag=[call_number]
|
||||
[last_Call-ID:]
|
||||
[last_CSeq:]
|
||||
Contact: <sip:sipp@[local_ip]:[local_port]>
|
||||
Content-Length: 0
|
||||
Expires: 600
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<!-- initial notify -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
NOTIFY sip:[$5] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];rport;branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]>;tag=[call_number]
|
||||
To[$3]
|
||||
Call-ID: [call_id]
|
||||
Cseq: 1 NOTIFY
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Event: message-summary
|
||||
Subscription-State: active;expires=50
|
||||
Content-Type: application/simple-message-summary
|
||||
Content-Length: [len]
|
||||
|
||||
Messages-Waiting: yes
|
||||
Voice-Message: 4/8 (1/2)
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200">
|
||||
</recv>
|
||||
|
||||
<pause milliseconds="10000"/>
|
||||
|
||||
|
||||
<!-- terminate subscription -->
|
||||
<send retrans="500">
|
||||
<![CDATA[
|
||||
NOTIFY sip:[$5] SIP/2.0
|
||||
Via: SIP/2.0/[transport] [local_ip]:[local_port];rport;branch=[branch]
|
||||
From: sipp <sip:sipp@[local_ip]>;tag=[call_number]
|
||||
To[$3]
|
||||
Call-ID: [call_id]
|
||||
Cseq: 2 NOTIFY
|
||||
Contact: sip:sipp@[local_ip]:[local_port]
|
||||
Max-Forwards: 70
|
||||
Event: message-summary
|
||||
Subscription-State: terminated;reason=noresource
|
||||
Content-Type: application/simple-message-summary
|
||||
Content-Length: [len]
|
||||
|
||||
Messages-Waiting: yes
|
||||
Voice-Message: 4/8 (1/2)
|
||||
|
||||
]]>
|
||||
</send>
|
||||
|
||||
<recv response="200">
|
||||
</recv>
|
||||
|
||||
<!-- definition of the response time repartition table (unit is ms) -->
|
||||
<ResponseTimeRepartition value="10, 20, 30, 40, 50, 100, 150, 200"/>
|
||||
|
||||
<!-- definition of the call length repartition table (unit is ms) -->
|
||||
<CallLengthRepartition value="10, 50, 100, 500, 1000, 5000, 10000"/>
|
||||
|
||||
</scenario>
|
||||
|
Loading…
Reference in new issue