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 285237a266..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) 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 b7bfea378c..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 */ @@ -199,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; @@ -326,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 @@ -438,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 @@ -476,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) { @@ -827,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); @@ -998,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) @@ -1006,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; } } @@ -1079,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); @@ -1476,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 @@ -1516,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; @@ -1541,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), @@ -1591,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)), @@ -1631,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), @@ -1659,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_stack.c b/libs/sofia-sip/libsofia-sip-ua/nua/nua_stack.c index 0d23077c8d..4cc659a4e7 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); 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_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..785247f47e 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,6 +497,11 @@ 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)) @@ -569,6 +574,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_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_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)