diff options
Diffstat (limited to 'pjsip')
-rw-r--r-- | pjsip/include/pjsip-simple/evsub.h | 15 | ||||
-rw-r--r-- | pjsip/include/pjsip-simple/presence.h | 17 | ||||
-rw-r--r-- | pjsip/include/pjsip-ua/sip_inv.h | 25 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_dialog.h | 186 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_errno.h | 9 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/evsub.c | 47 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/iscomposing.c | 7 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/presence.c | 9 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 104 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 15 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_errno.c | 7 |
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" }, }; |