From 630789155732b6c91a8d69474ec38096ab1f739a Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Tue, 26 Feb 2008 17:00:03 +0000 Subject: [PATCH] merge to sofia darcs: Mon Feb 25 09:49:39 EST 2008 Pekka.Pessi@nokia.com * nua_subnotref.c: fixed problems re-calculating the subscription duration upon NOTIFY Thanks for Colin Whittaker for reporting this problem. Wed Feb 6 12:05:13 EST 2008 Pekka.Pessi@nokia.com * nua_stack.c: pass events while in shutdown if NUTAG_SHUTDOWN_EVENTS(1) has been set Wed Feb 6 12:05:46 EST 2008 Pekka.Pessi@nokia.com * nua: using global preferences. Added NUTAG_SHUTDOWN_EVENTS(). Mon Feb 25 12:10:31 EST 2008 Pekka.Pessi@nokia.com * nua: renamed crm_deinit as crm_complete, commented nua_client_methods_t initializers Mon Feb 25 14:14:15 EST 2008 Pekka.Pessi@nokia.com * nua: added NUTAG_SUB_EXPIRES() Tue Feb 26 11:09:37 EST 2008 Pekka.Pessi@nokia.com * nua: initial fix for sf.net bug #1827511 BYE can now be challenged. Tue Feb 26 11:19:40 EST 2008 Pekka.Pessi@nokia.com * nua_session.c: ensure correct call state Avoid assert() on bad input from network - crash reported by Michael Jerris. Also if calls are being terminated, reject new INVITE/UPDATE/PRACK requests with 481. Tue Feb 26 11:33:19 EST 2008 Pekka.Pessi@nokia.com * sdp_print.c: pt 9 is, like, g722. 19 is used by nobody, so it can be used as filler? Tue Feb 26 11:40:00 EST 2008 Pekka.Pessi@nokia.com * nua_session.c: fixed non-compiling fix on session state check git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@7738 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- .../libsofia-sip-ua/nua/nua_dialog.c | 8 +- .../libsofia-sip-ua/nua/nua_dialog.h | 6 +- .../libsofia-sip-ua/nua/nua_extension.c | 19 +- .../libsofia-sip-ua/nua/nua_message.c | 20 +- .../libsofia-sip-ua/nua/nua_notifier.c | 51 +- .../libsofia-sip-ua/nua/nua_options.c | 19 +- .../libsofia-sip-ua/nua/nua_params.c | 361 +++++----- .../libsofia-sip-ua/nua/nua_params.h | 35 +- .../libsofia-sip-ua/nua/nua_publish.c | 20 +- .../libsofia-sip-ua/nua/nua_register.c | 19 +- .../libsofia-sip-ua/nua/nua_session.c | 215 +++--- .../sofia-sip/libsofia-sip-ua/nua/nua_stack.c | 17 +- .../sofia-sip/libsofia-sip-ua/nua/nua_stack.h | 30 +- .../libsofia-sip-ua/nua/nua_subnotref.c | 48 +- libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c | 102 ++- .../libsofia-sip-ua/nua/sofia-sip/nua_tag.h | 13 +- .../libsofia-sip-ua/nua/test_100rel.c | 6 +- .../libsofia-sip-ua/nua/test_cancel_bye.c | 76 ++- .../sofia-sip/libsofia-sip-ua/nua/test_init.c | 3 + .../libsofia-sip-ua/nua/test_nua_params.c | 9 +- .../libsofia-sip-ua/nua/test_proxy.c | 38 +- .../libsofia-sip-ua/nua/test_proxy.h | 5 + .../libsofia-sip-ua/nua/test_simple.c | 623 ++++++++++++++++-- .../sofia-sip/libsofia-sip-ua/sdp/sdp_print.c | 4 +- 24 files changed, 1283 insertions(+), 464 deletions(-) diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c index 2850de6220..18c7bb7e20 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.c @@ -373,8 +373,12 @@ void nua_dialog_usage_remove_at(nua_owner_t *own, /* Destroy saved client request */ if (nua_client_is_bound(du->du_cr)) { nua_client_bind(cr = du->du_cr, NULL); - if (!nua_client_is_queued(cr) && - !nua_client_is_reporting(cr)) + + if (nua_client_is_queued(cr)) + nua_client_request_complete(cr); + else if (nua_client_is_reporting(cr)) + ; + else nua_client_request_destroy(cr); } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h index 63596206bd..5e1ba48567 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_dialog.h @@ -253,11 +253,11 @@ typedef struct { nta_outgoing_t *orq, tagi_t const *tags); - /** @a crm_deinit is called when a client-side request is destroyed. + /** @a crm_complete is called when a client-side request is destroyed. * * @return The return value should be 0. It is currently ignored. */ - int (*crm_deinit)(nua_client_request_t *); + int (*crm_complete)(nua_client_request_t *); } nua_client_methods_t; @@ -516,6 +516,8 @@ void *nua_private_client_request(nua_client_request_t const *cr) return (void *)(cr + 1); } +void nua_client_request_complete(nua_client_request_t *); + void nua_client_request_destroy(nua_client_request_t *); int nua_client_request_queue(nua_client_request_t *cr); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c index 333ed9f239..c9237b6ba0 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_extension.c @@ -74,18 +74,21 @@ */ static nua_client_methods_t const nua_method_client_methods = { - SIP_METHOD_UNKNOWN, - 0, - { + SIP_METHOD_UNKNOWN, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 0, /* target_refresh */ 1, }, - /* nua_method_client_template */ NULL, - /* nua_method_client_init */ NULL, - /* nua_method_client_request */ NULL, - /* nua_method_client_check_restart */ NULL, - /* nua_method_client_response */ NULL + NULL, /* crm_template */ + NULL, /* crm_init */ + NULL, /* crm_send */ + NULL, /* crm_check_restart */ + NULL, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; int diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c index 8f8fc391b3..1d6c3a2250 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_message.c @@ -77,18 +77,22 @@ static int nua_message_client_init(nua_client_request_t *cr, tagi_t const *tags); static nua_client_methods_t const nua_message_client_methods = { - SIP_METHOD_MESSAGE, - 0, - { + SIP_METHOD_MESSAGE, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 0, /* target refresh */ 0 }, - /* nua_message_client_template */ NULL, - nua_message_client_init, - /*nua_message_client_request*/ NULL, - /* nua_message_client_check_restart */ NULL, - /*nua_message_client_response*/ NULL + NULL, /* crm_template */ + nua_message_client_init, /* crm_init */ + NULL, /* crm_send */ + NULL, /* crm_check_restart */ + NULL, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ + }; int diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c index a0e18efad7..35b59d16ed 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c @@ -237,7 +237,8 @@ int nua_subscribe_server_preprocess(nua_server_request_t *sr) sip_event_t *o = sip->sip_event; char const *event = o ? o->o_type : NULL; /* Maximum expiration time */ - unsigned long expires = 3600; + unsigned long expires = sip->sip_expires ? sip->sip_expires->ex_delta : 3600; + sip_time_t now = sip_now(); assert(nh && nh->nh_nua->nua_dhandle != nh); @@ -259,9 +260,10 @@ int nua_subscribe_server_preprocess(nua_server_request_t *sr) nu = nua_dialog_usage_private(du); - if (sip->sip_expires && sip->sip_expires->ex_delta < expires) - expires = sip->sip_expires->ex_delta; - nu->nu_requested = sip_now() + expires; + if (now + expires >= now) + nu->nu_requested = now + expires; + else + nu->nu_requested = SIP_TIME_MAX - 1; #if SU_HAVE_EXPERIMENTAL nu->nu_etags = @@ -295,9 +297,23 @@ int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags) sip_time_t now = sip_now(); if (nu->nu_requested) { - if (nu->nu_requested > nu->nu_expires) + if (sip->sip_expires) { + /* Expires in response can only shorten the expiration time */ + if (nu->nu_requested > now + sip->sip_expires->ex_delta) + nu->nu_requested = now + sip->sip_expires->ex_delta; + } + else { + unsigned sub_expires = NH_PGET(sr->sr_owner, sub_expires); + if (nu->nu_requested > now + sub_expires) + nu->nu_requested = now + sub_expires; + } + + if (nu->nu_requested >= now) nu->nu_expires = nu->nu_requested; - else if (nu->nu_expires <= now || nu->nu_requested <= now) + else + nu->nu_expires = now; + + if (nu->nu_expires <= now) nu->nu_substate = nua_substate_terminated; } @@ -305,7 +321,7 @@ int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags) ex->ex_delta = nu->nu_expires - now; } else { - /* Add header Expires: 0 */ + /* Always add header Expires: 0 */ } if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta) @@ -420,20 +436,21 @@ static int nua_notify_client_report(nua_client_request_t *cr, tagi_t const *tags); static nua_client_methods_t const nua_notify_client_methods = { - SIP_METHOD_NOTIFY, - 0, - { + SIP_METHOD_NOTIFY, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 1, /* in_dialog */ 1, /* target refresh */ 1 }, - /* nua_notify_client_template */ NULL, - nua_notify_client_init, - nua_notify_client_request, - /* nua_notify_client_check_restart */ NULL, - /* nua_notify_client_response */ NULL, - /* nua_notify_client_preliminary */ NULL, - nua_notify_client_report + NULL, /* crm_template */ + nua_notify_client_init, /* crm_init */ + nua_notify_client_request, /* crm_send */ + NULL, /* crm_check_restart */ + NULL, /* crm_recv */ + NULL, /* crm_preliminary */ + nua_notify_client_report, /* crm_report */ + NULL, /* crm_complete */ }; /**@internal Send NOTIFY. */ diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c index 82e5c23cf9..462a15f552 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_options.c @@ -88,18 +88,21 @@ */ static nua_client_methods_t const nua_options_client_methods = { - SIP_METHOD_OPTIONS, - 0, - { + SIP_METHOD_OPTIONS, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 0, /* target refresh */ 0 }, - /*nua_options_client_template*/ NULL, - /*nua_options_client_init*/ NULL, - /*nua_options_client_request*/ NULL, - /* nua_options_client_check_restart */ NULL, - /*nua_options_client_response*/ NULL + NULL, /* crm_template */ + NULL, /* crm_init */ + NULL, /* crm_send */ + NULL, /* crm_check_restart */ + NULL, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; int nua_stack_options(nua_t *nua, diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c index 206574e304..5568639b23 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.c @@ -77,19 +77,27 @@ su_inline int nhp_is_any_set(nua_handle_preferences_t const *nhp) su_inline void nhp_or_set(nua_handle_preferences_t *a, nua_handle_preferences_t const *b) { - unsigned *ap = a->nhp_set_.set_unsigned; - unsigned const *bp = b->nhp_set_.set_unsigned; - size_t i; - memcpy(a, b, offsetof(nua_handle_preferences_t, nhp_set)); + /* Bitwise or of bitfields, casted to unsigned */ + a->nhp_set_.set_unsigned[0] |= b->nhp_set_.set_unsigned[0]; + a->nhp_set_.set_unsigned[1] |= b->nhp_set_.set_unsigned[1]; + + /* + unsigned *ap, const *bp; + size_t i; + + ap = a->nhp_set_.set_unsigned; + bp = b->nhp_set_.set_unsigned; for (i = 0; i < (sizeof a->nhp_set); i += (sizeof *ap)) *ap++ |= *bp++; + + */ } static int nhp_set_tags(su_home_t *home, nua_handle_preferences_t *nhp, - int global, + nua_global_preferences_t *ngp, tagi_t const *tags); static int nhp_merge_lists(su_home_t *home, @@ -101,11 +109,11 @@ static int nhp_merge_lists(su_home_t *home, int always_merge, tag_value_t value); -static -nua_handle_preferences_t *nhp_move_params(su_home_t *home, - nua_handle_preferences_t *dst, - su_home_t *tmphome, - nua_handle_preferences_t const *src); +static int nhp_save_params(nua_handle_t *nh, + su_home_t *tmphome, + nua_global_preferences_t *gsrc, + nua_handle_preferences_t *src); + /* ====================================================================== */ /* Magical NUTAG_USER_AGENT() - add NHP_USER_AGENT there if it is not there */ @@ -169,6 +177,7 @@ int nua_stack_set_defaults(nua_handle_t *nh, NHP_SET(nhp, refer_with_id, 1); NHP_SET(nhp, substate, nua_substate_active); + NHP_SET(nhp, sub_expires, 3600); NHP_SET(nhp, allow, sip_allow_make(home, nua_allow_str)); NHP_SET(nhp, supported, sip_supported_make(home, "timer, 100rel")); @@ -198,20 +207,12 @@ int nua_stack_set_from(nua_t *nua, int initial, tagi_t const *tags) sip_from_t *f = NULL, f0[1]; int set; - char const *uicc_name = "default"; - tl_gets(tags, /* By nua_stack_set_from() */ SIPTAG_FROM_REF(from), SIPTAG_FROM_STR_REF(str), - NUTAG_UICC_REF(uicc_name), TAG_END()); -#if HAVE_UICC_H - if (initial && uicc_name) - nua->nua_uicc = uicc_create(root, uicc_name); -#endif - if (!initial && from == NONE && str == NONE) return 0; @@ -325,6 +326,7 @@ int nua_stack_init_instance(nua_handle_t *nh, tagi_t const *tags) * NUTAG_SMIME_SIGNATURE() \n * NUTAG_SOA_NAME() \n * NUTAG_SUBSTATE() \n + * NUTAG_SUB_EXPIRES() \n * NUTAG_SUPPORTED(), SIPTAG_SUPPORTED(), and SIPTAG_SUPPORTED_STR() \n * NUTAG_UPDATE_REFRESH() \n * NUTAG_USER_AGENT(), SIPTAG_USER_AGENT() and SIPTAG_USER_AGENT_STR() \n @@ -437,6 +439,7 @@ int nua_stack_init_instance(nua_handle_t *nh, tagi_t const *tags) * NUTAG_SESSION_TIMER() \n * NUTAG_SOA_NAME() \n * NUTAG_SUBSTATE() \n + * NUTAG_SUB_EXPIRES() \n * NUTAG_SUPPORTED(), SIPTAG_SUPPORTED(), and SIPTAG_SUPPORTED_STR() \n * NUTAG_UPDATE_REFRESH() \n * NUTAG_USER_AGENT(), SIPTAG_USER_AGENT() and SIPTAG_USER_AGENT_STR() \n @@ -475,146 +478,124 @@ int nua_stack_set_params(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags) { nua_handle_t *dnh = nua->nua_dhandle; - nua_handle_preferences_t tmp[1], *nhp = nh->nh_prefs; - nua_handle_preferences_t const *dnhp = dnh->nh_prefs; - - su_home_t tmphome[1] = { SU_HOME_INIT(tmphome) }; - - tagi_t const *ptags; - - int error, global; - int status = 900; - char const *phrase = "Error storing parameters"; - sip_supported_t const *supported = NULL; - sip_allow_t const *allow = NULL; - sip_allow_events_t const *allow_events = NULL; - sip_allow_t const *appl_method = NULL; + + int status; + char const *phrase; enter; - ptags = !nh->nh_used_ptags ? nh->nh_ptags : NULL; + { + su_home_t tmphome[1] = { SU_HOME_INIT(tmphome) }; + nua_handle_preferences_t *nhp = nh->nh_prefs; + nua_handle_preferences_t const *dnhp = dnh->nh_prefs; + nua_handle_preferences_t tmp[1]; + nua_global_preferences_t gtmp[1], *ngp = NULL; - *tmp = *nhp; NHP_UNSET_ALL(tmp); + *tmp = *nhp; NHP_UNSET_ALL(tmp); - /* Supported features, allowed methods and events are merged - with previous ones */ - if (!NHP_ISSET(nhp, supported)) - supported = tmp->nhp_supported = dnhp->nhp_supported; - if (!NHP_ISSET(nhp, allow)) - allow = tmp->nhp_allow = dnhp->nhp_allow; - if (!NHP_ISSET(nhp, allow_events)) - allow_events = tmp->nhp_allow_events = dnhp->nhp_allow_events; - if (!NHP_ISSET(nhp, appl_method)) - appl_method = tmp->nhp_appl_method = dnhp->nhp_appl_method; - - error = 0; - global = nh == dnh; /* save also stack-specific params */ + /* + * Supported features, allowed methods and events are merged + * with previous or default settings. + * + * Here we just copy pointers from default settings. However when saving + * settings we have to be extra careful so that we + * 1) zero the pointers if the setting has not been modified + * 2) do not free pointer if the setting has been modified + * See NHP_ZAP_OVERRIDEN() below for gorier details. + */ + if (!NHP_ISSET(nhp, supported)) + tmp->nhp_supported = dnhp->nhp_supported; + if (!NHP_ISSET(nhp, allow)) + tmp->nhp_allow = dnhp->nhp_allow; + if (!NHP_ISSET(nhp, allow_events)) + tmp->nhp_allow_events = dnhp->nhp_allow_events; + if (!NHP_ISSET(nhp, appl_method)) + tmp->nhp_appl_method = dnhp->nhp_appl_method; - /* Set and save parameters to tmp */ - if (nhp_set_tags(tmphome, tmp, global, ptags) < 0) - error = 1, phrase = "Error storing default handle parameters"; - else if (nhp_set_tags(tmphome, tmp, global, tags) < 0) - error = 1, phrase = "Error storing parameters"; - else { - if (NHP_IS_ANY_SET(tmp)) { - if (tmp->nhp_supported == supported) - tmp->nhp_supported = NULL; + if (nh == dnh) /* nua_set_params() call, save stack-wide params, too */ + ngp = gtmp, *gtmp = *nua->nua_prefs; - if (tmp->nhp_allow == allow) - tmp->nhp_allow = NULL; + /* Set and save parameters to tmp */ + if (!nh->nh_used_ptags && + nhp_set_tags(tmphome, tmp, NULL, nh->nh_ptags) < 0) + status = 900, phrase = "Error storing default handle parameters"; + else if (nhp_set_tags(tmphome, tmp, ngp, tags) < 0) + status = 900, phrase = "Error storing parameters"; + else if (nhp_save_params(nh, tmphome, ngp, tmp) < 0) + status = 900, phrase = su_strerror(ENOMEM); + else + status = 200, phrase = "OK", nh->nh_used_ptags = 1; - if (tmp->nhp_allow_events == allow_events) - tmp->nhp_allow_events = NULL; + su_home_deinit(tmphome); + } - if (tmp->nhp_appl_method == appl_method) - tmp->nhp_appl_method = NULL; + if (status == 200) { + nua_handle_preferences_t const *nhp = nh->nh_prefs; + nua_handle_preferences_t const *dnhp = dnh->nh_prefs; - /* Move parameters from tmp to nhp (or allocate new nhp) */ - if (nh != dnh && nhp == dnh->nh_prefs) - nhp = NULL; - nhp = nhp_move_params(nh->nh_home, nhp, tmphome, tmp); + if (!nh->nh_soa && NHP_GET(nhp, dnhp, media_enable)) { + /* Create soa when needed */ + char const *soa_name = NHP_GET(nhp, dnhp, soa_name); - if (nhp) - nh->nh_prefs = nhp; - else - /* Fail miserably with ENOMEM */ - error = 1, status = 900, phrase = su_strerror(ENOMEM); + if (dnh->nh_soa) + nh->nh_soa = soa_clone(dnh->nh_soa, nua->nua_root, nh); + else + nh->nh_soa = soa_create(soa_name, nua->nua_root, nh); + + if (!nh->nh_soa) + status = 900, phrase = "Error Creating SOA Object"; + else if (soa_set_params(nh->nh_soa, TAG_NEXT(nh->nh_ptags)) < 0) + status = 900, phrase = "Error Setting SOA Parameters"; + } + else if (nh->nh_soa && !NHP_GET(nhp, dnhp, media_enable)) { + /* ... destroy soa when not needed */ + soa_destroy(nh->nh_soa), nh->nh_soa = NULL; } - if (!error) - nh->nh_used_ptags = 1; + if (status == 200 && tags && nh->nh_soa && + soa_set_params(nh->nh_soa, TAG_NEXT(tags)) < 0) + status = 900, phrase = "Error Setting SOA Parameters"; } - su_home_deinit(tmphome); + if (status == 200 && nh == dnh) { + /* Set stack-specific things below */ + if (nua_stack_set_smime_params(nua, tags) < 0) { + status = 900, phrase = "Error setting S/MIME parameters"; + } + else if (nua->nua_nta && nta_agent_set_params(nua->nua_nta, TAG_NEXT(tags)) < 0) { + status = 900, phrase = "Error setting NTA parameters"; + } + else { + nua_stack_set_from(nua, 0, tags); - if (error) - ; - else if (!nh->nh_soa && NHP_GET(nhp, dnhp, media_enable)) { - /* Create soa when needed */ - char const *soa_name = NHP_GET(nhp, dnhp, soa_name); - - if (dnh->nh_soa) - nh->nh_soa = soa_clone(dnh->nh_soa, nua->nua_root, nh); - else - nh->nh_soa = soa_create(soa_name, nua->nua_root, nh); - - ptags = nh->nh_ptags; - - if (!nh->nh_soa) - error = 1, status = 900, phrase = "Error Creating SOA Object"; - } - else if (nh->nh_soa && !NHP_GET(nhp, dnhp, media_enable)) { - /* ... destroy soa when not needed */ - soa_destroy(nh->nh_soa), nh->nh_soa = NULL; + if (nua->nua_prefs->ngp_detect_network_updates) + nua_stack_launch_network_change_detector(nua); + } } - if (!error && nh->nh_soa) { - if ((ptags && soa_set_params(nh->nh_soa, TAG_NEXT(ptags)) < 0) || - (tags && soa_set_params(nh->nh_soa, TAG_NEXT(tags)) < 0)) - error = 1, status = 900, phrase = "Error Setting SOA Parameters"; - } - - if (error || nh != dnh) { - ; - } - else if (nua_stack_set_smime_params(nua, tags) < 0) { - error = 1, status = 900, phrase = "Error setting S/MIME parameters"; - } - else if (!nua->nua_nta) { - } - /* Set stack-specific things below */ - else if (nta_agent_set_params(nua->nua_nta, TAG_NEXT(tags)) < 0) { - status = 900, phrase = "Error setting NTA parameters"; - error = 1; - } - else { - nua_stack_set_from(nua, 0, tags); - if (NHP_ISSET(nhp, detect_network_updates)) - nua_stack_launch_network_change_detector(nua); - } - - if (error) { + if (status != 200) { if (e == nua_i_none) SU_DEBUG_1(("nua_set_params(): failed: %s\n", phrase)); return UA_EVENT2(e, status, phrase), -1; } - - if (e == nua_r_set_params) - UA_EVENT2(e, 200, "OK"); - - return 0; + else { + if (e == nua_r_set_params) + UA_EVENT2(e, status, phrase); + return 0; + } } -/** Parse parameters from tags to @a nhp. + +/** Parse parameters from tags to @a nhp or @a ngp. * * @param home allocate new values from @a home * @param nhp structure to store handle preferences - * @param global if true, save also global parameters + * @param ngp structure to store global preferences * @param tags list of tags to parse */ static int nhp_set_tags(su_home_t *home, nua_handle_preferences_t *nhp, - int global, + nua_global_preferences_t *ngp, tagi_t const *tags) { @@ -826,6 +807,10 @@ static int nhp_set_tags(su_home_t *home, else if (tag == nutag_substate) { NHP_SET(nhp, substate, (int)value); } + /* NUTAG_SUB_EXPIRES(sub_expires) */ + else if (tag == nutag_sub_expires) { + NHP_SET(nhp, sub_expires, value); + } /* NUTAG_KEEPALIVE(keepalive) */ else if (tag == nutag_keepalive) { NHP_SET(nhp, keepalive, (unsigned)value); @@ -997,7 +982,7 @@ static int nhp_set_tags(su_home_t *home, NHP_SET_STR(nhp, outbound, value); } /* NUTAG_DETECT_NETWORK_UPDATES(detect_network_updates) */ - else if (global && tag == nutag_detect_network_updates) { + else if (ngp && tag == nutag_detect_network_updates) { int detector = (int)value; if (detector < NUA_NW_DETECT_NOTHING) @@ -1005,7 +990,13 @@ static int nhp_set_tags(su_home_t *home, else if (detector > NUA_NW_DETECT_TRY_FULL) detector = NUA_NW_DETECT_TRY_FULL; - NHP_SET(nhp, detect_network_updates, detector); + ngp->ngp_detect_network_updates = detector; + ngp->ngp_set.ngp_detect_network_updates = 1; + } + /* NUTAG_SHUTDOWN_EVENTS() */ + else if (ngp && tag == nutag_shutdown_events) { + ngp->ngp_shutdown_events = value != 0; + ngp->ngp_set.ngp_shutdown_events = 1; } } @@ -1078,54 +1069,70 @@ static int nhp_merge_lists(su_home_t *home, return 1; } -static -nua_handle_preferences_t *nhp_move_params(su_home_t *home, - nua_handle_preferences_t *dst, - su_home_t *tmphome, - nua_handle_preferences_t const *src) +/** Save parameters in @a gtmp and @a tmp. + */ +static +int nhp_save_params(nua_handle_t *nh, + su_home_t *tmphome, + nua_global_preferences_t *gsrc, + nua_handle_preferences_t *src) { - /* Update prefs structure */ - nua_handle_preferences_t tbf[1]; + su_home_t *home = nh->nh_home; + nua_t *nua = nh->nh_nua; + nua_handle_t *dnh = nua->nua_dhandle; + nua_handle_preferences_t *dst = nh->nh_prefs, old[1]; - if (dst == NULL) - dst = su_zalloc(home, sizeof *dst); - if (dst == NULL) - return NULL; - if (su_home_move(home, tmphome) < 0) - return NULL; + if (gsrc) { + *nua->nua_prefs = *gsrc; /* No pointers this far */ + } - *tbf = *dst; + if (!NHP_IS_ANY_SET(src)) + return 0; + + if (nh == dnh || nh->nh_prefs != dnh->nh_prefs) { + dst = nh->nh_prefs, *old = *dst; + assert(dst); + } + else { + dst = su_zalloc(home, sizeof *dst), memset(old, 0, sizeof *old); + if (!dst) + return -1; + } + + /* Move allocations from tmphome to home */ + su_home_move(nh->nh_home, tmphome); + + /* Copy parameters that are set from src to dst */ nhp_or_set(dst, src); /* Handle pointer items. Free changed ones and zap unset ones. */ -#define NHP_ZAP_OVERRIDEN(tbf, nhp, pref) \ - (((tbf)->nhp_set.nhb_##pref \ - && (tbf)->nhp_##pref != (nhp)->nhp_##pref \ - ? su_free(home, (void *)(tbf)->nhp_##pref) : (void)0), \ - (void)(!(nhp)->nhp_set.nhb_##pref ? (nhp)->nhp_##pref = NULL : NULL)) + /* Note that pointer items where !NHP_ISSET(old, pref) are not freed + (because they were just on loan from the default preference set) */ +#define NHP_ZAP_OVERRIDEN(old, dst, free, pref) \ + (((NHP_ISSET(old, pref) && \ + (old)->nhp_##pref && (old)->nhp_##pref != (dst)->nhp_##pref) \ + ? (free)(home, (void *)(old)->nhp_##pref) : (void)0), \ + (void)(!(dst)->nhp_set.nhb_##pref ? (dst)->nhp_##pref = NULL : NULL)) - NHP_ZAP_OVERRIDEN(tbf, dst, soa_name); - NHP_ZAP_OVERRIDEN(tbf, dst, registrar); - NHP_ZAP_OVERRIDEN(tbf, dst, supported); - NHP_ZAP_OVERRIDEN(tbf, dst, allow); - NHP_ZAP_OVERRIDEN(tbf, dst, user_agent); - NHP_ZAP_OVERRIDEN(tbf, dst, organization); - NHP_ZAP_OVERRIDEN(tbf, dst, instance); - NHP_ZAP_OVERRIDEN(tbf, dst, m_display); - NHP_ZAP_OVERRIDEN(tbf, dst, m_username); - NHP_ZAP_OVERRIDEN(tbf, dst, m_params); - NHP_ZAP_OVERRIDEN(tbf, dst, m_features); - NHP_ZAP_OVERRIDEN(tbf, dst, outbound); + NHP_ZAP_OVERRIDEN(old, dst, su_free, soa_name); + NHP_ZAP_OVERRIDEN(old, dst, su_free, registrar); + NHP_ZAP_OVERRIDEN(old, dst, msg_header_free, allow); + NHP_ZAP_OVERRIDEN(old, dst, msg_header_free, supported); + NHP_ZAP_OVERRIDEN(old, dst, msg_header_free, allow_events); + NHP_ZAP_OVERRIDEN(old, dst, su_free, user_agent); + NHP_ZAP_OVERRIDEN(old, dst, su_free, organization); + NHP_ZAP_OVERRIDEN(old, dst, su_free, m_display); + NHP_ZAP_OVERRIDEN(old, dst, su_free, m_username); + NHP_ZAP_OVERRIDEN(old, dst, su_free, m_params); + NHP_ZAP_OVERRIDEN(old, dst, su_free, m_features); + NHP_ZAP_OVERRIDEN(old, dst, su_free, instance); + NHP_ZAP_OVERRIDEN(old, dst, su_free, outbound); + NHP_ZAP_OVERRIDEN(old, dst, msg_header_free, appl_method); + NHP_ZAP_OVERRIDEN(old, dst, msg_header_free, initial_route); -#define NHP_ZAP_OVERRIDEN_HDR(tbf, nhp, pref) \ - (((tbf)->nhp_set.nhb_##pref \ - && (tbf)->nhp_##pref != (nhp)->nhp_##pref \ - ? msg_header_free(home, (void *)(tbf)->nhp_##pref) : (void)0), \ - (void)(!(nhp)->nhp_set.nhb_##pref ? (nhp)->nhp_##pref = NULL : NULL)) - - NHP_ZAP_OVERRIDEN_HDR(tbf, dst, initial_route); - - return dst; + nh->nh_prefs = dst; + + return 0; } static int nua_handle_tags_filter(tagi_t const *f, tagi_t const *t); @@ -1475,6 +1482,7 @@ int nua_stack_set_smime_params(nua_t *nua, tagi_t const *tags) * NUTAG_SMIME_SIGNATURE() \n * NUTAG_SOA_NAME() \n * NUTAG_SUBSTATE() \n + * NUTAG_SUB_EXPIRES() \n * NUTAG_UPDATE_REFRESH() \n * NUTAG_USER_AGENT() \n * SIPTAG_ALLOW() \n @@ -1515,6 +1523,7 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags) { nua_handle_t *dnh = nua->nua_dhandle; + nua_global_preferences_t const *ngp = nua->nua_prefs; nua_handle_preferences_t const *nhp = nh->nh_prefs; tagi_t *lst; @@ -1540,8 +1549,6 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e, enter; - su_home_auto(tmphome, sizeof(tmphome)); - nta_agent_get_params(nua->nua_nta, NTATAG_UDP_MTU_REF(udp_mtu), NTATAG_MAX_PROCEEDING_REF(max_proceeding), @@ -1590,6 +1597,8 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e, ? sip_##pref##_make(tmphome, (char *)nhp->nhp_##pref) \ : NULL)) + su_home_auto(tmphome, sizeof(tmphome)); + lst = tl_filtered_tlist (tmphome, tags, TAG_IF(has_from, SIPTAG_FROM(from)), @@ -1630,6 +1639,7 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e, TIF(NUTAG_REFER_WITH_ID, refer_with_id), TIF(NUTAG_SUBSTATE, substate), + TIF(NUTAG_SUB_EXPIRES, sub_expires), TIF(SIPTAG_SUPPORTED, supported), TIF_STR(SIPTAG_SUPPORTED_STR, supported), @@ -1658,11 +1668,18 @@ int nua_stack_get_params(nua_t *nua, nua_handle_t *nh, nua_event_t e, TIF(NUTAG_M_PARAMS, m_params), TIF(NUTAG_M_FEATURES, m_features), TIF(NUTAG_OUTBOUND, outbound), - TIF(NUTAG_DETECT_NETWORK_UPDATES, detect_network_updates), /* Skip user-agent-level parameters if parameters are for handle only */ TAG_IF(nh != dnh, TAG_NEXT(media_params)), + /* Include tag in the list returned to user + * if it has been earlier set (by user) */ +#define GIF(TAG, pref) \ + TAG_IF(ngp->ngp_set.ngp_##pref, TAG(ngp->ngp_##pref)) + + GIF(NUTAG_DETECT_NETWORK_UPDATES, detect_network_updates), + GIF(NUTAG_SHUTDOWN_EVENTS, shutdown_events), + NTATAG_CONTACT(m), #if HAVE_SOFIA_SMIME diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h index 70df87f6d8..a9993fe9a3 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_params.h @@ -107,6 +107,7 @@ typedef struct nua_handle_preferences /* Subscriber state, i.e. nua_substate_pending */ unsigned nhp_substate; + unsigned nhp_sub_expires; /* REGISTER keepalive intervals */ unsigned nhp_keepalive, nhp_keepalive_stream; @@ -127,9 +128,6 @@ typedef struct nua_handle_preferences /** Outbound OPTIONS */ char const *nhp_outbound; - /** Network detection: NONE, INFORMAL, TRY_FULL */ - int nhp_detect_network_updates; - sip_allow_t *nhp_appl_method; /** Initial route set */ @@ -137,6 +135,12 @@ typedef struct nua_handle_preferences union { struct { /* A bit for each feature set by application */ + /* NOTE: + Some compilers behave weird if there are bitfields + together with width > 32 + So there should be a padding field (unsigned:0;) + every 32 bits. + */ unsigned nhb_retry_count:1; unsigned nhb_max_subscriptions:1; @@ -166,13 +170,15 @@ typedef struct nua_handle_preferences unsigned nhb_refer_with_id:1; unsigned nhb_refer_expires:1; unsigned nhb_substate:1; + unsigned nhb_sub_expires:1; unsigned nhb_keepalive:1; unsigned nhb_keepalive_stream:1; unsigned nhb_registrar:1; unsigned nhb_allow:1; + unsigned :0; /* at most 32 bits before this point */ unsigned nhb_supported:1; - unsigned :0; /* at most 32 bits ... */ + unsigned nhb_allow_events:1; unsigned nhb_user_agent:1; unsigned nhb_organization:1; @@ -183,7 +189,6 @@ typedef struct nua_handle_preferences unsigned nhb_m_features:1; unsigned nhb_instance:1; unsigned nhb_outbound:1; - unsigned nhb_detect_network_updates:1; unsigned nhb_appl_method:1; unsigned nhb_initial_route:1; unsigned :0; @@ -194,6 +199,26 @@ typedef struct nua_handle_preferences #define nhp_set nhp_set_.set_bits +/** Global preferences for nua. */ +typedef struct { + /** Network detection: NONE, INFORMAL, TRY_FULL */ + signed int ngp_detect_network_updates:3; + /** Pass events during shutdown, too */ + int ngp_shutdown_events:1; + + unsigned :0; /* pad */ + union { struct { + /* A bit for each feature set by application */ + unsigned ngp_detect_network_updates:1; + unsigned ngp_shutdown_events:1; + unsigned :0; + } set_bits; + unsigned set_unsigned[2]; + } ngp_set_; +} nua_global_preferences_t; + +#define ngp_set ngp_set_.set_bits + #define DNHP_GET(dnhp, pref) ((dnhp)->nhp_##pref) #define NHP_GET(nhp, dnhp, pref) \ diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c index 80d3ad5fbe..9cd71c17bf 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_publish.c @@ -244,19 +244,21 @@ static int nua_publish_client_response(nua_client_request_t *cr, sip_t const *sip); static nua_client_methods_t const nua_publish_client_methods = { - SIP_METHOD_PUBLISH, - 0, - { + SIP_METHOD_PUBLISH, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 0, /* target refresh */ 0 }, - nua_publish_client_template, - nua_publish_client_init, - nua_publish_client_request, - nua_publish_client_check_restart, - nua_publish_client_response, - /* nua_publish_client_preliminary */ NULL + nua_publish_client_template, /* crm_template */ + nua_publish_client_init, /* crm_init */ + nua_publish_client_request, /* crm_send */ + nua_publish_client_check_restart, /* crm_check_restart */ + nua_publish_client_response, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; /**@internal Send PUBLISH. */ diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c index 58c76aea88..4afffb7a88 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_register.c @@ -570,18 +570,21 @@ static int nua_register_client_response(nua_client_request_t *cr, sip_t const *sip); static nua_client_methods_t const nua_register_client_methods = { - SIP_METHOD_REGISTER, - 0, - { + SIP_METHOD_REGISTER, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 1, /* in_dialog */ 0, /* target refresh */ 0 }, - nua_register_client_template, - nua_register_client_init, - nua_register_client_request, - nua_register_client_check_restart, - nua_register_client_response + nua_register_client_template, /* crm_template */ + nua_register_client_init, /* crm_init */ + nua_register_client_request, /* crm_send */ + nua_register_client_check_restart, /* crm_check_restart */ + nua_register_client_response, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; /**@internal Send REGISTER. */ diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c index f4a07b6a28..0e3292a152 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c @@ -191,7 +191,7 @@ static int nua_session_usage_shutdown(nua_owner_t *, nua_dialog_usage_t *); static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags); -static int nua_invite_client_deinit(nua_client_request_t *cr); +static int nua_invite_client_complete(nua_client_request_t *cr); static nua_usage_class const nua_session_usage[1] = { { @@ -238,7 +238,8 @@ void nua_session_usage_remove(nua_handle_t *nh, cr = du->du_cr; - if (cr && cr->cr_orq && cr->cr_status >= 200) { + if (cr && cr->cr_orq && cr->cr_status >= 200 && + cr->cr_method == sip_method_invite) { ss->ss_reporting = 1; nua_invite_client_ack(cr, NULL); ss->ss_reporting = 0; @@ -250,14 +251,17 @@ void nua_session_usage_remove(nua_handle_t *nh, if (cr->cr_method != sip_method_invite) continue; + if (cr == du->du_cr) continue; - nua_stack_event(nh->nh_nua, nh, - NULL, - cr->cr_event, - SIP_481_NO_TRANSACTION, - NULL); + if (cr->cr_status < 200) { + nua_stack_event(nh->nh_nua, nh, + NULL, + cr->cr_event, + SIP_481_NO_TRANSACTION, + NULL); + } nua_client_request_destroy(cr); @@ -542,21 +546,21 @@ static int nua_invite_client_report(nua_client_request_t *cr, tagi_t const *tags); nua_client_methods_t const nua_invite_client_methods = { - SIP_METHOD_INVITE, - 0, - { + SIP_METHOD_INVITE, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 1, /* in_dialog */ 1, /* target refresh */ 1 }, - NULL, - nua_invite_client_init, - nua_invite_client_request, - session_timer_check_restart, - nua_invite_client_response, - nua_invite_client_preliminary, - nua_invite_client_report, - nua_invite_client_deinit + NULL, /* crm_template */ + nua_invite_client_init, /* crm_init */ + nua_invite_client_request, /* crm_send */ + session_timer_check_restart, /* crm_check_restart */ + nua_invite_client_response, /* crm_recv */ + nua_invite_client_preliminary, /* crm_preliminary */ + nua_invite_client_report, /* crm_report */ + nua_invite_client_complete, /* crm_complete */ }; extern nua_client_methods_t const nua_bye_client_methods; @@ -604,10 +608,15 @@ static int nua_invite_client_init(nua_client_request_t *cr, if (!du) return -1; + ss = nua_dialog_usage_private(du); + + if (ss->ss_state >= nua_callstate_terminating) + return nua_client_return(cr, 900, "Session is terminating", msg); + if (nua_client_bind(cr, du) < 0) return nua_client_return(cr, 900, "INVITE already in progress", msg); - ss = nua_dialog_usage_private(du); + cr->cr_neutral = 0; session_timer_preferences(ss->ss_timer, sip, @@ -617,8 +626,6 @@ static int nua_invite_client_init(nua_client_request_t *cr, NH_PGET(nh, refresher), NH_PGET(nh, min_se)); - cr->cr_neutral = 0; - return 0; } @@ -635,6 +642,9 @@ static int nua_invite_client_request(nua_client_request_t *cr, if (du == NULL) /* Call terminated */ return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg); + if (ss->ss_state >= nua_callstate_terminating) + return nua_client_return(cr, 900, "Session is terminating", msg); + assert(ss); invite_timeout = NH_PGET(nh, invite_timeout); @@ -1047,7 +1057,7 @@ int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e, if (error < 0) { if (ss->ss_reason == NULL) ss->ss_reason = "SIP;cause=500;text=\"Internal Error\""; - ss->ss_reporting = 1; /* We report state here if BYE fails */ + ss->ss_reporting = 1; /* We report terminated state here if BYE fails */ error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL); ss->ss_reporting = 0; signal_call_state_change(nh, ss, 500, "Internal Error", @@ -1090,6 +1100,7 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags) char const *invite_branch; assert(cr->cr_orq); + assert(cr->cr_method == sip_method_invite); if (!ds->ds_leg) { @@ -1226,8 +1237,8 @@ int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags) return error; } -/** Deinitialize client request */ -static int nua_invite_client_deinit(nua_client_request_t *cr) +/** Complete client request */ +static int nua_invite_client_complete(nua_client_request_t *cr) { if (cr->cr_orq == NULL) /* Xyzzy */; @@ -1263,18 +1274,21 @@ static int nua_cancel_client_request(nua_client_request_t *cr, tagi_t const *tags); nua_client_methods_t const nua_cancel_client_methods = { - SIP_METHOD_CANCEL, - 0, - { + SIP_METHOD_CANCEL, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 1, /* target refresh */ 0 }, - NULL, - NULL, - nua_cancel_client_request, - /* nua_cancel_client_check_restart */ NULL, - /* nua_cancel_client_response */ NULL + NULL, /* crm_template */ + NULL, /* crm_init */ + nua_cancel_client_request, /* crm_send */ + NULL, /* crm_check_restart */ + NULL, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; int nua_stack_cancel(nua_t *nua, nua_handle_t *nh, nua_event_t e, @@ -1493,20 +1507,21 @@ static int nua_prack_client_report(nua_client_request_t *cr, tagi_t const *tags); nua_client_methods_t const nua_prack_client_methods = { - SIP_METHOD_PRACK, - 0, - { + SIP_METHOD_PRACK, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 1, /* target refresh */ 0 }, - NULL, - nua_prack_client_init, - nua_prack_client_request, - /* nua_prack_client_check_restart */ NULL, - nua_prack_client_response, - NULL, - nua_prack_client_report + NULL, /* crm_template */ + nua_prack_client_init, /* crm_init */ + nua_prack_client_request, /* crm_send */ + NULL, /* crm_check_restart */ + nua_prack_client_response, /* crm_recv */ + NULL, /* crm_preliminary */ + nua_prack_client_report, /* crm_report */ + NULL, /* crm_complete */ }; int nua_stack_prack(nua_t *nua, nua_handle_t *nh, nua_event_t e, @@ -1542,6 +1557,8 @@ static int nua_prack_client_request(nua_client_request_t *cr, if (du == NULL) /* Call terminated */ return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg); assert(ss); + if (ss->ss_state >= nua_callstate_terminating) + return nua_client_return(cr, 900, "Session is terminating", msg); cri = du->du_cr; @@ -1820,6 +1837,7 @@ nua_invite_server_init(nua_server_request_t *sr) { nua_handle_t *nh = sr->sr_owner; nua_t *nua = nh->nh_nua; + nua_session_usage_t *ss; sr->sr_neutral = 1; @@ -1858,6 +1876,15 @@ nua_invite_server_init(nua_server_request_t *sr) /* Glare - RFC 3261 14.2 and RFC 3311 section 5.2 */ return SR_STATUS1(sr, SIP_491_REQUEST_PENDING); } + + ss = nua_dialog_usage_private(sr->sr_usage); + + if (ss->ss_state < nua_callstate_completed && + ss->ss_state != nua_callstate_init) { + /* We should never trigger this, + but better not to assert() on network input */ + return nua_server_retry_after(sr, 500, "Overlapping Requests 2", 0, 10); + } } sr->sr_neutral = 0; @@ -1887,6 +1914,11 @@ nua_session_server_init(nua_server_request_t *sr) /* UPDATE/PRACK sent within an existing dialog? */ return SR_STATUS(sr, 481, "Call Does Not Exist"); } + else if (sr->sr_usage) { + nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage); + if (ss->ss_state >= nua_callstate_terminating) + return SR_STATUS(sr, 481, "Call is being terminated"); + } if (nh->nh_soa) { sip_accept_t *a = nua->nua_invite_accept; @@ -1972,8 +2004,10 @@ int nua_invite_server_preprocess(nua_server_request_t *sr) session_timer_store(ss->ss_timer, request); - if (!(ss->ss_state >= nua_callstate_ready || ss->ss_state == nua_callstate_init)) - return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR); +#if 0 /* The glare and overlap tests should take care of this. */ + assert(ss->ss_state >= nua_callstate_completed || + ss->ss_state == nua_callstate_init); +#endif if (NH_PGET(nh, auto_answer) || /* Auto-answer to re-INVITE unless auto_answer is set to 0 on handle */ @@ -2319,7 +2353,7 @@ int process_ack(nua_server_request_t *sr, nua_stack_event(nh->nh_nua, nh, NULL, nua_i_media_error, status, phrase, NULL); - ss->ss_reporting = 1; /* We report state here if BYE fails */ + ss->ss_reporting = 1; /* We report terminated state here if BYE fails */ error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL); ss->ss_reporting = 0; @@ -2416,7 +2450,7 @@ int process_timeout(nua_server_request_t *sr, /* send BYE, too, if 200 OK (or 183 to re-INVITE) timeouts */ ss->ss_reason = reason; - ss->ss_reporting = 1; /* We report state here if BYE fails */ + ss->ss_reporting = 1; /* We report terminated state here if BYE fails */ error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL); ss->ss_reporting = 0; @@ -2787,18 +2821,21 @@ static int nua_info_client_request(nua_client_request_t *cr, tagi_t const *tags); nua_client_methods_t const nua_info_client_methods = { - SIP_METHOD_INFO, - 0, - { + SIP_METHOD_INFO, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 1, /* target refresh */ 0 }, - /*nua_info_client_template*/ NULL, - nua_info_client_init, - nua_info_client_request, - /*nua_info_client_check_restart*/ NULL, - /*nua_info_client_response*/ NULL + NULL, /* crm_template */ + nua_info_client_init, /* crm_init */ + nua_info_client_request, /* crm_send */ + NULL, /* crm_check_restart */ + NULL, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; int @@ -2934,20 +2971,21 @@ static int nua_update_client_report(nua_client_request_t *cr, tagi_t const *tags); nua_client_methods_t const nua_update_client_methods = { - SIP_METHOD_UPDATE, - 0, /* size of private data */ - { + SIP_METHOD_UPDATE, /* crm_method, crm_method_name */ + 0, /* crm_extrasize of private data */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 1, /* target refresh */ 1 }, - NULL, - nua_update_client_init, - nua_update_client_request, - session_timer_check_restart, - nua_update_client_response, - NULL, - nua_update_client_report + NULL, /* crm_template */ + nua_update_client_init, /* crm_init */ + nua_update_client_request, /* crm_send */ + session_timer_check_restart, /* crm_check_restart */ + nua_update_client_response, /* crm_recv */ + NULL, /* crm_preliminary */ + nua_update_client_report, /* crm_report */ + NULL, /* crm_complete */ }; int nua_stack_update(nua_t *nua, nua_handle_t *nh, nua_event_t e, @@ -2982,6 +3020,8 @@ static int nua_update_client_request(nua_client_request_t *cr, if (du == NULL) /* Call terminated */ return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg); assert(ss); + if (ss->ss_state >= nua_callstate_terminating) + return nua_client_return(cr, 900, "Session is terminating", msg); cri = du->du_cr; @@ -3397,20 +3437,21 @@ static int nua_bye_client_report(nua_client_request_t *cr, tagi_t const *tags); nua_client_methods_t const nua_bye_client_methods = { - SIP_METHOD_BYE, - 0, - { + SIP_METHOD_BYE, /* crm_method, crm_method_name */ + 0, /* crm_extrasize */ + { /* crm_flags */ /* create_dialog */ 0, /* in_dialog */ 1, /* target refresh */ 0 }, - NULL, - nua_bye_client_init, - nua_bye_client_request, - /*nua_bye_client_check_restart*/ NULL, - /*nua_bye_client_response*/ NULL, - /*nua_bye_client_preliminary*/ NULL, - nua_bye_client_report + NULL, /* crm_template */ + nua_bye_client_init, /* crm_init */ + nua_bye_client_request, /* crm_send */ + NULL, /* crm_check_restart */ + NULL, /* crm_recv */ + NULL, /* crm_preliminary */ + nua_bye_client_report, /* crm_report */ + NULL, /* crm_complete */ }; int @@ -3443,7 +3484,9 @@ static int nua_bye_client_init(nua_client_request_t *cr, if (nh->nh_soa) soa_terminate(nh->nh_soa, 0); - cr->cr_usage = du; + + du->du_cr = NULL; + nua_client_bind(cr, du); return 0; } @@ -3515,17 +3558,23 @@ static int nua_bye_client_report(nua_client_request_t *cr, else { nua_session_usage_t *ss = nua_dialog_usage_private(du); + if (ss->ss_reporting) { + return 1; /* Somebody else's problem */ + } + else if (cr->cr_waiting) { + return 1; /* Application problem */ + } + signal_call_state_change(nh, ss, status, "to BYE", nua_callstate_terminated); - if (ss && !ss->ss_reporting) { - if (du->du_cr == NULL || - !nua_client_is_queued(du->du_cr) || - du->du_cr->cr_status >= 200) { - /* INVITE is completed, we can zap the session... */; - cr->cr_usage = NULL; - nua_session_usage_destroy(nh, ss); - } + if (du && + (du->du_cr == NULL || + !nua_client_is_queued(du->du_cr) || + du->du_cr->cr_status >= 200)) { + /* INVITE is completed, we can zap the session... */; + cr->cr_usage = NULL; + nua_session_usage_destroy(nh, ss); } } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c index 226fe5e398..bc5e626277 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c @@ -209,7 +209,7 @@ int nua_stack_init(su_root_t *root, nua_t *nua) if (nua_stack_set_from(nua, 1, nua->nua_args) < 0) return -1; - if (NHP_ISSET(dnh->nh_prefs, detect_network_updates)) + if (nua->nua_prefs->ngp_detect_network_updates) nua_stack_launch_network_change_detector(nua); nua_stack_timer(nua, nua->nua_timer, NULL); @@ -293,7 +293,8 @@ int nua_stack_event(nua_t *nua, nua_handle_t *nh, msg_t *msg, if ((event > nua_r_authenticate && event <= nua_r_ack) || event < nua_i_error || (nh && !nh->nh_valid) - ) { + || (nua->nua_shutdown && event != nua_r_shutdown && + !nua->nua_prefs->ngp_shutdown_events)) { if (msg) msg_destroy(msg); return event; @@ -2075,6 +2076,13 @@ nua_client_request_t *nua_client_request_remove(nua_client_request_t *cr) return cr; } +void nua_client_request_complete(nua_client_request_t *cr) +{ + nua_client_request_remove(cr); + if (cr && cr->cr_methods->crm_complete) + cr->cr_methods->crm_complete(cr); +} + void nua_client_request_destroy(nua_client_request_t *cr) { nua_handle_t *nh; @@ -2082,14 +2090,12 @@ void nua_client_request_destroy(nua_client_request_t *cr) if (cr == NULL) return; - if (cr->cr_methods->crm_deinit) - cr->cr_methods->crm_deinit(cr); + nua_client_request_complete(cr); nh = cr->cr_owner; nua_destroy_signal(cr->cr_signal); - nua_client_request_remove(cr); nua_client_bind(cr, NULL); if (cr->cr_msg) @@ -2098,7 +2104,6 @@ void nua_client_request_destroy(nua_client_request_t *cr) if (cr->cr_orq) nta_outgoing_destroy(cr->cr_orq); - cr->cr_orq = NULL; if (cr->cr_timer) diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h index de99527258..cf6f9c4595 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.h @@ -217,28 +217,31 @@ struct nua_s { su_clone_r nua_clone; su_task_r nua_client; - su_network_changed_t *nua_nw_changed; - nua_callback_f nua_callback; nua_magic_t *nua_magic; nua_event_frame_t *nua_current; nua_saved_event_t nua_signal[1]; + /**< Used by stop-and-wait args calls */ + tagi_t const *nua_args; + /* Engine state flags */ + sip_time_t nua_shutdown; + unsigned nua_shutdown_started:1; /**< Shutdown initiated */ unsigned nua_shutdown_final:1; /**< Shutdown is complete */ unsigned nua_from_is_set; unsigned :0; - /**< Used by stop-and-wait args calls */ - tagi_t const *nua_args; - /**< Local SIP address. Contents are kept around for ever. */ - sip_from_t nua_from[1]; + sip_from_t nua_from[1]; + + /* ---------------------------------------------------------------------- */ /* Protocol (server) side */ + su_network_changed_t *nua_nw_changed; nua_registration_t *nua_registrations; /**< Active registrations */ @@ -250,21 +253,8 @@ struct nua_s { nta_agent_t *nua_nta; su_timer_t *nua_timer; - void *nua_sip_parser; - - sip_time_t nua_shutdown; - - /* Route */ - sip_service_route_t *nua_service_route; - /* User-agent parameters */ - unsigned nua_media_enable:1; - - unsigned :0; - -#if HAVE_SMIME /* Start NRC Boston */ - sm_object_t *sm; -#endif /* End NRC Boston */ + nua_global_preferences_t nua_prefs[1]; nua_handle_t *nua_handles; nua_handle_t **nua_handles_tail; diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c index 30ce9bd3c1..6207e73581 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c @@ -183,18 +183,21 @@ static int nua_subscribe_client_response(nua_client_request_t *cr, sip_t const *sip); static nua_client_methods_t const nua_subscribe_client_methods = { - SIP_METHOD_SUBSCRIBE, - 0, - { + SIP_METHOD_SUBSCRIBE, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 1, /* in_dialog */ 1, /* target refresh */ 1 }, - NULL, - nua_subscribe_client_init, - nua_subscribe_client_request, - /* nua_subscribe_client_check_restart */ NULL, - nua_subscribe_client_response + NULL, /* crm_template */ + nua_subscribe_client_init, /* crm_init */ + nua_subscribe_client_request, /* crm_send */ + NULL, /* crm_check_restart */ + nua_subscribe_client_response, /* crm_recv */ + NULL, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; int @@ -368,6 +371,9 @@ static int nua_subscribe_client_response(nua_client_request_t *cr, else delta = 0; + if (delta > eu->eu_expires) + delta = eu->eu_expires; + if (win_messenger_enable && !nua_dialog_is_established(nh->nh_ds)) { /* Notify from messanger does not match with dialog tag */ nh->nh_ds->ds_remote_tag = su_strdup(nh->nh_home, ""); @@ -647,8 +653,6 @@ int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags) if (substate == nua_substate_active || substate == nua_substate_pending) { if (subs && subs->ss_expires) delta = strtoul(subs->ss_expires, NULL, 10); - else - delta = eu->eu_expires; } else if (substate == nua_substate_embryonic) { if (subs && subs->ss_reason) { @@ -686,7 +690,8 @@ int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags) nua_dialog_usage_set_refresh_range(du, retry, retry + 5); } else { - nua_dialog_usage_set_refresh(du, delta); + if (delta < SIP_TIME_MAX) + nua_dialog_usage_set_refresh(du, delta); } return retval; @@ -777,20 +782,21 @@ static int nua_refer_client_response(nua_client_request_t *cr, sip_t const *sip); static nua_client_methods_t const nua_refer_client_methods = { - SIP_METHOD_REFER, - 0, - { + SIP_METHOD_REFER, /* crm_method, crm_method_name */ + 0, /* crm_extra */ + { /* crm_flags */ /* create_dialog */ 1, /* in_dialog */ 1, /* target refresh */ 1 }, - /*nua_refer_client_template*/ NULL, - nua_refer_client_init, - nua_refer_client_request, - /* nua_refer_client_check_restart */ NULL, - nua_refer_client_response, - nua_refer_client_response, /* Preliminary */ - NULL + NULL, /* crm_template */ + nua_refer_client_init, /* crm_init */ + nua_refer_client_request, /* crm_send */ + NULL, /* crm_check_restart */ + nua_refer_client_response, /* crm_recv */ + nua_refer_client_response, /* crm_preliminary */ + NULL, /* crm_report */ + NULL, /* crm_complete */ }; int diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c index 88686a7454..cf40cd890f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_tag.c @@ -227,21 +227,22 @@ * - NUTAG_ALLOW_EVENTS(), SIPTAG_ALLOW_EVENTS(), and * SIPTAG_ALLOW_EVENTS_STR() * - NUTAG_MAX_SUBSCRIPTIONS() - * - NUTAG_SUBSTATE() + * - NUTAG_SUBSTATE(), NUTAG_SUB_EXPIRES() * @par Specifications * - @RFC3265 * * @par SIP Event Subscriber * - nua_subscribe(), #nua_r_subscribe, #nua_i_notify, NUTAG_SUBSTATE(), - * SIPTAG_EVENT(), SIPTAG_EXPIRES(), + * SIPTAG_EVENT(), SIPTAG_EXPIRES() * - nua_unsubscribe(), #nua_r_unsubscribe() * @par Specifications * - @RFC3265 * * @par SIP Event Notifier * - #nua_i_subscribe(), nua_notify(), #nua_r_notify, - * NUTAG_SUBSTATE(), SIPTAG_EVENT() + * NUTAG_SUBSTATE(), NUTAG_SUB_EXPIRES(), SIPTAG_EVENT() * Settings: + * - NUTAG_SUB_EXPIRES() * - NUTAG_ALLOW_EVENTS(), SIPTAG_ALLOW_EVENTS(), and * SIPTAG_ALLOW_EVENTS_STR() * - NUTAG_ALLOW("SUBSCRIBE"), NUTAG_APPL_METHOD("SUBSCRIBE") @@ -267,6 +268,8 @@ * Settings: * - NUTAG_ALLOW(x), NUTAG_APPL_METHOD(x) * + * @par Server Shutdown + * - nua_shutdown(), NUTAG_SHUTDOWN_EVENTS(), nua_destroy(). */ /* @par S/MIME @@ -767,14 +770,14 @@ tag_typedef_t nutag_answer_sent = BOOLTAG_TYPEDEF(answer_sent); * * @par Used with * - with nua_create(), nua_set_params(), nua_get_params(), - * nua_handle(), nua_set_hparams(), nua_get_hparams(), and - * nua_notifier() to change the default subscription state returned by - * the intenal event server + * nua_handle(), nua_set_hparams(), nua_get_hparams(), and + * nua_notifier() to change the default subscription state returned by + * the internal event server * - with nua_notify() and nua_respond() to SUBSCRIBE to determine the - * subscription state (if application include @SubscriptionState - * header in the tag list, the NUTAG_SUBSTATE() value is ignored) + * subscription state (if application include @SubscriptionState + * header in the tag list, the NUTAG_SUBSTATE() value is ignored) * - with #nua_r_subscribe, #nua_i_notify, #nua_i_subscribe, and #nua_r_notify - * to indicate the current subscription state + * to indicate the current subscription state * * @par Parameter type * int @@ -806,6 +809,39 @@ tag_typedef_t nutag_substate = INTTAG_TYPEDEF(substate); */ +/**@def NUTAG_SUB_EXPIRES() + * + * Default expiration time of subscriptions. + * + * @par Used with + * - with nua_create(), nua_set_params(), nua_get_params(), nua_handle(), + * nua_set_hparams(), nua_get_hparams(), nua_respond(), nua_notify(), and + * nua_notifier() to change the default expiration time of subscriptions + * + * @par Parameter type + * unsigned int + * + * @par Values + * - default expiration time in seconds + * + * Note that the expires parameter in @SubscriptionState or @Expires header + * in the nua_response() to the SUBSCRIBE overrides the default subscription + * expiration specified by NUTAG_SUB_EXPIRES(). + * + * @sa @RFC3265, NUTAG_REFER_EXPIRES(), @Expires, SIPTAG_EXPIRES(), + * SIPTAG_EXPIRES_STR(), @SubscriptionState, nua_respond(), nua_notifier(), + * #nua_r_subscribe, #nua_i_subscribe, #nua_r_refer, #nua_r_notify, + * #nua_i_notify. + * + * Corresponding tag taking reference parameter is NUTAG_SUB_EXPIRES_REF(). + */ +tag_typedef_t nutag_sub_expires = UINTTAG_TYPEDEF(substate); + +/**@def NUTAG_SUB_EXPIRES_REF(x) + * Reference tag for NUTAG_SUB_EXPIRES(). + */ + + /**@def NUTAG_NEWSUB() * * Send unsolicited NOTIFY request. @@ -1075,17 +1111,17 @@ tag_typedef_t nutag_update_refresh = BOOLTAG_TYPEDEF(update_refresh); * REFER. * * @par Used with - * nua_set_params() \n - * nua_get_params() \n - * nua_set_hparams() \n - * nua_get_hparams() \n + * nua_handle(), nua_respond() \n + * nua_set_params() or nua_set_hparams() \n + * nua_get_params() or nua_get_hparams() * * @par Parameter type * unsigned int * * @par Values - * @c 0 disable \n - * @c >0 interval in seconds + * - default interval in seconds + * + * @sa NUTAG_SUB_EXPIRES() * * Corresponding tag taking reference parameter is NUTAG_REFER_EXPIRES_REF(). */ @@ -1570,6 +1606,9 @@ extern msg_hclass_t sip_route_class[]; * NUTAG_INITIAL_ROUTE_STR() tags, the route set is constructed from them * all. * + * The initial route is inserted into request message before the route + * entries set with SIPTAG_ROUTE() or SIPTAG_ROUTE_STR(). + * * @par Used with * nua_set_params() \n * nua_set_hparams() \n @@ -2736,6 +2775,39 @@ tag_typedef_t nutag_dialog = UINTTAG_TYPEDEF(dialog); * Reference tag for NUTAG_SIP_PARSER(). */ + +/**@def NUTAG_SHUTDOWN_EVENTS(x) + * + * Allow passing of normal events when stack is being shut down. + * + * By default, only #nua_r_shutdown events are passed to application after + * calling nua_shutdown(). If application is interested in nua events during + * shutdown, it should give NUTAG_SHUTDOWN_EVENTS(1) to nua_create() or + * nua_set_params() called before nua_shutdown(). + * + * @par Used with + * nua_create(), nua_set_params(). + * + * @par Parameter type + * int (boolean) + * + * @par Values + * @c 0 False \n + * @c !=0 True + * + * Corresponding tag taking reference parameter is NUTAG_SHUTDOWN_EVENTS_REF(). + * + * @sa nua_shutdown(), nua_destroy(). + * + * @NEW_1_12_9. + */ +tag_typedef_t nutag_shutdown_events = BOOLTAG_TYPEDEF(shutdown_events); + +/**@def NUTAG_SHUTDOWN_EVENTS_REF(x) + * Reference tag for NUTAG_SHUTDOWN_EVENTS(). + */ + + /* ---------------------------------------------------------------------- */ tag_typedef_t nutag_soa_session = PTRTAG_TYPEDEF(soa_session); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h b/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h index 01d825e8a5..b8d71c7ca8 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/sofia-sip/nua_tag.h @@ -497,12 +497,16 @@ SOFIAPUBFUN char const *nua_substate_name(enum nua_substate substate); /** Convert string to enum nua_substate. @NEW_1_12_5. */ SOFIAPUBFUN enum nua_substate nua_substate_make(char const *sip_substate); +#define NUTAG_SUB_EXPIRES(x) nutag_sub_expires, tag_uint_v(x) +SOFIAPUBVAR tag_typedef_t nutag_sub_expires; +#define NUTAG_SUB_EXPIRES_REF(x) nutag_sub_expires_ref, tag_uint_vr(&(x)) +SOFIAPUBVAR tag_typedef_t nutag_sub_expires_ref; + #define NUTAG_NEWSUB(x) nutag_newsub, tag_bool_v(x) SOFIAPUBVAR tag_typedef_t nutag_newsub; #define NUTAG_NEWSUB_REF(x) nutag_newsub_ref, tag_bool_vr(&(x)) SOFIAPUBVAR tag_typedef_t nutag_newsub_ref; - #define NUTAG_REFER_EXPIRES(x) nutag_refer_expires, tag_uint_v((x)) SOFIAPUBVAR tag_typedef_t nutag_refer_expires; #define NUTAG_REFER_EXPIRES_REF(x) nutag_refer_expires_ref, tag_uint_vr((&(x))) @@ -569,6 +573,13 @@ SOFIAPUBVAR tag_typedef_t nutag_detect_network_updates; nutag_detect_network_updates_ref, tag_int_vr(&(x)) SOFIAPUBVAR tag_typedef_t nutag_detect_network_updates_ref; +#define NUTAG_SHUTDOWN_EVENTS(x) \ + nutag_shutdown_events, tag_bool_v(x) +SOFIAPUBVAR tag_typedef_t nutag_shutdown_events; +#define NUTAG_SHUTDOWN_EVENTS_REF(x) \ + nutag_shutdown_events_ref, tag_bool_vr(&(x)) +SOFIAPUBVAR tag_typedef_t nutag_shutdown_events_ref; + /* Pass nua handle as tagged argument */ #if SU_INLINE_TAG_CAST su_inline tag_value_t nutag_handle_v(nua_handle_t *v) { return (tag_value_t)v; } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c index 5fd2f0c46f..058420fe27 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_100rel.c @@ -726,9 +726,8 @@ int test_183rel(struct context *ctx) if (e->data->e_event == nua_r_bye) { TEST_E(e->data->e_event, nua_r_bye); TEST(e->data->e_status, 200); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); - TEST(callstate(e->data->e_tags), nua_callstate_terminated); bye = 0; + break; } else if (e->data->e_event == nua_r_invite) { TEST_E(e->data->e_event, nua_r_invite); @@ -741,7 +740,10 @@ int test_183rel(struct context *ctx) cancel = 0; } } + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_state); + TEST(callstate(e->data->e_tags), nua_callstate_terminated); } + TEST_1(!e->next); free_events_in_list(ctx, a->events); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c index 107247c8cc..69f91dffa8 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_cancel_bye.c @@ -1133,7 +1133,7 @@ int test_bye_before_ack(struct context *ctx) Server transitions: INIT -(S1)-> RECEIVED: nua_i_invite, nua_i_state RECEIVED -(S2a)-> EARLY: nua_respond(180), nua_i_state - EARLY -(S6b)--> TERMINATED: nua_i_cancel, nua_i_state + EARLY -(S6b)--> TERMINATED: nua_i_bye, nua_i_state */ TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_invite); TEST(e->data->e_status, 100); @@ -1423,6 +1423,79 @@ int reject_reinvite_401(CONDITION_PARAMS) return 0; } +int test_bye_with_407(struct context *ctx) +{ + BEGIN(); + struct endpoint *a = &ctx->a, *c = &ctx->c; + struct call *a_call = a->call, *c_call = c->call; + struct event *e; + + a_call->sdp = "m=audio 5008 RTP/AVP 8"; + c_call->sdp = "m=audio 5010 RTP/AVP 0 8"; + +/* BYE after receiving 401 + + A C + |-------INVITE------>| + |<----100 Trying-----| + | | + |<----180 Ringing----| + |<-------200---------| + |--------ACK-------->| + | | + | |<----BYE----| + | |-----407--->| + |<-------BYE---------| + |--------200-------->| + | | +*/ + if (print_headings) + printf("TEST NUA-6.4.5: BYE with 407\n"); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(c->to), TAG_END())); + + INVITE(a, a_call, a_call->nh, + TAG_IF(!ctx->proxy_tests, NUTAG_URL(c->contact->m_url)), + SIPTAG_SUBJECT_STR("NUA-6.4.2"), + SOATAG_USER_SDP_STR(a_call->sdp), + NUTAG_AUTOANSWER(0), + NUTAG_APPL_METHOD("UPDATE"), + TAG_END()); + + run_abc_until(ctx, -1, until_ready, -1, NULL, -1, accept_call); + + free_events_in_list(ctx, a->events); + + TEST_1(e = c->events->head); TEST_E(e->data->e_event, nua_i_invite); + free_events_in_list(ctx, c->events); + + BYE(c, c_call, c_call->nh, + TAG_END()); + run_c_until(ctx, -1, save_until_final_response); + + TEST_1(nua_handle_has_active_call(a_call->nh)); + TEST_1(nua_handle_has_active_call(c_call->nh)); + + free_events_in_list(ctx, a->events); + free_events_in_list(ctx, c->events); + + AUTHENTICATE(c, c_call, c_call->nh, + NUTAG_AUTH("Digest:\"test-proxy\":charlie:secret"), TAG_END()); + + run_abc_until(ctx, -1, until_terminated, -1, NULL, -1, until_terminated); + + free_events_in_list(ctx, a->events); + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + + free_events_in_list(ctx, c->events); + nua_handle_destroy(c_call->nh), c_call->nh = NULL; + + if (print_headings) + printf("TEST NUA-6.4.5: PASSED\n"); + + END(); +} + int test_bye_to_invalid_contact(struct context *ctx) { BEGIN(); @@ -1576,6 +1649,7 @@ int test_bye_to_invalid_contact(struct context *ctx) int test_early_bye(struct context *ctx) { return + test_bye_with_407(ctx) || test_bye_before_200(ctx) || test_bye_before_ack(ctx) || test_bye_after_receiving_401(ctx) || diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c index b12b75f533..8b10b1aa23 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_init.c @@ -151,8 +151,11 @@ int test_nua_init(struct context *ctx, AUTHTAG_ALGORITHM("md5"), AUTHTAG_NEXT_EXPIRES(60), AUTHTAG_MAX_NCOUNT(1), + AUTHTAG_ALLOW("ACK, CANCEL"), TAG_END()); + test_proxy_domain_set_record_route(ctx->c.domain, 1); + ctx->proxy_tests = 1; } diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c index 6a9f2e1fb7..846d104057 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_nua_params.c @@ -203,6 +203,7 @@ int test_nua_params(struct context *ctx) NUTAG_REFER_EXPIRES(333), NUTAG_REFER_WITH_ID(0), NUTAG_SUBSTATE(nua_substate_pending), + NUTAG_SUB_EXPIRES(3700), NUTAG_KEEPALIVE(66), NUTAG_KEEPALIVE_STREAM(33), @@ -284,6 +285,7 @@ int test_nua_params(struct context *ctx) int auth_cache = -1; unsigned refer_expires = (unsigned)-1; int refer_with_id = -1; + unsigned sub_expires = (unsigned)-1; int substate = -1; sip_allow_t const *allow = NONE; @@ -352,6 +354,7 @@ int test_nua_params(struct context *ctx) NUTAG_REFER_EXPIRES_REF(refer_expires), NUTAG_REFER_WITH_ID_REF(refer_with_id), NUTAG_SUBSTATE_REF(substate), + NUTAG_SUB_EXPIRES_REF(sub_expires), SIPTAG_SUPPORTED_REF(supported), SIPTAG_SUPPORTED_STR_REF(supported_str), @@ -382,7 +385,7 @@ int test_nua_params(struct context *ctx) NUTAG_INSTANCE_REF(instance), TAG_END()); - TEST(n, 50); + TEST(n, 51); TEST_S(sip_header_as_string(tmphome, (void *)from), Alice); TEST_S(from_str, Alice); @@ -416,6 +419,7 @@ int test_nua_params(struct context *ctx) TEST(refer_expires, 333); TEST(refer_with_id, 0); TEST(substate, nua_substate_pending); + TEST(sub_expires, 3700); TEST_S(sip_header_as_string(tmphome, (void *)allow), "OPTIONS, INFO, ACK"); TEST_S(allow_str, "OPTIONS, INFO, ACK"); @@ -492,6 +496,7 @@ int test_nua_params(struct context *ctx) unsigned refer_expires = (unsigned)-1; int refer_with_id = -1; int substate = -1; + unsigned sub_expires = (unsigned)-1; sip_allow_t const *allow = NONE; char const *allow_str = "NONE"; @@ -552,6 +557,7 @@ int test_nua_params(struct context *ctx) NUTAG_PATH_ENABLE_REF(path_enable), NUTAG_AUTH_CACHE_REF(auth_cache), NUTAG_SUBSTATE_REF(substate), + NUTAG_SUB_EXPIRES_REF(sub_expires), SIPTAG_SUPPORTED_REF(supported), SIPTAG_SUPPORTED_STR_REF(supported_str), @@ -607,6 +613,7 @@ int test_nua_params(struct context *ctx) TEST(refer_expires, (unsigned)-1); TEST(refer_with_id, -1); TEST(substate, -1); + TEST(sub_expires, -1); TEST_P(allow, NONE); TEST_S(allow_str, "NONE"); diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c index afc2819a45..1543811c2a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.c @@ -135,6 +135,7 @@ struct domain { sip_time_t min_expires, expires, max_expires; int outbound_tcp; /**< Use inbound TCP connection as outbound */ char const *authorize; /**< Authorization realm to use */ + int record_route; } prefs; tagi_t *tags; @@ -478,6 +479,23 @@ void test_proxy_domain_get_outbound(struct domain *d, } } +void test_proxy_domain_set_record_route(struct domain *d, + int use_record_route) +{ + if (d) { + d->prefs.record_route = use_record_route; + } +} + +void test_proxy_domain_get_record_route(struct domain *d, + int *return_use_record_route) +{ + if (d) { + if (return_use_record_route) + *return_use_record_route = d->prefs.record_route; + } +} + int test_proxy_domain_set_authorize(struct domain *d, char const *realm) { @@ -743,6 +761,9 @@ static int proxy_tr_with(struct proxy *proxy, if (t->method != sip_method_ack && t->method != sip_method_cancel) nta_incoming_bind(irq, proxy_ack_cancel, t); + if (domain && domain->prefs.record_route) + t->rr = 1; + if (process(t) < 200) return 0; @@ -818,6 +839,9 @@ static int originating_transaction(struct proxy_tr *t) t->use_auth = 407; } + if (o && o->prefs.record_route) + t->rr = 1; + return 0; } @@ -841,7 +865,6 @@ static int validate_transaction(struct proxy_tr *t) url_cmp(t->proxy->rr_uri, t->sip->sip_route->r_url) == 0)) { sip_route_remove(t->msg, t->sip); /* add record-route also to the forwarded request */ - t->rr = 1; } if (t->use_auth) @@ -947,12 +970,17 @@ static int target_transaction(struct proxy_tr *t, msg_header_insert(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)c->rq); - if (t->rr && 0) { + if (t->rr) { sip_record_route_t rr[1]; - *sip_record_route_init(rr)->r_url = *t->proxy->rr_uri; - - msg_header_add_dup(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)rr); + if (t->proxy->rr_uri) { + *sip_record_route_init(rr)->r_url = *t->proxy->rr_uri; + msg_header_add_dup(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)rr); + } + else if (t->proxy->lr) { + *sip_record_route_init(rr)->r_url = *t->proxy->lr->r_url; + msg_header_add_dup(c->msg, (msg_pub_t *)c->sip, (msg_header_t *)rr); + } } if (c->rq) diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h index d8f5c0933c..40dc50b2f1 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_proxy.h @@ -73,6 +73,11 @@ void test_proxy_domain_set_outbound(struct domain *d, void test_proxy_domain_get_outbound(struct domain *d, int *return_use_outbound); +void test_proxy_domain_set_record_route(struct domain *d, + int use_record_route); +void test_proxy_domain_get_record_route(struct domain *d, + int *return_use_record_route); + int test_proxy_close_tports(struct proxy *p); SOFIA_END_DECLS diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c index 020b1f2c4e..4997908f89 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c @@ -45,6 +45,9 @@ extern int accept_request(CONDITION_PARAMS); +int save_until_nth_final_response(CONDITION_PARAMS); +int accept_n_notifys(CONDITION_PARAMS); + int test_message(struct context *ctx) { BEGIN(); @@ -564,6 +567,47 @@ static char const presence_closed[] = "\n"; +int accept_and_notify_twice(CONDITION_PARAMS) +{ + msg_t *with = nua_current_request(nua); + + if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))) + return 0; + + save_event_in_list(ctx, event, ep, call); + + switch (event) { + case nua_i_subscribe: + if (status < 200) { + NOTIFY(ep, call, nh, + SIPTAG_EVENT(sip->sip_event), + SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"), + SIPTAG_PAYLOAD_STR(presence_closed), + NUTAG_SUBSTATE(nua_substate_pending), + TAG_END()); + NOTIFY(ep, call, nh, + SIPTAG_EVENT(sip->sip_event), + SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"), + SIPTAG_PAYLOAD_STR(presence_open), + NUTAG_SUBSTATE(nua_substate_active), + TAG_END()); + RESPOND(ep, call, nh, SIP_202_ACCEPTED, + NUTAG_WITH(with), + SIPTAG_EXPIRES_STR("360"), + TAG_END()); + } + return 0; + + case nua_r_notify: + return status >= 200 && + tl_find(tags, nutag_substate)->t_value == nua_substate_active; + + default: + return 0; + } +} + + int accept_and_notify(CONDITION_PARAMS) { msg_t *with = nua_current_request(nua); @@ -580,12 +624,14 @@ int accept_and_notify(CONDITION_PARAMS) NUTAG_WITH(with), SIPTAG_EXPIRES_STR("360"), TAG_END()); + NOTIFY(ep, call, nh, SIPTAG_EVENT(sip->sip_event), SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"), SIPTAG_PAYLOAD_STR(presence_closed), NUTAG_SUBSTATE(nua_substate_pending), TAG_END()); + } return 0; @@ -597,6 +643,31 @@ int accept_and_notify(CONDITION_PARAMS) } } +int save_and_notify(CONDITION_PARAMS) +{ + if (!(check_handle(ep, call, nh, SIP_500_INTERNAL_SERVER_ERROR))) + return 0; + + save_event_in_list(ctx, event, ep, call); + + switch (event) { + case nua_i_subscribe: + NOTIFY(ep, call, nh, + SIPTAG_EVENT(sip->sip_event), + SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"), + SIPTAG_PAYLOAD_STR(presence_closed), + NUTAG_SUBSTATE(nua_substate_active), + TAG_END()); + return 0; + + case nua_r_notify: + return status >= 200; + + default: + return 0; + } +} + extern int save_until_notified_and_responded(CONDITION_PARAMS); extern int save_until_notified(CONDITION_PARAMS); @@ -606,7 +677,7 @@ int test_subscribe_notify(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *en1, *en2, *es; sip_t const *sip; tagi_t const *n_tags, *r_tags; @@ -620,44 +691,47 @@ int test_subscribe_notify(struct context *ctx) SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url), SIPTAG_EVENT_STR("presence"), + SIPTAG_EXPIRES_STR("333"), SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"), TAG_END()); run_ab_until(ctx, -1, save_until_notified_and_responded, - -1, accept_and_notify); + -1, accept_and_notify_twice); /* Client events: - nua_subscribe(), nua_i_notify/nua_r_subscribe + nua_subscribe(), nua_i_notify/nua_r_subscribe/nua_i_notify */ - TEST_1(e = a->events->head); - if (e->data->e_event == nua_i_notify) { - TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_subscribe); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending); - } - else { - TEST_E(e->data->e_event, nua_r_subscribe); - TEST(e->data->e_status, 202); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; + for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) { + if (en1 == NULL && e->data->e_event == nua_i_notify) + en1 = e; + else if (en2 == NULL && e->data->e_event == nua_i_notify) + en2 = e; + else if (e->data->e_event == nua_r_subscribe) + es = e; + else + TEST_1(!e); } + + TEST_1(e = en1); + TEST_E(e->data->e_event, nua_i_notify); + TEST_1(tl_find(e->data->e_tags, nutag_substate)); + TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_pending); + TEST_1(sip = sip_object(e->data->e_msg)); TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence"); TEST_1(sip->sip_content_type); TEST_S(sip->sip_content_type->c_type, "application/pidf+xml"); TEST_1(sip->sip_subscription_state); TEST_S(sip->sip_subscription_state->ss_substate, "pending"); TEST_1(sip->sip_subscription_state->ss_expires); - TEST_1(tl_find(n_tags, nutag_substate)); - TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_pending); - TEST_1(!e->next); + + TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe); + TEST_1(e->data->e_status == 202 || e->data->e_status == 200); + TEST_1(tl_find(e->data->e_tags, nutag_substate)); + TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_pending); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_expires); + TEST_1(sip->sip_expires->ex_delta <= 333); + free_events_in_list(ctx, a->events); /* Server events: nua_i_subscribe, nua_r_notify */ @@ -731,6 +805,80 @@ int test_subscribe_notify(struct context *ctx) /* ---------------------------------------------------------------------- */ +/* Re-SUBSCRIBE + + A B + | | + |<------NOTIFY-------| + |-------200 OK------>| + | | +*/ + if (print_headings) + printf("TEST NUA-11.4.3: re-SUBSCRIBE\n"); + + /* Set default expiration time */ + nua_set_hparams(b_call->nh, NUTAG_SUB_EXPIRES(365), TAG_END()); + run_b_until(ctx, nua_r_set_params, until_final_response); + + SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url), + SIPTAG_EVENT_STR("presence"), + SIPTAG_EXPIRES_STR("3600"), + SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"), + TAG_END()); + + run_ab_until(ctx, -1, save_until_notified_and_responded, + -1, save_until_final_response); + + /* Client events: + nua_subscribe(), nua_i_notify/nua_r_subscribe + */ + for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) { + if (en1 == NULL && e->data->e_event == nua_i_notify) + en1 = e; + else if (e->data->e_event == nua_r_subscribe) + es = e; + else + TEST_1(!e); + } + TEST_1(e = en1); + TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence"); + TEST_1(sip->sip_content_type); + TEST_S(sip->sip_content_type->c_type, "application/pidf+xml"); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "active"); + TEST_1(sip->sip_subscription_state->ss_expires); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); + + TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe); + TEST_1(tl_find(e->data->e_tags, nutag_substate)); + TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_expires); + TEST_1(sip->sip_expires->ex_delta <= 365); + + free_events_in_list(ctx, a->events); + + /* Server events: nua_i_subscribe, nua_r_notify */ + TEST_1(e = b->events->head); + TEST_E(e->data->e_event, nua_i_subscribe); + TEST_E(e->data->e_status, 100); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active); + + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-11.4.3: PASSED\n"); + + /* ---------------------------------------------------------------------- */ + /* un-SUBSCRIBE A B @@ -742,7 +890,7 @@ int test_subscribe_notify(struct context *ctx) | | */ if (print_headings) - printf("TEST NUA-11.4.3: un-SUBSCRIBE\n"); + printf("TEST NUA-11.4.4: un-SUBSCRIBE\n"); UNSUBSCRIBE(a, a_call, a_call->nh, TAG_END()); @@ -752,31 +900,35 @@ int test_subscribe_notify(struct context *ctx) /* Client events: nua_unsubscribe(), nua_i_notify/nua_r_unsubscribe */ - TEST_1(e = a->events->head); - if (e->data->e_event == nua_i_notify) { - TEST_E(e->data->e_event, nua_i_notify); - n_tags = e->data->e_tags; + for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) { + if (en1 == NULL && e->data->e_event == nua_i_notify) + en1 = e; + else if (e->data->e_event == nua_r_unsubscribe) + es = e; + else + TEST_1(!e); + } + if (en1) { + TEST_1(e = en1); TEST_E(e->data->e_event, nua_i_notify); TEST_1(sip = sip_object(e->data->e_msg)); TEST_1(sip->sip_event); TEST_1(sip->sip_subscription_state); TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); TEST_1(!sip->sip_subscription_state->ss_expires); + n_tags = e->data->e_tags; TEST_1(tl_find(n_tags, nutag_substate)); TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_unsubscribe); - TEST(e->data->e_status, 200); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated); } - else { - TEST_E(e->data->e_event, nua_r_unsubscribe); - TEST(e->data->e_status, 200); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated); - } - TEST_1(!e->next); + + TEST_1(e = es); TEST_E(e->data->e_event, nua_r_unsubscribe); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_expires); + TEST_1(sip->sip_expires->ex_delta == 0); + free_events_in_list(ctx, a->events); /* Notifier events: nua_r_notify */ @@ -799,7 +951,7 @@ int test_subscribe_notify(struct context *ctx) nua_handle_destroy(b_call->nh), b_call->nh = NULL; if (print_headings) - printf("TEST NUA-11.4.3: PASSED\n"); + printf("TEST NUA-11.4.4: PASSED\n"); if (print_headings) printf("TEST NUA-11.4: PASSED\n"); @@ -825,7 +977,7 @@ int test_subscribe_notify_graceful(struct context *ctx) struct endpoint *a = &ctx->a, *b = &ctx->b; struct call *a_call = a->call, *b_call = b->call; - struct event *e; + struct event *e, *en1, *en2, *es; sip_t const *sip; tagi_t const *n_tags, *r_tags; struct nat_filter *f; @@ -846,35 +998,33 @@ int test_subscribe_notify_graceful(struct context *ctx) /* Client events: nua_subscribe(), nua_i_notify/nua_r_subscribe */ - TEST_1(e = a->events->head); - if (e->data->e_event == nua_i_notify) { - TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_subscribe); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending); - } - else { - TEST_E(e->data->e_event, nua_r_subscribe); - TEST(e->data->e_status, 202); - r_tags = e->data->e_tags; - TEST_1(tl_find(r_tags, nutag_substate)); - TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic); - TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); - TEST_1(sip = sip_object(e->data->e_msg)); - n_tags = e->data->e_tags; + for (en1 = en2 = es = NULL, e = a->events->head; e; e = e->next) { + if (en1 == NULL && e->data->e_event == nua_i_notify) + en1 = e; + else if (es == NULL && e->data->e_event == nua_r_subscribe) + es = e; + else + TEST_1(!e); } + TEST_1(e = en1); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); TEST_1(sip->sip_event); TEST_S(sip->sip_event->o_type, "presence"); TEST_1(sip->sip_content_type); TEST_S(sip->sip_content_type->c_type, "application/pidf+xml"); TEST_1(sip->sip_subscription_state); TEST_S(sip->sip_subscription_state->ss_substate, "pending"); TEST_1(sip->sip_subscription_state->ss_expires); + n_tags = e->data->e_tags; TEST_1(tl_find(n_tags, nutag_substate)); TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_pending); - TEST_1(!e->next); + + TEST_1(e = es); TEST_E(e->data->e_event, nua_r_subscribe); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + if (es == a->events->head) + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_embryonic); + else + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_pending); free_events_in_list(ctx, a->events); /* Server events: nua_i_subscribe, nua_r_notify */ @@ -1011,7 +1161,6 @@ int save_until_notify_responded_twice(CONDITION_PARAMS) return ep->flags.bit0 && ep->flags.bit1; } - /* ---------------------------------------------------------------------- */ /* Unsolicited NOTIFY */ @@ -1028,6 +1177,8 @@ int test_newsub_notify(struct context *ctx) sip_call_id_t *i; tagi_t const *n_tags, *r_tags; +#if 0 + if (print_headings) printf("TEST NUA-11.7.1: rejecting NOTIFY without subscription locally\n"); @@ -1161,6 +1312,298 @@ int test_newsub_notify(struct context *ctx) nua_handle_destroy(a_call->nh), a_call->nh = NULL; nua_handle_destroy(b_call->nh), b_call->nh = NULL; +#else + (void)i; + nua_set_params(b->nua, NUTAG_APPL_METHOD("NOTIFY"), TAG_END()); + run_b_until(ctx, nua_r_set_params, until_final_response); +#endif + + /* ---------------------------------------------------------------------- */ + + if (print_headings) + printf("TEST NUA-11.7.4: multiple unsolicited NOTIFYs\n"); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_EXPIRES_STR("10"), + SIPTAG_SUBJECT_STR("NUA-11.7.4aa"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: no"), + TAG_END()); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=333"), + SIPTAG_SUBJECT_STR("NUA-11.7.4a"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: no"), + TAG_END()); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=3000"), + SIPTAG_SUBJECT_STR("NUA-11.7.4b"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"), + TAG_END()); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("terminated"), + SIPTAG_SUBJECT_STR("NUA-11.7.4c"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"), + TAG_END()); + + a->state.n = 4; + b->state.n = 4; + + run_ab_until(ctx, -1, save_until_nth_final_response, + -1, accept_n_notifys); + + /* Notifier events: nua_r_notify nua_r_notify */ + TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated); + TEST_1(!e->next); + + /* subscriber events: + nua_i_notify + */ + TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); + + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "active"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "active"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); + TEST_1(!e->next); + + free_events_in_list(ctx, a->events); + free_events_in_list(ctx, b->events); + + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + nua_handle_destroy(b_call->nh), b_call->nh = NULL; + + if (print_headings) + printf("TEST NUA-11.7.4: PASSED\n"); + + /* ---------------------------------------------------------------------- */ + + if (print_headings) + printf("TEST NUA-11.7.5: multiple unsolicited NOTIFYs\n"); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=333"), + SIPTAG_SUBJECT_STR("NUA-11.7.5a"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: no"), + TAG_END()); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=3000"), + SIPTAG_SUBJECT_STR("NUA-11.7.5b"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"), + TAG_END()); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("terminated"), + SIPTAG_SUBJECT_STR("NUA-11.7.5c"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"), + TAG_END()); + + a->state.n = 3; + b->state.n = 3; + + run_ab_until(ctx, -1, save_until_nth_final_response, + -1, accept_n_notifys); + + /* Notifier events: nua_r_notify nua_r_notify */ + TEST_1(e = a->events->head); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_r_notify); + TEST(e->data->e_status, 200); + r_tags = e->data->e_tags; + TEST_1(tl_find(r_tags, nutag_substate)); + TEST(tl_find(r_tags, nutag_substate)->t_value, nua_substate_terminated); + TEST_1(!e->next); + + /* subscriber events: + nua_i_notify + */ + TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "active"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "active"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); + TEST_1(!e->next); + + free_events_in_list(ctx, a->events); + free_events_in_list(ctx, b->events); + + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + nua_handle_destroy(b_call->nh), b_call->nh = NULL; + + if (print_headings) + printf("TEST NUA-11.7.5: PASSED\n"); + + /* ---------------------------------------------------------------------- */ + +#if 0 + + if (print_headings) + printf("TEST NUA-11.7.6: unsolicited NOTIFY handle destroyed\n"); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=333"), + SIPTAG_SUBJECT_STR("NUA-11.7.6a"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: no"), + TAG_END()); + + NOTIFY(a, a_call, a_call->nh, + NUTAG_URL(b->contact->m_url), + NUTAG_NEWSUB(1), + SIPTAG_SUBSCRIPTION_STATE_STR("active; expires=3000"), + SIPTAG_SUBJECT_STR("NUA-11.7.6b"), + SIPTAG_EVENT_STR("message-summary"), + SIPTAG_CONTENT_TYPE_STR("application/simple-message-summary"), + SIPTAG_PAYLOAD_STR("Messages-Waiting: yes"), + TAG_END()); + + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + + a->state.n = 3; + b->state.n = 3; + + run_b_until(ctx, -1, accept_n_notifys); + + /* subscriber events: + nua_i_notify + */ + TEST_1(e = b->events->head); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "active"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "active"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_active); + TEST_1(e = e->next); TEST_E(e->data->e_event, nua_i_notify); + TEST_1(sip = sip_object(e->data->e_msg)); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); + n_tags = e->data->e_tags; + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, nua_substate_terminated); + TEST_1(!e->next); + + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-11.7.6: PASSED\n"); + + nua_handle_destroy(b_call->nh), b_call->nh = NULL; + +#endif + if (print_headings) printf("TEST NUA-11.7: PASSED\n"); @@ -1183,6 +1626,50 @@ int accept_notify(CONDITION_PARAMS) return event == nua_i_notify; } +int save_until_nth_final_response(CONDITION_PARAMS) +{ + save_event_in_list(ctx, event, ep, call); + + if (nua_r_set_params <= event && event < nua_i_network_changed + && status >= 200) { + if (ep->state.n > 0) + ep->state.n--; + return ep->state.n == 0; + } + + return 0; +} + +int accept_n_notifys(CONDITION_PARAMS) +{ + tagi_t const *substate = tl_find(tags, nutag_substate); + + if (event == nua_i_notify && status < 200) + RESPOND(ep, call, nh, SIP_200_OK, + NUTAG_WITH_THIS(ep->nua), + TAG_END()); + + save_event_in_list(ctx, event, ep, call); + + if (event != nua_i_notify) + return 0; + + if (ep->state.n > 0) + ep->state.n--; + + if (ep->state.n == 0) + return 1; + + if (substate && substate->t_value == nua_substate_terminated) { + if (call && call->nh == nh) { + call->nh = NULL; + nua_handle_destroy(nh); + } + } + + return 0; +} + /* ======================================================================== */ int save_until_subscription_terminated(CONDITION_PARAMS); @@ -1336,8 +1823,8 @@ int accept_subscription_until_terminated(CONDITION_PARAMS) int test_simple(struct context *ctx) { - return - test_message(ctx) + return 0 + || test_message(ctx) || test_publish(ctx) || test_subscribe_notify(ctx) || test_subscribe_notify_graceful(ctx) diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c index e4bb915f7b..8f949661bd 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c @@ -613,8 +613,8 @@ static void print_media(sdp_printer_t *p, sdp_printf(p, " %s", l->l_text); } else { - sdp_printf(p, " 9"); /* SDP syntax requires at least one format. - 9 is used by nobody, right?. */ + sdp_printf(p, " 19"); /* SDP syntax requires at least one format. + 19 is used by nobody, right?. */ }