summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-03-05 11:53:36 +0000
committerBenny Prijono <bennylp@teluu.com>2006-03-05 11:53:36 +0000
commitcb88a08734906fe0e47fef54752d5928ae9d45ac (patch)
treefd67bf0c001d6193bec0feb4d6e41cba6432ccda
parent84ba2655e4a972faf9b763001579b4d5d616cc4b (diff)
Added API to terminate dialog prematurely. Affect: dialog, invite sessions, evsub, and presence
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@283 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/include/pjsip-simple/evsub.h15
-rw-r--r--pjsip/include/pjsip-simple/presence.h17
-rw-r--r--pjsip/include/pjsip-ua/sip_inv.h25
-rw-r--r--pjsip/include/pjsip/sip_dialog.h186
-rw-r--r--pjsip/include/pjsip/sip_errno.h9
-rw-r--r--pjsip/src/pjsip-simple/evsub.c47
-rw-r--r--pjsip/src/pjsip-simple/iscomposing.c7
-rw-r--r--pjsip/src/pjsip-simple/presence.c9
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c104
-rw-r--r--pjsip/src/pjsip/sip_dialog.c15
-rw-r--r--pjsip/src/pjsip/sip_errno.c7
11 files changed, 406 insertions, 35 deletions
diff --git a/pjsip/include/pjsip-simple/evsub.h b/pjsip/include/pjsip-simple/evsub.h
index ba1dd0a6..0bcd63a9 100644
--- a/pjsip/include/pjsip-simple/evsub.h
+++ b/pjsip/include/pjsip-simple/evsub.h
@@ -287,6 +287,21 @@ PJ_DECL(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
unsigned option,
pjsip_evsub **p_evsub);
+/**
+ * Forcefully destroy the subscription session. 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 event 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_evsub_terminate( pjsip_evsub *sub,
+ pj_bool_t notify );
+
/**
* Get subscription state.
diff --git a/pjsip/include/pjsip-simple/presence.h b/pjsip/include/pjsip-simple/presence.h
index 8559d91d..f8d63527 100644
--- a/pjsip/include/pjsip-simple/presence.h
+++ b/pjsip/include/pjsip-simple/presence.h
@@ -129,6 +129,23 @@ PJ_DECL(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
/**
+ * Forcefully destroy the presence 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 presence 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_pres_terminate( pjsip_evsub *sub,
+ pj_bool_t notify );
+
+
+
+/**
* Call this function to create request to initiate presence subscription, to
* refresh subcription, or to request subscription termination.
*
diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h
index f41a1d94..4939c9d3 100644
--- a/pjsip/include/pjsip-ua/sip_inv.h
+++ b/pjsip/include/pjsip-ua/sip_inv.h
@@ -169,6 +169,8 @@ struct pjsip_inv_session
char obj_name[PJ_MAX_OBJ_NAME]; /**< Log identification. */
pj_pool_t *pool; /**< Dialog's pool. */
pjsip_inv_state state; /**< Invite sess state. */
+ pjsip_status_code cause; /**< Disconnect cause. */
+ pj_bool_t notify; /**< Internal. */
pjsip_dialog *dlg; /**< Underlying dialog. */
pjsip_role_e role; /**< Invite role. */
unsigned options; /**< Options in use. */
@@ -333,6 +335,29 @@ PJ_DECL(pj_status_t) pjsip_inv_create_uas(pjsip_dialog *dlg,
/**
+ * Forcefully terminate and destroy INVITE session, regardless of
+ * the state of the session. Note that this function should only be used
+ * when there is failure in the INVITE session creation. After the
+ * invite session has been created and initialized, normally application
+ * SHOULD use #pjsip_inv_end_session() to end the INVITE session instead.
+ *
+ * Note also that this function may terminate the underlying dialog, if
+ * there are no other sessions in the dialog.
+ *
+ * @param inv The invite session.
+ * @param st_code Status code for the reason of the termination.
+ * @param notify If set to non-zero, then on_state_changed()
+ * callback will be called.
+ *
+ * @return PJ_SUCCESS if the INVITE session has been
+ * terminated.
+ */
+PJ_DECL(pj_status_t) pjsip_inv_terminate( pjsip_inv_session *inv,
+ int st_code,
+ pj_bool_t notify );
+
+
+/**
* Create the initial INVITE request for this session. This function can only
* be called for UAC session. If local media capability is specified when
* the invite session was created, then this function will put an SDP offer
diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h
index 27ea7980..42ce7ba1 100644
--- a/pjsip/include/pjsip/sip_dialog.h
+++ b/pjsip/include/pjsip/sip_dialog.h
@@ -59,7 +59,19 @@ enum pjsip_dialog_state
};
/**
- * This structure describes the dialog structure.
+ * This structure describes the dialog structure. Application MUST NOT
+ * try to SET the values here directly, but instead it MUST use the
+ * appropriate dialog API. The dialog declaration only needs to be made
+ * visible because other PJSIP modules need to see it (e.g. INVITE session,
+ * the event framework, etc.).
+ *
+ * Application MAY READ the dialog contents directly after it acquires
+ * dialog lock.
+ *
+ * To acquire dialog lock, use #pjsip_dlg_inc_lock(), and to release it,
+ * use #pjsip_dlg_dec_lock(). DO NOT USE pj_mutex_lock()/pj_mutex_unlock()
+ * on the dialog's mutex directly, because this will not protect against
+ * dialog being destroyed.
*/
struct pjsip_dialog
{
@@ -69,7 +81,8 @@ struct pjsip_dialog
/* Dialog's system properties. */
char obj_name[PJ_MAX_OBJ_NAME]; /**< Standard id. */
pj_pool_t *pool; /**< Dialog's pool. */
- pj_mutex_t *mutex; /**< Dialog's mutex. */
+ pj_mutex_t *mutex; /**< Dialog's mutex. Do not call!!
+ Use pjsip_dlg_inc_lock() instead! */
pjsip_user_agent *ua; /**< User agent instance. */
pjsip_endpoint *endpt; /**< Endpoint instance. */
@@ -89,17 +102,18 @@ struct pjsip_dialog
pjsip_auth_clt_sess auth_sess; /**< Client authentication session. */
/** Session counter. */
- int sess_count;
+ int sess_count; /**< Number of sessions. */
/** Transaction counter. */
- int tsx_count;
+ int tsx_count; /**< Number of pending transactions. */
/* Dialog usages. */
unsigned usage_cnt; /**< Number of registered usages. */
- pjsip_module *usage[PJSIP_MAX_MODULE]; /**< Array of usages, priority sorted */
+ pjsip_module *usage[PJSIP_MAX_MODULE]; /**< Array of usages,
+ priority sorted */
/** Module specific data. */
- void *mod_data[PJSIP_MAX_MODULE];
+ void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
};
@@ -107,6 +121,10 @@ struct pjsip_dialog
* This utility function returns PJ_TRUE if the specified method is a
* dialog creating request. This method property is used to determine
* whether Contact header should be included in outgoing request.
+ *
+ * @param m The SIP method.
+ *
+ * @return PJ_TRUE if the method creates a dialog.
*/
PJ_DECL(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m);
@@ -124,6 +142,19 @@ PJ_DECL(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m);
*
* Note that initially, the session count in the dialog will be initialized
* to zero.
+ *
+ * @param ua The user agent module instance.
+ * @param local_uri Dialog local URI (i.e. From header).
+ * @param local_contact_uri Optional dialog local Contact URI.
+ * If this argument is NULL, the Contact will be
+ * taken from the local URI.
+ * @param remote_uri Dialog remote URI (i.e. To header).
+ * @param target Optional initial remote target. If this argument
+ * is NULL, the initial target will be set to
+ * remote URI.
+ * @param p_dlg Pointer to receive the dialog.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua,
const pj_str_t *local_uri,
@@ -146,6 +177,18 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua,
*
* Note that initially, the session count in the dialog will be initialized
* to zero.
+ *
+ *
+ * @param ua The user agent module instance.
+ * @param rdata The incoming request that creates the dialog,
+ * such as INVITE, SUBSCRIBE, or REFER.
+ * @param contact Optional URI to be used as local Contact. If
+ * this argument is NULL, the local contact will be
+ * initialized from the value of To header in the
+ * request.
+ * @param p_dlg Pointer to receive the dialog.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua,
pjsip_rx_data *rdata,
@@ -162,12 +205,33 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua,
*
* Note that initially, the session count in the dialog will be initialized
* to zero.
+ *
+ * @param original_dlg The original UAC dialog.
+ * @param rdata The incoming forked response message.
+ * @param new_dlg Pointer to receive the new dialog.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_fork(const pjsip_dialog *original_dlg,
const pjsip_rx_data *rdata,
pjsip_dialog **new_dlg );
/**
+ * Forcefully terminate the dialog. Application can only call this function
+ * when there is no session associated to the dialog. If there are sessions
+ * that use this dialog, this function will refuse to terminate the dialog.
+ * For this case, application MUST call the appropriate termination function
+ * for each dialog session (e.g. #pjsip_inv_terminate() to terminate INVITE
+ * session).
+ *
+ * @param dlg The dialog.
+ *
+ * @return PJ_SUCCESS if dialog has been terminated.
+ */
+PJ_DECL(pj_status_t) pjsip_dlg_terminate( pjsip_dialog *dlg );
+
+
+/**
* Set dialog's initial route set to route_set list. This can only be called
* for UAC dialog, before any request is sent. After dialog has been
* established, the route set can not be changed.
@@ -177,6 +241,11 @@ PJ_DECL(pj_status_t) pjsip_dlg_fork(const pjsip_dialog *original_dlg,
*
* The route_set argument is standard list of Route headers (i.e. with
* sentinel).
+ *
+ * @param dlg The UAC dialog.
+ * @param route_set List of Route header.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dialog *dlg,
const pjsip_route_hdr *route_set );
@@ -184,6 +253,11 @@ PJ_DECL(pj_status_t) pjsip_dlg_set_route_set( pjsip_dialog *dlg,
/**
* Increment the number of sessions in the dialog. Note that initially
* (after created) the dialog has the session counter set to zero.
+ *
+ * @param dlg The dialog.
+ * @param mod The module that increments the session counter.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_inc_session( pjsip_dialog *dlg,
pjsip_module *mod);
@@ -194,12 +268,24 @@ PJ_DECL(pj_status_t) pjsip_dlg_inc_session( pjsip_dialog *dlg,
* reach zero and there is no pending transaction, the dialog will be
* destroyed. Note that this function may destroy the dialog immediately
* if there is no pending transaction when this function is called.
+ *
+ * @param dlg The dialog.
+ * @param mod The module that decrements the session counter.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_dec_session( pjsip_dialog *dlg,
pjsip_module *mod);
/**
* Add a module as dialog usage, and optionally set the module specific data.
+ *
+ * @param dlg The dialog.
+ * @param module The module to be registered as dialog usage.
+ * @param mod_data Optional arbitrary data to be attached to dialog's
+ * mod_data array at the module's index.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_add_usage( pjsip_dialog *dlg,
pjsip_module *module,
@@ -208,6 +294,13 @@ PJ_DECL(pj_status_t) pjsip_dlg_add_usage( pjsip_dialog *dlg,
/**
* Attach module specific data to the dialog. Application can also set
* the value directly by accessing dlg->mod_data[module_id].
+ *
+ * @param dlg The dialog
+ * @param mod_id The ID of the module from which the data is to be
+ * set to the dialog.
+ * @param data Arbitrary data.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_set_mod_data( pjsip_dialog *dlg,
int mod_id,
@@ -216,6 +309,12 @@ PJ_DECL(pj_status_t) pjsip_dlg_set_mod_data( pjsip_dialog *dlg,
/**
* Get module specific data previously attached to the dialog. Application
* can also get value directly by accessing dlg->mod_data[module_id].
+ *
+ * @param dlg The dialog
+ * @param mod_id The ID of the module from which the data is to be
+ * retrieved from the dialog.
+ *
+ * @return The data that was previously set, or NULL.
*/
PJ_DECL(void*) pjsip_dlg_get_mod_data( pjsip_dialog *dlg,
int mod_id);
@@ -224,12 +323,16 @@ PJ_DECL(void*) pjsip_dlg_get_mod_data( pjsip_dialog *dlg,
/**
* Lock dialog and increment session counter termporarily, to prevent it
* from being destroyed.
+ *
+ * @param dlg The dialog.
*/
PJ_DECL(void) pjsip_dlg_inc_lock( pjsip_dialog *dlg );
/**
* Unlock dialog and decrement temporary session counter. After this function
* is called, dialog may be destroyed.
+ *
+ * @param dlg The dialog.
*/
PJ_DECL(void) pjsip_dlg_dec_lock( pjsip_dialog *dlg );
@@ -239,11 +342,26 @@ PJ_DECL(void) pjsip_dlg_dec_lock( pjsip_dialog *dlg );
* matches an existing dialog, the user agent must have put the matching
* dialog instance in the rdata, or otherwise this function will return
* NULL if the message didn't match any existing dialog.
+ *
+ * This function can only be called after endpoint distributes the message
+ * to the transaction layer or UA layer. In other words, application can
+ * only call this function in the context of module that runs in priority
+ * number higher than PJSIP_MOD_PRIORITY_UA_PROXY_LAYER.
+ *
+ * @param rdata Incoming message buffer.
+ *
+ * @return The dialog instance that "owns" the message.
*/
PJ_DECL(pjsip_dialog*) pjsip_rdata_get_dlg( pjsip_rx_data *rdata );
/**
- * Get the associated dialog in a transaction.
+ * Get the associated dialog for the specified transaction, if any.
+ *
+ * @param tsx The transaction.
+ *
+ * @return The dialog instance which has been registered
+ * to the transaction as transaction user, or
+ * NULL if the transaction is outside any dialogs.
*/
PJ_DECL(pjsip_dialog*) pjsip_tsx_get_dlg( pjsip_transaction *tsx );
@@ -256,6 +374,16 @@ PJ_DECL(pjsip_dialog*) pjsip_tsx_get_dlg( pjsip_transaction *tsx );
* INVITE request as the parameter.
*
* This function will also put Contact header where appropriate.
+ *
+ * @param dlg The dialog instance.
+ * @param method The method of the request.
+ * @param cseq Optional CSeq, which only needs to be specified
+ * when creating ACK and CANCEL. For other requests,
+ * specify -1 to use dialog's internal counter.
+ * @param tdata Pointer to receive the request's transmit
+ * data buffer.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_create_request( pjsip_dialog *dlg,
const pjsip_method *method,
@@ -277,6 +405,13 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_request( pjsip_dialog *dlg,
*
* This function will decrement the transmit data's reference counter
* regardless the status of the operation.
+ *
+ * @param dlg The dialog.
+ * @param tdata The request message to be sent.
+ * @param p_tsx Optional argument to receive the transaction
+ * instance used to send the request.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_send_request ( pjsip_dialog *dlg,
pjsip_tx_data *tdata,
@@ -289,6 +424,16 @@ PJ_DECL(pj_status_t) pjsip_dlg_send_request ( pjsip_dialog *dlg,
* than endpoint's API #pjsip_endpt_create_response() in that the dialog
* function adds Contact header and Record-Routes headers in the response
* where appropriate.
+ *
+ * @param dlg The dialog.
+ * @param rdata The incoming request message for which the
+ * response will be created.
+ * @param st_code Status code.
+ * @param st_text Optional string for custom status reason text.
+ * @param tdata Pointer to receive the response message transmit
+ * data buffer.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
pjsip_rx_data *rdata,
@@ -300,6 +445,14 @@ PJ_DECL(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg,
/**
* Modify previously sent response with other status code. Contact header
* will be added when appropriate.
+ *
+ * @param dlg The dialog.
+ * @param tdata The transmit data buffer containing response
+ * message to be modified.
+ * @param st_code New status code to be set.
+ * @param st_text Optional string for custom status reason text.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg,
pjsip_tx_data *tdata,
@@ -313,6 +466,17 @@ PJ_DECL(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg,
*
* This function decrements the transmit data's reference counter regardless
* the status of the operation.
+ *
+ * @param dlg The dialog.
+ * @param tsx The UAS transaction associated with the incoming
+ * request. If the request is within a dialog, or
+ * a dialog has been created for the request that
+ * creates the dialog, application can get the
+ * transaction instance for the request by calling
+ * #pjsip_rdata_get_tsx().
+ * @param tdata Response message to be sent.
+ *
+ * @return PJ_SUCCESS on success.
*/
PJ_DECL(pj_status_t) pjsip_dlg_send_response( pjsip_dialog *dlg,
pjsip_transaction *tsx,
@@ -340,6 +504,11 @@ PJ_DECL(pj_status_t) pjsip_dlg_respond( pjsip_dialog *dlg,
const pjsip_hdr *hdr_list,
const pjsip_msg_body *body );
+
+/**
+ * @}
+ */
+
/*
* Internal (called by sip_ua_layer.c)
*/
@@ -356,9 +525,6 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg,
pjsip_rx_data *rdata );
-/**
- * @}
- */
PJ_END_DECL
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index 89153344..c4195a13 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -363,6 +363,15 @@ PJ_BEGIN_DECL
*/
#define PJSIP_ENOREFERSESSION (PJSIP_ERRNO_START_PJSIP+122) /* 171122 */
+/************************************************************
+ * INVITE SESSIONS ERRORS
+ ***********************************************************/
+/**
+ * @hideinitializer
+ * Session already terminated.
+ */
+#define PJSIP_ESESSIONTERMINATED (PJSIP_ERRNO_START_PJSIP+140) /* 171140 */
+
diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c
index 8218c76e..2a95b2cc 100644
--- a/pjsip/src/pjsip-simple/evsub.c
+++ b/pjsip/src/pjsip-simple/evsub.c
@@ -198,6 +198,7 @@ struct pjsip_evsub
struct evpkg *pkg; /**< The event package. */
unsigned option; /**< Options. */
pjsip_evsub_user user; /**< Callback. */
+ pj_bool_t call_cb; /**< Notify callback? */
pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */
pjsip_evsub_state state; /**< Subscription state. */
pj_str_t state_str; /**< String describing the state. */
@@ -509,6 +510,7 @@ static void evsub_destroy( pjsip_evsub *sub )
static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
const pj_str_t *state_str, pjsip_event *event)
{
+ pjsip_evsub_state prev_state = sub->state;
pj_str_t old_state_str = sub->state_str;
sub->state = state;
@@ -525,11 +527,12 @@ static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
(int)sub->state_str.slen,
sub->state_str.ptr));
- if (sub->user.on_evsub_state)
+ if (sub->user.on_evsub_state && sub->call_cb)
(*sub->user.on_evsub_state)(sub, event);
- if (state == PJSIP_EVSUB_STATE_TERMINATED) {
-
+ if (state == PJSIP_EVSUB_STATE_TERMINATED &&
+ prev_state != PJSIP_EVSUB_STATE_TERMINATED)
+ {
if (sub->pending_tsx == 0) {
evsub_destroy(sub);
}
@@ -559,7 +562,7 @@ static void on_timer( pj_timer_heap_t *timer_heap,
case TIMER_TYPE_UAC_REFRESH:
/* Time for UAC to refresh subscription */
- if (sub->user.on_client_refresh) {
+ if (sub->user.on_client_refresh && sub->call_cb) {
(*sub->user.on_client_refresh)(sub);
} else {
pjsip_tx_data *tdata;
@@ -576,7 +579,7 @@ static void on_timer( pj_timer_heap_t *timer_heap,
case TIMER_TYPE_UAS_TIMEOUT:
/* Refresh from UAC has not been received */
- if (sub->user.on_server_timeout) {
+ if (sub->user.on_server_timeout && sub->call_cb) {
(*sub->user.on_server_timeout)(sub);
} else {
pjsip_tx_data *tdata;
@@ -652,6 +655,7 @@ static pj_status_t evsub_create( pjsip_dialog *dlg,
sub->dlg = dlg;
sub->pkg = pkg;
sub->role = role;
+ sub->call_cb = PJ_TRUE;
sub->option = option;
sub->state = PJSIP_EVSUB_STATE_NULL;
sub->state_str = evsub_state_names[sub->state];
@@ -839,6 +843,29 @@ on_return:
/*
+ * Forcefully destroy subscription.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_terminate( pjsip_evsub *sub,
+ pj_bool_t notify )
+{
+ PJ_ASSERT_RETURN(sub, PJ_EINVAL);
+
+ pjsip_dlg_inc_lock(sub->dlg);
+
+ if (sub->pending_tsx) {
+ pj_assert(!"Unable to terminate when there's pending tsx");
+ pjsip_dlg_dec_lock(sub->dlg);
+ return PJ_EINVALIDOP;
+ }
+
+ sub->call_cb = notify;
+ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
+
+ pjsip_dlg_dec_lock(sub->dlg);
+ return PJ_SUCCESS;
+}
+
+/*
* Get subscription state.
*/
PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
@@ -1613,7 +1640,7 @@ static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
/* Call application registered callback to handle incoming NOTIFY,
* if any.
*/
- if (st_code==200 && sub->user.on_rx_notify) {
+ if (st_code==200 && sub->user.on_rx_notify && sub->call_cb) {
(*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
&res_hdr, &body);
@@ -1781,8 +1808,10 @@ static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
*/
pj_list_init(&res_hdr);
- (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
- &res_hdr, &body);
+ if (sub->user.on_rx_refresh && sub->call_cb) {
+ (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
+ &res_hdr, &body);
+ }
/* Application MUST specify final response! */
PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
@@ -1886,7 +1915,7 @@ static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
/* Call on_tsx_state callback, if any. */
- if (sub->user.on_tsx_state)
+ if (sub->user.on_tsx_state && sub->call_cb)
(*sub->user.on_tsx_state)(sub, tsx, event);
diff --git a/pjsip/src/pjsip-simple/iscomposing.c b/pjsip/src/pjsip-simple/iscomposing.c
index 369281af..38fa578e 100644
--- a/pjsip/src/pjsip-simple/iscomposing.c
+++ b/pjsip/src/pjsip-simple/iscomposing.c
@@ -80,9 +80,10 @@ PJ_DEF(pj_xml_node*) pjsip_iscomposing_create_xml( pj_pool_t *pool,
pj_xml_add_node(doc, node);
/* Add lastactive, if any. */
- if (!is_composing && lst_actv) {
- PJ_TODO(IMPLEMENT_LAST_ACTIVE_ATTRIBUTE);
- }
+ PJ_UNUSED_ARG(lst_actv);
+ //if (!is_composing && lst_actv) {
+ // PJ_TODO(IMPLEMENT_LAST_ACTIVE_ATTRIBUTE);
+ //}
/* Add contenttype, if any. */
if (content_tp) {
diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c
index f8014fce..88e38152 100644
--- a/pjsip/src/pjsip-simple/presence.c
+++ b/pjsip/src/pjsip-simple/presence.c
@@ -322,6 +322,15 @@ on_return:
/*
+ * Forcefully terminate presence.
+ */
+PJ_DEF(pj_status_t) pjsip_pres_terminate( pjsip_evsub *sub,
+ pj_bool_t notify )
+{
+ return pjsip_evsub_terminate(sub, notify);
+}
+
+/*
* Create SUBSCRIBE
*/
PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index 9b55f25b..c1142501 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -124,12 +124,25 @@ static pj_status_t mod_inv_unload(void)
void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state,
pjsip_event *e)
{
+ pjsip_inv_state prev_state = inv->state;
+
+ /* Set state. */
inv->state = state;
- if (mod_inv.cb.on_state_changed)
+
+ /* If state is DISCONNECTED, cause code MUST have been set. */
+ pj_assert(inv->state != PJSIP_INV_STATE_DISCONNECTED ||
+ inv->cause != 0);
+
+ /* Call on_state_changed() callback. */
+ if (mod_inv.cb.on_state_changed && inv->notify)
(*mod_inv.cb.on_state_changed)(inv, e);
- if (inv->state == PJSIP_INV_STATE_DISCONNECTED)
+ /* Only decrement when previous state is not already DISCONNECTED */
+ if (inv->state == PJSIP_INV_STATE_DISCONNECTED &&
+ prev_state != PJSIP_INV_STATE_DISCONNECTED)
+ {
pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod);
+ }
}
@@ -290,7 +303,7 @@ static void mod_inv_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e)
(*inv_state_handler[inv->state])(inv, e);
/* Call on_tsx_state */
- if (mod_inv.cb.on_tsx_state_changed)
+ if (mod_inv.cb.on_tsx_state_changed && inv->notify)
(*mod_inv.cb.on_tsx_state_changed)(inv, tsx, e);
/* Clear invite transaction when tsx is terminated. */
@@ -378,6 +391,8 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uac( pjsip_dialog *dlg,
inv->state = PJSIP_INV_STATE_NULL;
inv->dlg = dlg;
inv->options = options;
+ inv->notify = PJ_TRUE;
+ inv->cause = 0;
/* Object name will use the same dialog pointer. */
pj_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg);
@@ -774,6 +789,8 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg,
inv->state = PJSIP_INV_STATE_NULL;
inv->dlg = dlg;
inv->options = options;
+ inv->notify = PJ_TRUE;
+ inv->cause = 0;
/* Object name will use the same dialog pointer. */
pj_snprintf(inv->obj_name, PJ_MAX_OBJ_NAME, "inv%p", dlg);
@@ -833,6 +850,50 @@ PJ_DEF(pj_status_t) pjsip_inv_create_uas( pjsip_dialog *dlg,
return PJ_SUCCESS;
}
+/*
+ * Forcefully terminate the session.
+ */
+PJ_DEF(pj_status_t) pjsip_inv_terminate( pjsip_inv_session *inv,
+ int st_code,
+ pj_bool_t notify)
+{
+ PJ_ASSERT_RETURN(inv, PJ_EINVAL);
+
+ /* Lock dialog. */
+ pjsip_dlg_inc_lock(inv->dlg);
+
+ /* Set callback notify flag. */
+ inv->notify = notify;
+
+ /* If there's pending transaction, terminate the transaction.
+ * This may subsequently set the INVITE session state to
+ * disconnected.
+ */
+ if (inv->invite_tsx &&
+ inv->invite_tsx->state <= PJSIP_TSX_STATE_COMPLETED)
+ {
+ pjsip_tsx_terminate(inv->invite_tsx, st_code);
+
+ }
+
+ /* Set cause. */
+ inv->cause = st_code;
+
+ /* Forcefully terminate the session if state is not DISCONNECTED */
+ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
+ inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, NULL);
+ }
+
+ /* Done.
+ * The dec_lock() below will actually destroys the dialog if it
+ * has no other session.
+ */
+ pjsip_dlg_dec_lock(inv->dlg);
+
+ return PJ_SUCCESS;
+}
+
+
static void *clone_sdp(pj_pool_t *pool, const void *data, unsigned len)
{
PJ_UNUSED_ARG(len);
@@ -974,7 +1035,7 @@ static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv )
PJ_LOG(5,(inv->obj_name, "SDP negotiation done, status=%d", status));
- if (mod_inv.cb.on_media_update)
+ if (mod_inv.cb.on_media_update && inv->notify)
(*mod_inv.cb.on_media_update)(inv, status);
return status;
@@ -1063,7 +1124,7 @@ static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
/* Inform application about remote offer. */
- if (mod_inv.cb.on_rx_offer) {
+ if (mod_inv.cb.on_rx_offer && inv->notify) {
(*mod_inv.cb.on_rx_offer)(inv, sdp);
@@ -1304,6 +1365,9 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv,
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
+ /* Set cause code. */
+ if (inv->cause==0) inv->cause = st_code;
+
/* Create appropriate message. */
switch (inv->state) {
case PJSIP_INV_STATE_CALLING:
@@ -1358,8 +1422,7 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv,
case PJSIP_INV_STATE_DISCONNECTED:
/* No need to do anything. */
- PJ_TODO(RETURN_A_PROPER_STATUS_CODE_HERE);
- return PJ_EINVALIDOP;
+ return PJSIP_ESESSIONTERMINATED;
default:
pj_assert("!Invalid operation!");
@@ -1612,8 +1675,10 @@ static void inv_respond_incoming_bye( pjsip_inv_session *inv,
/* Terminate session: */
- if (inv->state != PJSIP_INV_STATE_DISCONNECTED)
+ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
+ if (inv->cause==0) inv->cause=PJSIP_SC_OK;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+ }
}
/*
@@ -1627,6 +1692,7 @@ static void inv_handle_bye_response( pjsip_inv_session *inv,
pj_status_t status;
if (e->body.tsx_state.type != PJSIP_EVENT_RX_MSG) {
+ if (inv->cause==0) inv->cause=PJSIP_SC_OK;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
return;
}
@@ -1646,6 +1712,7 @@ static void inv_handle_bye_response( pjsip_inv_session *inv,
/* Does not have proper credentials.
* End the session anyway.
*/
+ if (inv->cause==0) inv->cause=PJSIP_SC_OK;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
} else {
@@ -1656,7 +1723,7 @@ static void inv_handle_bye_response( pjsip_inv_session *inv,
} else {
/* End the session. */
-
+ if (inv->cause==0) inv->cause=PJSIP_SC_OK;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
@@ -1774,6 +1841,7 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e)
/* Does not have proper credentials.
* End the session.
*/
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
} else {
@@ -1788,6 +1856,7 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e)
} else {
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
@@ -1815,6 +1884,7 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e)
} else {
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
break;
@@ -1837,6 +1907,7 @@ static void inv_on_state_calling( pjsip_inv_session *inv, pjsip_event *e)
tsx->status_code == PJSIP_SC_TSX_TIMEOUT ||
PJSIP_SC_TSX_TRANSPORT_ERROR)
{
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
}
@@ -1877,10 +1948,12 @@ static void inv_on_state_incoming( pjsip_inv_session *inv, pjsip_event *e)
/*
* Transaction sent final response.
*/
- if (tsx->status_code/100 == 2)
+ if (tsx->status_code/100 == 2) {
inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e);
- else
+ } else {
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+ }
break;
case PJSIP_TSX_STATE_TERMINATED:
@@ -1888,6 +1961,7 @@ static void inv_on_state_incoming( pjsip_inv_session *inv, pjsip_event *e)
* This happens on transport error (e.g. failed to send
* response)
*/
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
break;
@@ -1948,8 +2022,10 @@ static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e)
e->body.tsx_state.src.rdata);
}
- } else
+ } else {
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+ }
break;
case PJSIP_TSX_STATE_CONFIRMED:
@@ -1981,6 +2057,7 @@ static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e)
}
} else {
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
break;
@@ -2016,6 +2093,7 @@ static void inv_on_state_early( pjsip_inv_session *inv, pjsip_event *e)
tsx->status_code == PJSIP_SC_TSX_TIMEOUT ||
PJSIP_SC_TSX_TRANSPORT_ERROR)
{
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
}
@@ -2049,6 +2127,7 @@ static void inv_on_state_connecting( pjsip_inv_session *inv, pjsip_event *e)
* error.
*/
if (tsx->status_code/100 != 2) {
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
break;
@@ -2240,6 +2319,7 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e)
/*
* Handle responses that terminates dialog.
*/
+ if (inv->cause==0) inv->cause = tsx->status_code;
inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
}
}
diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c
index 4139b221..795e103b 100644
--- a/pjsip/src/pjsip/sip_dialog.c
+++ b/pjsip/src/pjsip/sip_dialog.c
@@ -613,6 +613,21 @@ static pj_status_t unregister_and_destroy_dialog( pjsip_dialog *dlg )
/*
+ * Forcefully terminate dialog.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_terminate( pjsip_dialog *dlg )
+{
+ /* Number of sessions must be zero. */
+ PJ_ASSERT_RETURN(dlg->sess_count==0, PJ_EINVALIDOP);
+
+ /* MUST not have pending transactions. */
+ PJ_ASSERT_RETURN(dlg->tsx_count==0, PJ_EINVALIDOP);
+
+ return unregister_and_destroy_dialog(dlg);
+}
+
+
+/*
* Set route_set
*/
PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dialog *dlg,
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index d62d9188..87909e6b 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -82,7 +82,12 @@ static const struct
{ PJSIP_EAUTHINVALIDDIGEST, "Invalid authorization digest" },
/* UA/dialog layer. */
- { PJSIP_EMISSINGTAG, "Missing From/To tag parameter" }
+ { PJSIP_EMISSINGTAG, "Missing From/To tag parameter" },
+ { PJSIP_ENOTREFER, "Expecting REFER request"} ,
+ { PJSIP_ENOREFERSESSION, "Not associated with REFER subscription"},
+
+ /* Invite session. */
+ { PJSIP_ESESSIONTERMINATED, "Session already terminated" },
};