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-12977e46ad98
remotes/origin/iphone
Benny Prijono 15 years ago
parent 610973a068
commit 4dd961b2fb

@ -37,6 +37,7 @@ SOURCE errno.c
SOURCE evsub.c
SOURCE evsub_msg.c
SOURCE iscomposing.c
SOURCE mwi.c
SOURCE pidf.c
SOURCE presence.c
SOURCE presence_body.c

@ -193,6 +193,7 @@ static void usage(void)
puts (" --username=string Set authentication username");
puts (" --password=string Set authentication password");
puts (" --publish Send presence PUBLISH for this account");
puts (" --mwi Subscribe to message summary/waiting indication");
puts (" --use-100rel Require reliable provisional response (100rel)");
puts (" --use-timer Require SIP session timers");
puts (" --timer-se=N Session timers expiration period, in secs (def:1800)");
@ -483,7 +484,7 @@ static pj_status_t parse_args(int argc, char *argv[],
OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
OPT_BOUND_ADDR, OPT_CONTACT_PARAMS, OPT_CONTACT_URI_PARAMS,
OPT_100REL, OPT_USE_IMS, OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
OPT_NAMESERVER, OPT_STUN_SRV,
OPT_MWI, OPT_NAMESERVER, OPT_STUN_SRV,
OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
OPT_AUTO_ANSWER, OPT_AUTO_PLAY, OPT_AUTO_PLAY_HANGUP, OPT_AUTO_LOOP,
OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
@ -535,6 +536,7 @@ static pj_status_t parse_args(int argc, char *argv[],
{ "registrar", 1, 0, OPT_REGISTRAR},
{ "reg-timeout",1, 0, OPT_REG_TIMEOUT},
{ "publish", 0, 0, OPT_PUBLISH},
{ "mwi", 0, 0, OPT_MWI},
{ "use-100rel", 0, 0, OPT_100REL},
{ "use-ims", 0, 0, OPT_USE_IMS},
{ "id", 1, 0, OPT_ID},
@ -833,6 +835,10 @@ static pj_status_t parse_args(int argc, char *argv[],
cur_acc->publish_enabled = PJ_TRUE;
break;
case OPT_MWI: /* mwi */
cur_acc->mwi_enabled = PJ_TRUE;
break;
case OPT_100REL: /** 100rel */
cur_acc->require_100rel = PJ_TRUE;
cfg->cfg.require_100rel = PJ_TRUE;
@ -1550,6 +1556,13 @@ static void write_account_settings(int acc_index, pj_str_t *result)
pj_strcat2(result, line);
}
/* Publish */
if (acc_cfg->publish_enabled)
pj_strcat2(result, "--publish\n");
/* MWI */
if (acc_cfg->mwi_enabled)
pj_strcat2(result, "--mwi\n");
}
@ -2742,6 +2755,37 @@ static void on_nat_detect(const pj_stun_nat_detect_result *res)
}
/*
* MWI indication
*/
static void on_mwi_info(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info)
{
pj_str_t body;
PJ_LOG(3,(THIS_FILE, "Received MWI for acc %d:", acc_id));
if (mwi_info->rdata->msg_info.ctype) {
const pjsip_ctype_hdr *ctype = mwi_info->rdata->msg_info.ctype;
PJ_LOG(3,(THIS_FILE, " Content-Type: %.*s/%.*s",
(int)ctype->media.type.slen,
ctype->media.type.ptr,
(int)ctype->media.subtype.slen,
ctype->media.subtype.ptr));
}
if (!mwi_info->rdata->msg_info.msg->body) {
PJ_LOG(3,(THIS_FILE, " no message body"));
return;
}
body.ptr = mwi_info->rdata->msg_info.msg->body->data;
body.slen = mwi_info->rdata->msg_info.msg->body->len;
PJ_LOG(3,(THIS_FILE, " Body:\n%.*s", (int)body.slen, body.ptr));
}
/*
* Print buddy list.
*/
@ -4337,6 +4381,7 @@ pj_status_t app_init(int argc, char *argv[])
app_config.cfg.cb.on_call_transfer_status = &on_call_transfer_status;
app_config.cfg.cb.on_call_replaced = &on_call_replaced;
app_config.cfg.cb.on_nat_detect = &on_nat_detect;
app_config.cfg.cb.on_mwi_info = &on_mwi_info;
/* Set sound device latency */
if (app_config.capture_lat > 0)

@ -64,7 +64,7 @@ export PJSIP_UA_CFLAGS += $(_CFLAGS)
export PJSIP_SIMPLE_SRCDIR = ../src/pjsip-simple
export PJSIP_SIMPLE_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \
errno.o evsub.o evsub_msg.o iscomposing.o \
pidf.o presence.o presence_body.o publishc.o \
mwi.o pidf.o presence.o presence_body.o publishc.o \
rpid.o xpidf.o
export PJSIP_SIMPLE_CFLAGS += $(_CFLAGS)

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__ */

@ -511,6 +511,18 @@ typedef struct pjsip_media_type
pj_str_t param; /**< Media type parameters (concatenated). */
} pjsip_media_type;
/**
* Copy SIP media type to another.
*
* @param pool Pool to duplicate strings.
* @param dst Destination structure.
* @param src Source structure.
*/
PJ_DECL(void) pjsip_media_type_cp(pj_pool_t *pool,
pjsip_media_type *dst,
const pjsip_media_type *src);
/**
* @}
*/

@ -37,6 +37,7 @@
#include <pjsip-simple/evsub.h>
#include <pjsip-simple/evsub_msg.h>
#include <pjsip-simple/iscomposing.h>
#include <pjsip-simple/mwi.h>
#include <pjsip-simple/presence.h>
#include <pjsip-simple/pidf.h>
#include <pjsip-simple/publish.h>

@ -378,6 +378,17 @@ PJ_DECL(void) pjsua_logging_config_dup(pj_pool_t *pool,
const pjsua_logging_config *src);
/**
* Structure to be passed on MWI callback.
*/
typedef struct pjsua_mwi_info
{
pjsip_evsub *evsub; /**< Event subscription session, for
reference. */
pjsip_rx_data *rdata; /**< The received NOTIFY request. */
} pjsua_mwi_info;
/**
* This structure describes application callback to receive various event
* notification from PJSUA-API. All of these callbacks are OPTIONAL,
@ -821,6 +832,17 @@ typedef struct pjsua_callback
const pjsip_uri *target,
const pjsip_event *e);
/**
* This callback is called when a NOTIFY request for message summary /
* message waiting indication is received.
*
* @param acc_id The account ID.
* @param mwi_info Structure containing details of the event,
* including the received NOTIFY request in the
* \a rdata field.
*/
void (*on_mwi_info)(pjsua_acc_id acc_id, pjsua_mwi_info *mwi_info);
} pjsua_callback;
@ -1859,6 +1881,14 @@ typedef struct pjsua_acc_config
*/
pj_str_t reg_uri;
/**
* Enable message summary and message waiting indication subscription
* (RFC 3842) for this account.
*
* Default: no
*/
pj_bool_t mwi_enabled;
/**
* If this flag is set, the presence information of this account will
* be PUBLISH-ed to the server where the account belongs.

@ -144,6 +144,8 @@ typedef struct pjsua_acc
pjsip_publishc *publish_sess; /**< Client publication session. */
pj_bool_t publish_state; /**< Last published online status */
pjsip_evsub *mwi_sub; /**< MWI client subscription */
pjsip_dialog *mwi_dlg; /**< Dialog for MWI sub. */
} pjsua_acc;
@ -451,6 +453,11 @@ void pjsua_pres_delete_acc(int acc_id);
*/
pj_status_t pjsua_im_init(void);
/**
* Start MWI subscription
*/
void pjsua_start_mwi(pjsua_acc *acc);
/**
* Init call subsystem.
*/

@ -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);
}
}

@ -598,6 +598,20 @@ PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code)
&status_phrase[code] : &status_phrase[0];
}
///////////////////////////////////////////////////////////////////////////////
/*
* Media type
*/
PJ_DEF(void) pjsip_media_type_cp( pj_pool_t *pool,
pjsip_media_type *dst,
const pjsip_media_type *src)
{
PJ_ASSERT_ON_FAIL(pool && dst && src, return);
pj_strdup(pool, &dst->type, &src->type);
pj_strdup(pool, &dst->subtype, &src->subtype);
pj_strdup(pool, &dst->param, &src->param);
}
///////////////////////////////////////////////////////////////////////////////
/*
* Generic pjsip_hdr_names/hvalue header.

@ -328,7 +328,11 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
/* If accounts has registration enabled, start registration */
if (pjsua_var.acc[id].cfg.reg_uri.slen)
pjsua_acc_set_registration(id, PJ_TRUE);
else {
/* Otherwise subscribe to MWI, if it's enabled */
if (pjsua_var.acc[id].cfg.mwi_enabled)
pjsua_start_mwi(&pjsua_var.acc[id]);
}
return PJ_SUCCESS;
}
@ -1056,6 +1060,10 @@ static void regc_cb(struct pjsip_regc_cbparam *param)
/* Send initial PUBLISH if it is enabled */
if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
pjsua_pres_init_publish_acc(acc->index);
/* Subscribe to MWI, if it's enabled */
if (acc->cfg.mwi_enabled)
pjsua_start_mwi(acc);
}
} else {

@ -820,6 +820,9 @@ PJ_DEF(pj_status_t) pjsua_init( const pjsua_config *ua_cfg,
status = pjsip_pres_init_module( pjsua_var.endpt, pjsip_evsub_instance());
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
/* Initialize MWI support */
status = pjsip_mwi_init_module(pjsua_var.endpt, pjsip_evsub_instance());
/* Init PUBLISH module */
pjsip_publishc_init_module(pjsua_var.endpt);

@ -1853,6 +1853,230 @@ static pj_status_t refresh_client_subscriptions(void)
return PJ_SUCCESS;
}
/***************************************************************************
* MWI
*/
/* Callback called when *client* subscription state has changed. */
static void mwi_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
{
pjsua_acc *acc;
PJ_UNUSED_ARG(event);
/* Note: #937: no need to acuire PJSUA_LOCK here. Since the buddy has
* a dialog attached to it, lock_buddy() will use the dialog
* lock, which we are currently holding!
*/
acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
if (!acc)
return;
PJ_LOG(4,(THIS_FILE,
"MWI subscription for %.*s is %s",
(int)acc->cfg.id.slen, acc->cfg.id.ptr,
pjsip_evsub_get_state_name(sub)));
if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
/* Clear subscription */
acc->mwi_dlg = NULL;
acc->mwi_sub = NULL;
pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
}
}
/* Callback called when we receive NOTIFY */
static void mwi_evsub_on_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)
{
pjsua_mwi_info mwi_info;
pjsua_acc *acc;
PJ_UNUSED_ARG(p_st_code);
PJ_UNUSED_ARG(p_st_text);
PJ_UNUSED_ARG(res_hdr);
PJ_UNUSED_ARG(p_body);
acc = (pjsua_acc*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
if (!acc)
return;
/* Construct mwi_info */
pj_bzero(&mwi_info, sizeof(mwi_info));
mwi_info.evsub = sub;
mwi_info.rdata = rdata;
/* Call callback */
if (pjsua_var.ua_cfg.cb.on_mwi_info) {
(*pjsua_var.ua_cfg.cb.on_mwi_info)(acc->index, &mwi_info);
}
}
/* Event subscription callback. */
static pjsip_evsub_user mwi_cb =
{
&mwi_evsub_on_state,
NULL, /* on_tsx_state: not interested */
NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
* we want to authenticate
*/
&mwi_evsub_on_rx_notify,
NULL, /* on_client_refresh: Use default behaviour, which is to
* refresh client subscription. */
NULL, /* on_server_timeout: Use default behaviour, which is to send
* NOTIFY to terminate.
*/
};
void pjsua_start_mwi(pjsua_acc *acc)
{
pj_pool_t *tmp_pool = NULL;
pj_str_t contact;
pjsip_tx_data *tdata;
pj_status_t status;
if (!acc->cfg.mwi_enabled) {
if (acc->mwi_sub) {
/* Terminate MWI subscription */
pjsip_tx_data *tdata;
pjsip_evsub *sub = acc->mwi_sub;
/* Detach sub from this account */
acc->mwi_sub = NULL;
acc->mwi_dlg = NULL;
pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
/* Unsubscribe */
status = pjsip_mwi_initiate(acc->mwi_sub, 0, &tdata);
if (status == PJ_SUCCESS) {
status = pjsip_mwi_send_request(acc->mwi_sub, tdata);
}
}
return;
}
if (acc->mwi_sub) {
/* Subscription is already active */
return;
}
/* Generate suitable Contact header unless one is already set in
* the account
*/
if (acc->contact.slen) {
contact = acc->contact;
} else {
tmp_pool = pjsua_pool_create("tmpmwi", 512, 256);
status = pjsua_acc_create_uac_contact(tmp_pool, &contact,
acc->index, &acc->cfg.id);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to generate Contact header",
status);
pj_pool_release(tmp_pool);
return;
}
}
/* Create UAC dialog */
status = pjsip_dlg_create_uac( pjsip_ua_instance(),
&acc->cfg.id,
&contact,
&acc->cfg.id,
NULL, &acc->mwi_dlg);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create dialog", status);
if (tmp_pool) pj_pool_release(tmp_pool);
return;
}
/* Increment the dialog's lock otherwise when presence session creation
* fails the dialog will be destroyed prematurely.
*/
pjsip_dlg_inc_lock(acc->mwi_dlg);
/* Create UAC subscription */
status = pjsip_mwi_create_uac(acc->mwi_dlg, &mwi_cb,
PJSIP_EVSUB_NO_EVENT_ID, &acc->mwi_sub);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error creating MWI subscription", status);
if (tmp_pool) pj_pool_release(tmp_pool);
pjsip_dlg_dec_lock(acc->mwi_dlg);
return;
}
/* If account is locked to specific transport, then lock dialog
* to this transport too.
*/
if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
pjsip_tpselector tp_sel;
pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
pjsip_dlg_set_transport(acc->mwi_dlg, &tp_sel);
}
/* Set route-set */
if (!pj_list_empty(&acc->route_set)) {
pjsip_dlg_set_route_set(acc->mwi_dlg, &acc->route_set);
}
/* Set credentials */
if (acc->cred_cnt) {
pjsip_auth_clt_set_credentials( &acc->mwi_dlg->auth_sess,
acc->cred_cnt, acc->cred);
}
/* Set authentication preference */
pjsip_auth_clt_set_prefs(&acc->mwi_dlg->auth_sess, &acc->cfg.auth_pref);
pjsip_evsub_set_mod_data(acc->mwi_sub, pjsua_var.mod.id, acc);
status = pjsip_mwi_initiate(acc->mwi_sub, -1, &tdata);
if (status != PJ_SUCCESS) {
pjsip_dlg_dec_lock(acc->mwi_dlg);
if (acc->mwi_sub) {
pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
}
acc->mwi_sub = NULL;
acc->mwi_dlg = NULL;
pjsua_perror(THIS_FILE, "Unable to create initial MWI SUBSCRIBE",
status);
if (tmp_pool) pj_pool_release(tmp_pool);
return;
}
pjsua_process_msg_data(tdata, NULL);
status = pjsip_pres_send_request(acc->mwi_sub, tdata);
if (status != PJ_SUCCESS) {
pjsip_dlg_dec_lock(acc->mwi_dlg);
if (acc->mwi_sub) {
pjsip_pres_terminate(acc->mwi_sub, PJ_FALSE);
}
acc->mwi_sub = NULL;
acc->mwi_dlg = NULL;
pjsua_perror(THIS_FILE, "Unable to send initial MWI SUBSCRIBE",
status);
if (tmp_pool) pj_pool_release(tmp_pool);
return;
}
pjsip_dlg_dec_lock(acc->mwi_dlg);
if (tmp_pool) pj_pool_release(tmp_pool);
}
/***************************************************************************/
/* Timer callback to re-create client subscription */
static void pres_timer_cb(pj_timer_heap_t *th,
pj_timer_entry *entry)
@ -1860,14 +2084,20 @@ static void pres_timer_cb(pj_timer_heap_t *th,
unsigned i;
pj_time_val delay = { PJSUA_PRES_TIMER, 0 };
/* Retry failed PUBLISH requests */
entry->id = PJ_FALSE;
/* Retry failed PUBLISH and MWI SUBSCRIBE requests */
for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) {
pjsua_acc *acc = &pjsua_var.acc[i];
/* Retry PUBLISH */
if (acc->cfg.publish_enabled && acc->publish_sess==NULL)
pjsua_pres_init_publish_acc(acc->index);
}
entry->id = PJ_FALSE;
/* Re-subscribe MWI subscription if it's terminated prematurely */
if (acc->cfg.mwi_enabled && !acc->mwi_sub)
pjsua_start_mwi(acc);
}
/* #937: No need to do bulk client refresh, as buddies have their
* own individual timer now.

@ -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…
Cancel
Save