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 194ebf4a3c..d15e4298e4 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_notifier.c @@ -558,9 +558,6 @@ static int nua_notify_client_init(nua_client_request_t *cr, } } - if (nu->nu_substate == nua_substate_terminated) - cr->cr_terminating = 1; - cr->cr_usage = du; return nua_notify_client_init_etag(cr, msg, sip, tags); @@ -700,6 +697,18 @@ int nua_notify_client_request(nua_client_request_t *cr, if (nu->nu_substate == nua_substate_terminated) cr->cr_terminating = 1; + if (cr->cr_terminating) { + nua_server_request_t *sr; + for (sr = du->du_dialog->ds_sr; sr; sr = sr->sr_next) { + if (sr->sr_usage == du) { + /* If subscribe has not been responded, don't terminate usage by NOTIFY */ + sr->sr_terminating = 1; + cr->cr_terminating = 0; + break; + } + } + } + if (du->du_event && !sip->sip_event) sip_add_dup(cr->cr_msg, sip, (sip_header_t *)du->du_event); 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 de3e5d4928..8bb4d55d44 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/nua_subnotref.c @@ -248,15 +248,18 @@ static int nua_subscribe_client_request(nua_client_request_t *cr, nua_dialog_usage_t *du = cr->cr_usage; sip_time_t expires = 0; - if (cr->cr_event != nua_r_subscribe || - (du && du->du_shutdown) || - (sip->sip_expires && sip->sip_expires->ex_delta == 0)) + if (cr->cr_event != nua_r_subscribe || !du || du->du_shutdown) cr->cr_terminating = 1; if (du) { struct event_usage *eu = nua_dialog_usage_private(du); sip_event_t *o = sip->sip_event; + if (eu->eu_notified && + sip->sip_expires && + sip->sip_expires->ex_delta == 0) + cr->cr_terminating = 1; + if (nua_client_bind(cr, du) < 0) return -1; 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 4176ce728f..1fdb7ae32f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_simple.c @@ -607,6 +607,27 @@ int accept_and_notify_twice(CONDITION_PARAMS) } } +int save_until_responded_and_notified_twice(CONDITION_PARAMS) +{ + save_event_in_list(ctx, event, ep, call); + + if (event == nua_i_notify) { + if (ep->flags.bit0) + ep->flags.bit1 = 1; + ep->flags.bit0 = 1; + } + + if (event == nua_r_subscribe || event == nua_r_unsubscribe) { + if (status >= 300) + return 1; + else if (status >= 200) { + ep->flags.bit2 = 1; + } + } + + return ep->flags.bit0 && ep->flags.bit1 && ep->flags.bit2; +} + int accept_and_notify(CONDITION_PARAMS) { @@ -620,16 +641,21 @@ int accept_and_notify(CONDITION_PARAMS) switch (event) { case nua_i_subscribe: if (status < 200) { + int fetch = sip->sip_expires && sip->sip_expires->ex_delta == 0; + RESPOND(ep, call, nh, SIP_202_ACCEPTED, NUTAG_WITH(with), SIPTAG_EXPIRES_STR("360"), + SIPTAG_EXPIRES(sip->sip_expires), 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), + NUTAG_SUBSTATE(fetch + ? nua_substate_pending + : nua_substate_terminated), TAG_END()); } @@ -695,7 +721,7 @@ int test_subscribe_notify(struct context *ctx) SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"), TAG_END()); - run_ab_until(ctx, -1, save_until_notified_and_responded, + run_ab_until(ctx, -1, save_until_responded_and_notified_twice, -1, accept_and_notify_twice); /* Client events: @@ -727,10 +753,16 @@ int test_subscribe_notify(struct context *ctx) 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)); - if (es == a->events->head) - TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_embryonic); - else - TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_pending); + r_tags = tl_find(e->data->e_tags, nutag_substate); + if (es == a->events->head) { + TEST(r_tags->t_value, nua_substate_embryonic); + } + else if (es == a->events->head->next) { + TEST_1(r_tags->t_value == nua_substate_pending); + } + else { + TEST_1(r_tags->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 <= 333); @@ -1164,6 +1196,235 @@ int save_until_notify_responded_twice(CONDITION_PARAMS) return ep->flags.bit0 && ep->flags.bit1; } +/* ---------------------------------------------------------------------- */ + +/* + * When incoming SUBSCRIBE, send NOTIFY, + * 200 OK SUBSCRIBE when NOTIFY has been responded. + */ +int notify_and_accept(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: + if (status < 200) { + NOTIFY(ep, call, nh, + SIPTAG_EVENT(sip->sip_event), + SIPTAG_CONTENT_TYPE_STR("application/pidf+xml"), + SIPTAG_PAYLOAD_STR(presence_closed), + TAG_END()); + } + return 0; + + case nua_r_notify: + if (status >= 200) { + struct event *e; + for (e = ep->events->head; e; e = e->next) { + if (e->data->e_event == nua_i_subscribe) { + RESPOND(ep, call, nh, SIP_200_OK, + NUTAG_WITH(e->data->e_msg), + TAG_END()); + break; + } + } + return 1; + } + + default: + return 0; + } +} + +int test_event_fetch(struct context *ctx) +{ + BEGIN(); + + struct endpoint *a = &ctx->a, *b = &ctx->b; + struct call *a_call = a->call, *b_call = b->call; + struct event *e, *en1, *en2, *es; + sip_t const *sip; + tagi_t const *r_tags; + + if (print_headings) + printf("TEST NUA-11.6.1: event fetch using nua_notify()\n"); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); + +/* Fetch 1: + + A B + | | + |------SUBSCRIBE----->| + | Expires: 0 | + |<---------202--------| + | | + |<-------NOTIFY-------| + | S-State: terminated | + |-------200 OK------->| + | | +*/ + + SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url), + SIPTAG_EVENT_STR("presence"), + SIPTAG_EXPIRES_STR("0"), + SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"), + TAG_END()); + + run_ab_until(ctx, -1, save_until_notified_and_responded, + -1, accept_and_notify); + + /* Client events: + nua_subscribe(), nua_i_notify/nua_r_subscribe/nua_i_notify + */ + 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_terminated); + 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, "terminated"); + + 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)); + + if (es == a->events->head) + TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_embryonic); + else + TEST(tl_find(e->data->e_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); + + /* 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_terminated); + + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-11.6.1: PASSED\n"); + + 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.6.2: event fetch, NOTIFY comes before 202\n"); + + TEST_1(a_call->nh = nua_handle(a->nua, a_call, SIPTAG_TO(b->to), TAG_END())); + +/* Fetch 2: + + A B + | | + |------SUBSCRIBE----->| + | Expires: 0 | + |<-------NOTIFY-------| + | S-State: terminated | + |-------200 OK------->| + | | + |<---------202--------| + | | +*/ + + SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url), + SIPTAG_EVENT_STR("presence"), + SIPTAG_EXPIRES_STR("0"), + SIPTAG_ACCEPT_STR("application/xpidf, application/pidf+xml"), + TAG_END()); + + run_ab_until(ctx, -1, save_until_notified_and_responded, + -1, notify_and_accept); + + /* Client events: + nua_subscribe(), nua_i_notify/nua_r_subscribe/nua_i_notify + */ + 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_terminated); + 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, "terminated"); + + 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)); + + if (es == a->events->head) + TEST(tl_find(e->data->e_tags, nutag_substate)->t_value, nua_substate_embryonic); + else + TEST(tl_find(e->data->e_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); + + /* 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_terminated); + + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-11.6.2: PASSED\n"); + + nua_handle_destroy(a_call->nh), a_call->nh = NULL; + nua_handle_destroy(b_call->nh), b_call->nh = NULL; + + END(); +} + /* ---------------------------------------------------------------------- */ /* Unsolicited NOTIFY */ @@ -1830,6 +2091,7 @@ int test_simple(struct context *ctx) || test_message(ctx) || test_publish(ctx) || test_subscribe_notify(ctx) + || test_event_fetch(ctx) || test_subscribe_notify_graceful(ctx) || test_newsub_notify(ctx) || test_subscription_timeout(ctx) diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c b/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c index ef0bed0d58..2023bdfdf4 100644 --- a/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c +++ b/libs/sofia-sip/libsofia-sip-ua/nua/test_sip_events.c @@ -357,6 +357,73 @@ int test_events(struct context *ctx) if (print_headings) printf("TEST NUA-12.5: PASSED\n"); + /* ---------------------------------------------------------------------- */ +/* Fetch event, SUBSCRIBE with expires: 0 + + A B + | | + |------SUBSCRIBE---->| + |<--------202--------| + |<------NOTIFY-------| + |-------200 OK------>| + | | +*/ + if (print_headings) + printf("TEST NUA-12.5.1: event fetch\n"); + + SUBSCRIBE(a, a_call, a_call->nh, NUTAG_URL(b->contact->m_url), + SIPTAG_EVENT_STR("presence"), + SIPTAG_ACCEPT_STR("application/pidf+xml"), + SIPTAG_EXPIRES_STR("0"), + TAG_END()); + + run_ab_until(ctx, -1, save_until_notified_and_responded, + -1, save_until_subscription); + + /* Client events: + nua_subscribe(), nua_i_notify/nua_r_subscribe + */ + TEST_1(en = event_by_type(a->events->head, nua_i_notify)); + TEST_1(es = event_by_type(a->events->head, nua_r_subscribe)); + + e = es; TEST_E(e->data->e_event, nua_r_subscribe); + TEST_1(t = tl_find(e->data->e_tags, nutag_substate)); + TEST_1(t->t_value == nua_substate_pending || + t->t_value == nua_substate_terminated || + t->t_value == nua_substate_embryonic); + + e = en; 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(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_payload && sip->sip_payload->pl_data); + TEST_1(sip->sip_subscription_state); + TEST_S(sip->sip_subscription_state->ss_substate, "terminated"); + TEST_1(tl_find(n_tags, nutag_substate)); + TEST(tl_find(n_tags, nutag_substate)->t_value, + nua_substate_terminated); + TEST_1(!en->next || !es->next); + free_events_in_list(ctx, a->events); + + /* + Server events: + nua_i_subscription + */ + TEST_1(e = b->events->head); + TEST_E(e->data->e_event, nua_i_subscription); + TEST(tl_gets(e->data->e_tags, NEATAG_SUB_REF(sub), TAG_END()), 1); + TEST_1(sub); + TEST_1(!e->next); + + free_events_in_list(ctx, b->events); + + if (print_headings) + printf("TEST NUA-12.4.1: PASSED\n"); + + /* ---------------------------------------------------------------------- */ /* 2nd SUBSCRIBE with event id @@ -370,7 +437,7 @@ int test_events(struct context *ctx) */ /* XXX - we should do this before unsubscribing first one */ if (print_headings) - printf("TEST NUA-12.4: establishing 2nd subscription\n"); + printf("TEST NUA-12.4.2: establishing 2nd subscription\n"); NOTIFIER(b, b_call, b_call->nh, SIPTAG_EVENT_STR("presence"),