static int nua_publish_client_response(nua_client_request_t *cr, int status, char const *phrase, sip_t const *sip) { nua_handle_t *nh = cr->cr_owner; nua_dialog_usage_t *du = cr->cr_usage; if (!cr->cr_terminated && du && sip) { struct publish_usage *pu = nua_dialog_usage_private(du); sip_expires_t const *ex = sip->sip_expires; /* Reset state */ pu->pu_published = 0; if (pu->pu_etag) su_free(nh->nh_home, pu->pu_etag), pu->pu_etag = NULL; if (status < 300) { pu->pu_published = 1; pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_etag); if (!ex || ex->ex_delta == 0 || !pu->pu_etag) { cr->cr_terminated = 1; if (!ex || ex->ex_delta == 0) SET_STATUS(900, "Received Invalid Expiration Time"); else SET_STATUS1(NUA_ERROR_AT(__FILE__, __LINE__)); } else nua_dialog_usage_set_refresh(du, ex->ex_delta); } } return nua_base_client_response(cr, status, phrase, sip, NULL); }
static void nua_notify_usage_refresh(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du, sip_time_t now) { struct notifier_usage *nu = nua_dialog_usage_private(du); nua_client_request_t *cr = du->du_cr; nua_event_t e = nua_r_notify; if (cr) { int terminating = 0; if (nu->nu_expires && nu->nu_expires <= now) terminating = 1; else if (nu->nu_requested && nu->nu_requested <= now) terminating = 1; if (nua_client_resend_request(cr, terminating) >= 0) return; } else { if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0) return; } nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__), NUTAG_SUBSTATE(nua_substate_terminated), TAG_END()); nua_dialog_usage_remove(nh, ds, du, NULL, NULL); }
static int nua_refer_server_respond(nua_server_request_t *sr, tagi_t const *tags) { nua_handle_t *nh = sr->sr_owner; struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage); sip_refer_sub_t const *rs = sip_refer_sub(sr->sr_response.sip); if (sr->sr_status < 200 || nu == NULL) { } else if (sr->sr_status < 300 && /* No subscription if Refer-Sub: false in response */ (rs == NULL || !su_casematch(rs->rs_value, "false"))) { sr->sr_usage->du_ready = 1; nu->nu_expires = sip_now() + NH_PGET(nh, refer_expires); if (sr->sr_application) /* Application responded to REFER */ nu->nu_substate = nua_substate_active; } else { /* Destroy the implicit subscription usage */ sr->sr_terminating = 1; } return nua_base_server_respond(sr, tags); }
/** @interal Shut down NOTIFY usage. * * @retval >0 shutdown done * @retval 0 shutdown in progress * @retval <0 try again later */ static int nua_notify_usage_shutdown(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du) { struct notifier_usage *nu = nua_dialog_usage_private(du); nua_client_request_t *cr = du->du_cr; nu->nu_substate = nua_substate_terminated; if (cr) { SU_DEBUG_5(("%s(%p, %p, %p): using existing cr=%p\n", "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *)du, (void *)cr)); if (nua_client_resend_request(cr, 1) >= 0) return 0; } else { SU_DEBUG_5(("%s(%p, %p, %p): new NOTIFY cr for %s\n", "nua_notify_usage_shutdown", (void *)nh, (void *)ds, (void *)du, du->du_event ? du->du_event->o_type : "<implicit>")); if (nua_client_tcreate(nh, nua_r_notify, &nua_notify_client_methods, SIPTAG_EVENT(du->du_event), NUTAG_SUBSTATE(nua_substate_terminated), TAG_END()) >= 0) return 0; } nua_dialog_usage_remove(nh, ds, du, NULL, NULL); return 200; }
static int nua_refer_server_preprocess(nua_server_request_t *sr) { nua_handle_t *nh = sr->sr_owner; sip_t const *sip = sr->sr_request.sip; struct notifier_usage *nu; sip_event_t *o; if (nh->nh_ds->ds_got_referrals || NH_PGET(nh, refer_with_id)) o = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq); else o = sip_event_make(nh->nh_home, "refer"); if (o) { sr->sr_usage = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o); msg_header_free(nh->nh_home, (msg_header_t *)o); } if (!sr->sr_usage) return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR); nu = nua_dialog_usage_private(sr->sr_usage); nu->nu_requested = sip_now() + NH_PGET(nh, refer_expires); return 0; }
static int nua_notify_client_report(nua_client_request_t *cr, int status, char const *phrase, sip_t const *sip, nta_outgoing_t *orq, tagi_t const *tags) { nua_handle_t *nh = cr->cr_owner; nua_dialog_usage_t *du = cr->cr_usage; struct notifier_usage *nu = nua_dialog_usage_private(du); enum nua_substate substate = nua_substate_terminated; if (nu && !cr->cr_terminated) substate = nu->nu_substate; nua_stack_tevent(nh->nh_nua, nh, nta_outgoing_getresponse(orq), (enum nua_event_e)cr->cr_event, status, phrase, NUTAG_SUBSTATE(substate), SIPTAG_EVENT(du ? du->du_event : NULL), TAG_NEXT(tags)); if (du && du->du_cr == cr && !cr->cr_terminated) { if (nu->nu_requested) { /* Re-SUBSCRIBEd while NOTIFY was in progress, resend NOTIFY */ nua_client_resend_request(cr, 0); } else if (nu->nu_expires) { nua_dialog_usage_set_refresh_at(du, nu->nu_expires); } } return 0; }
static int nua_refer_client_response(nua_client_request_t *cr, int status, char const *phrase, sip_t const *sip) { nua_dialog_usage_t *du = cr->cr_usage; enum nua_substate substate = nua_substate_terminated; if (du) { struct event_usage *eu = nua_dialog_usage_private(du); if (status < 200) { substate = eu->eu_substate; } else if (status < 300) { sip_refer_sub_t const *rs = sip_refer_sub(sip); if (rs && su_casematch("false", rs->rs_value)) cr->cr_terminated = 1; if (!cr->cr_terminated) substate = eu->eu_substate; } } return nua_base_client_tresponse(cr, status, phrase, sip, NUTAG_SUBSTATE(substate), SIPTAG_EVENT(du ? du->du_event : NULL), TAG_END()); }
static int nua_publish_client_init(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { nua_handle_t *nh = cr->cr_owner; nua_dialog_usage_t *du; struct publish_usage *pu; if (cr->cr_event == nua_r_publish) { du = nua_dialog_usage_add(nh, nh->nh_ds, nua_publish_usage, NULL); if (!du) return -1; pu = nua_dialog_usage_private(du); pu->pu_published = 0; if (sip->sip_if_match) { pu->pu_etag = sip_etag_dup(nh->nh_home, sip->sip_if_match); if (!pu->pu_etag) return -1; sip_header_remove(msg, sip, (sip_header_t *)sip->sip_if_match); } } else du = nua_dialog_usage_get(nh->nh_ds, nua_publish_usage, NULL); cr->cr_usage = du; return 0; }
static int nua_publish_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { nua_dialog_usage_t *du = cr->cr_usage; int un, done; sip_etag_t const *etag = NULL; un = cr->cr_terminating || cr->cr_event != nua_r_publish || (du && du->du_shutdown) || (sip->sip_expires && sip->sip_expires->ex_delta == 0); nua_client_set_terminating(cr, un); done = un; if (du) { struct publish_usage *pu = nua_dialog_usage_private(du); if (nua_client_bind(cr, du) < 0) return -1; if (pu->pu_published) done = 1; etag = pu->pu_etag; } return nua_base_client_trequest(cr, msg, sip, SIPTAG_IF_MATCH(etag), TAG_IF(done, SIPTAG_PAYLOAD(NONE)), TAG_IF(done, SIPTAG_CONTENT_TYPE(NONE)), TAG_IF(un, SIPTAG_EXPIRES_STR("0")), TAG_NEXT(tags)); }
static int nua_publish_client_check_restart(nua_client_request_t *cr, int status, char const *phrase, sip_t const *sip) { char const *restarting = NULL; if (cr->cr_terminating || !cr->cr_usage) ; else if (status == 412) restarting = phrase; else if (200 <= status && status < 300 && sip->sip_expires && sip->sip_expires->ex_delta == 0) restarting = "Immediate re-PUBLISH"; if (restarting) { struct publish_usage *pu = nua_dialog_usage_private(cr->cr_usage); if (pu) { pu->pu_published = 0; su_free(cr->cr_owner->nh_home, pu->pu_etag), pu->pu_etag = NULL; if (nua_client_restart(cr, 100, restarting)) return 0; } } return nua_base_client_check_restart(cr, status, phrase, sip); }
static int nua_subscribe_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { nua_dialog_usage_t *du = cr->cr_usage; sip_time_t expires = 0; if (cr->cr_event == nua_r_destroy || !du || du->du_shutdown) nua_client_set_terminating(cr, 1); if (du) { struct event_usage *eu = nua_dialog_usage_private(du); sip_event_t *o = sip->sip_event; if (nua_client_bind(cr, du) < 0) return -1; if (eu->eu_no_id && o && o->o_id) { /* Notifier does not handle id properly, remove it */ msg_header_remove_param(o->o_common, "id"); } #if 0 if (cr->cr_terminating) { /* Already terminated subscription? */ if (eu->eu_substate == nua_substate_terminated || eu->eu_substate == nua_substate_embryonic) { return nua_client_return(cr, SIP_200_OK, msg); } } #endif nua_dialog_usage_reset_refresh(du); /* during SUBSCRIBE transaction */ if (cr->cr_terminating || cr->cr_event != nua_r_subscribe) expires = eu->eu_delta = 0; else if (sip->sip_expires) /* Use value specified by application or negotiated with Min-Expires */ expires = eu->eu_delta = sip->sip_expires->ex_delta; else /* We just use common default value, but the default is actually package-specific according to the RFC 3265 section 4.4.4: [Event] packages MUST also define a default "Expires" value to be used if none is specified. */ expires = eu->eu_delta = 3600; eu->eu_final_wait = 0; if (eu->eu_substate == nua_substate_terminated) eu->eu_substate = nua_substate_embryonic; } if (!sip->sip_expires || sip->sip_expires->ex_delta != expires) { sip_expires_t ex[1]; sip_expires_init(ex)->ex_delta = expires; sip_add_dup(msg, sip, (sip_header_t *)ex); } return nua_base_client_request(cr, msg, sip, tags); }
int nua_subscribe_server_preprocess(nua_server_request_t *sr) { nua_handle_t *nh = sr->sr_owner; nua_dialog_state_t *ds = nh->nh_ds; nua_dialog_usage_t *du; struct notifier_usage *nu; sip_t const *sip = sr->sr_request.sip; sip_event_t *o = sip->sip_event; char const *event = o ? o->o_type : NULL; /* Maximum expiration time */ unsigned long expires = sip->sip_expires ? sip->sip_expires->ex_delta : 3600; sip_time_t now = sip_now(); sip_allow_events_t *appl_event = NH_PGET(nh, appl_event); assert(nh && nh->nh_nua->nua_dhandle != nh); du = nua_dialog_usage_get(ds, nua_notify_usage, o); if (du == NULL) { /* Create a new subscription */ du = nua_dialog_usage_add(nh, ds, nua_notify_usage, o); if (du == NULL) return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR); } else if (!msg_header_find_param((void *)appl_event, event)) { unsigned max_expires; /* Refresh existing subscription */ if (su_strmatch(event, "refer")) max_expires = NH_PGET(nh, refer_expires); else max_expires = NH_PGET(nh, sub_expires); if (expires >= max_expires) expires = max_expires; SR_STATUS1(sr, SIP_200_OK); } nu = nua_dialog_usage_private(du); if (now + expires >= now) nu->nu_requested = now + expires; else nu->nu_requested = SIP_TIME_MAX - 1; #if SU_HAVE_EXPERIMENTAL nu->nu_etags = sip_suppress_body_if_match(sip) || sip_suppress_notify_if_match(sip) || sip_has_feature(sr->sr_request.sip->sip_supported, "etags"); #endif sr->sr_usage = du; return sr->sr_status <= 100 ? 0 : sr->sr_status; }
/** @internal Respond to a SUBSCRIBE request. * */ static int nua_subscribe_server_respond(nua_server_request_t *sr, tagi_t const *tags) { struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage); msg_t *msg = sr->sr_response.msg; sip_t *sip = sr->sr_response.sip; if (200 <= sr->sr_status && sr->sr_status < 300) { sip_expires_t ex[1]; sip_expires_init(ex); if (nu) { sip_time_t now = sip_now(); if (nu->nu_requested) { 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 nu->nu_expires = now; if (nu->nu_expires <= now) nu->nu_substate = nua_substate_terminated; } if (nu->nu_expires > now) ex->ex_delta = nu->nu_expires - now; } else { /* Always add header Expires: 0 */ } if (!sip->sip_expires || sip->sip_expires->ex_delta > ex->ex_delta) sip_add_dup(msg, sip, (sip_header_t *)ex); } return nua_base_server_respond(sr, tags); }
/** Refresh subscription */ static void nua_subscribe_usage_refresh(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du, sip_time_t now) { nua_client_request_t *cr = du->du_cr; struct event_usage *eu = nua_dialog_usage_private(du); assert(eu); if (eu->eu_final_wait) { /* Did not receive NOTIFY for fetch */ sip_event_t const *o = du->du_event; char const *id = o ? o->o_id : NULL; SU_DEBUG_3(("nua(%p): event %s%s%s fetch timeouts\n", (void *)nh, o ? o->o_type : "(empty)", id ? "; id=" : "", id ? id : "")); nua_stack_tevent(nh->nh_nua, nh, NULL, nua_i_notify, 408, "Fetch Timeouts without NOTIFY", NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_EVENT(du->du_event), TAG_END()); nua_dialog_usage_remove(nh, ds, du, NULL, NULL); return; } if (cr) { if (nua_client_resend_request(cr, 0) >= 0) return; } else if (eu->eu_refer) { /* * XXX - If we have received a NOTIFY, we should try to terminate * subscription */ } if (!eu->eu_unsolicited) nua_stack_tevent(nh->nh_nua, nh, NULL, nua_i_notify, NUA_ERROR_AT(__FILE__, __LINE__), NUTAG_SUBSTATE(nua_substate_terminated), SIPTAG_EVENT(du->du_event), TAG_END()); nua_dialog_usage_remove(nh, ds, du, NULL, NULL); }
static int nua_refer_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { nua_handle_t *nh = cr->cr_owner; nua_dialog_usage_t *du, *du0 = cr->cr_usage; struct event_usage *eu; sip_event_t *event; int error; cr->cr_usage = NULL; event = sip_event_format(nh->nh_home, "refer;id=%u", sip->sip_cseq->cs_seq); if (!event) return -1; if (du0 == NULL || du0->du_event == NULL || du0->du_event->o_id == NULL || strcmp(du0->du_event->o_id, event->o_id)) { du = nua_dialog_usage_add(nh, nh->nh_ds, nua_subscribe_usage, event); if (!du) return -1; } else { du = du0, du0 = NULL; } if (du0) nua_dialog_usage_remove(nh, nh->nh_ds, du0, NULL, NULL); eu = nua_dialog_usage_private(cr->cr_usage = du); eu ->eu_refer = 1; error = nua_base_client_request(cr, msg, sip, tags); if (!error) { /* Give application an Event header for matching NOTIFYs with REFER */ nua_stack_tevent(nh->nh_nua, nh, NULL, (enum nua_event_e)cr->cr_event, SIP_100_TRYING, NUTAG_REFER_EVENT(event), SIPTAG_EVENT(event), TAG_END()); su_free(nh->nh_home, event); } return error; }
static int nua_subscribe_server_report(nua_server_request_t *sr, tagi_t const *tags) { nua_handle_t *nh = sr->sr_owner; nua_dialog_state_t *ds = nh->nh_ds; nua_dialog_usage_t *du = sr->sr_usage; struct notifier_usage *nu = nua_dialog_usage_private(du); enum nua_substate substate = nua_substate_terminated; int notify = 0; int retval; if (nu && !sr->sr_terminating) { substate = nu->nu_substate; } /* nu_requested is set by SUBSCRIBE and cleared when NOTIFY is sent */ if (nu && nu->nu_requested && substate != nua_substate_embryonic) { #if SU_HAVE_EXPERIMENTAL sip_t const *sip = sr->sr_request.sip; sip_suppress_notify_if_match_t *snim = sip_suppress_notify_if_match(sip); sip_suppress_body_if_match_t *sbim = sip_suppress_body_if_match(sip); if (!nu->nu_tag) notify = 1; else if (snim && su_casematch(snim->snim_tag, nu->nu_tag)) notify = 0; else if (sbim && su_casematch(snim->snim_tag, nu->nu_tag)) notify = 1, nu->nu_no_body = 1; else #endif notify = 1; notify = notify && du->du_cr != NULL; } retval = nua_base_server_treport(sr, NUTAG_SUBSTATE(substate), TAG_END()); if (retval >= 2 || du == NULL) return retval; if (notify) { /* Send NOTIFY (and terminate subscription, when needed) */ nua_dialog_usage_refresh(nh, ds, du); } return retval; }
/** Terminate subscription. * * @retval >0 shutdown done * @retval 0 shutdown in progress * @retval <0 try again later */ static int nua_subscribe_usage_shutdown(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du) { struct event_usage *eu = nua_dialog_usage_private(du); nua_client_request_t *cr = du->du_cr; assert(eu); (void)eu; if (cr) { if (nua_client_resend_request(cr, 1) >= 0) return 0; } nua_dialog_usage_remove(nh, ds, du, NULL, NULL); return 200; }
static int nua_refer_server_report(nua_server_request_t *sr, tagi_t const *tags) { nua_handle_t *nh = sr->sr_owner; struct notifier_usage *nu = nua_dialog_usage_private(sr->sr_usage); sip_t const *sip = sr->sr_request.sip; sip_referred_by_t *by = sip->sip_referred_by, default_by[1]; sip_event_t const *o = sr->sr_usage->du_event; enum nua_substate substate = nua_substate_terminated; int initial = sr->sr_initial, retval; if (nu) { if (!sr->sr_terminating) substate = nu->nu_substate; } if (by == NULL) { by = sip_referred_by_init(default_by); by->b_display = sip->sip_from->a_display; *by->b_url = *sip->sip_from->a_url; } retval = nua_base_server_treport(sr, NUTAG_SUBSTATE(substate), NUTAG_REFER_EVENT(o), TAG_IF(by, SIPTAG_REFERRED_BY(by)), TAG_END()); if (retval >= 2 || nu == NULL) return retval; if (initial) nua_stack_post_signal(nh, nua_r_notify, SIPTAG_EVENT(o), SIPTAG_CONTENT_TYPE_STR("message/sipfrag"), SIPTAG_PAYLOAD_STR("SIP/2.0 100 Trying\r\n"), TAG_END()); return retval; }
static void nua_notify_usage_refresh(nua_handle_t *nh, nua_dialog_state_t *ds, nua_dialog_usage_t *du) { struct notifier_usage *nu = nua_dialog_usage_private(du); nua_client_request_t *cr = du->du_cr; nua_event_t e = nua_r_notify; if (cr) { int terminating = 0; sip_time_t now = sip_now(); SU_DEBUG_7(("%s(%p, %p, %p): using existing cr=%p\n", "nua_notify_usage_refresh", (void *)nh, (void *)ds, (void *)du, (void *)cr)); if (nu->nu_expires && nu->nu_expires <= now) terminating = 1; else if (nu->nu_requested && nu->nu_requested <= now) terminating = 1; if (nua_client_resend_request(cr, terminating) >= 0) return; } else { SU_DEBUG_7(("%s(%p, %p, %p): new NOTIFY cr for %s\n", "nua_notify_usage_refresh", (void *)nh, (void *)ds, (void *)du, du->du_event ? du->du_event->o_type : "<implicit>")); if (nua_client_create(nh, e, &nua_notify_client_methods, NULL) >= 0) return; } nua_stack_tevent(nh->nh_nua, nh, NULL, e, NUA_ERROR_AT(__FILE__, __LINE__), NUTAG_SUBSTATE(nua_substate_terminated), TAG_END()); nua_dialog_usage_remove(nh, ds, du, NULL, NULL); }
static int nua_notify_client_init(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { nua_handle_t *nh = cr->cr_owner; nua_dialog_usage_t *du; struct notifier_usage *nu; sip_event_t const *o = sip->sip_event; sip_subscription_state_t *ss = sip->sip_subscription_state; sip_time_t now = sip_now(); if (o == NULL && nh->nh_ds->ds_has_notifys == 1) o = NONE; du = nua_dialog_usage_get(nh->nh_ds, nua_notify_usage, o); if (!du) { tagi_t const *newsub = tl_find_last(tags, nutag_newsub); if (!newsub || !newsub->t_value) return 0; /* Rejected eventually by nua_notify_client_request() */ /* Create new notifier */ du = nua_dialog_usage_add(nh, nh->nh_ds, nua_notify_usage, o); if (du == NULL) return -1; nu = nua_dialog_usage_private(du); nu->nu_expires = now; } else nu = nua_dialog_usage_private(du); if (nu->nu_substate == nua_substate_terminated) { /*Xyzzy*/; } else if (ss != NULL) { /* SIPTAG_SUBSCRIPTION_STATE() overrides NUTAG_SUBSTATE() */ nu->nu_substate = nua_substate_make(ss->ss_substate); if (ss->ss_expires) { unsigned long expires = strtoul(ss->ss_expires, NULL, 10); if (now + expires < now) expires = SIP_TIME_MAX - now - 1; /* We can change the lifetime of unsolicited subscription at will */ if (nu->nu_requested == 0) nu->nu_expires = nu->nu_requested = now + expires; /* Notifier can only shorten the subscribed time */ else if (nu->nu_requested >= now + expires) nu->nu_expires = nu->nu_requested = now + expires; } else { if (nu->nu_requested >= nu->nu_expires) nu->nu_expires = nu->nu_requested; } } else { enum nua_substate substate = nu->nu_substate; if (nu->nu_requested >= nu->nu_expires) nu->nu_expires = nu->nu_requested; if (nu->nu_expires > now) { tagi_t const *t = tl_find_last(tags, nutag_substate); if (t) substate = (enum nua_substate)t->t_value; } else substate = nua_substate_terminated; switch (substate) { case nua_substate_embryonic: /*FALLTHROUGH*/ case nua_substate_pending: nu->nu_substate = nua_substate_pending; break; case nua_substate_active: default: nu->nu_substate = nua_substate_active; break; case nua_substate_terminated: nu->nu_substate = nua_substate_terminated; break; } } cr->cr_usage = du; return nua_notify_client_init_etag(cr, msg, sip, tags); }
static int nua_notify_client_init_etag(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { #if SU_HAVE_EXPERIMENTAL nua_handle_t *nh = cr->cr_owner; struct notifier_usage *nu = nua_dialog_usage_private(cr->cr_usage); nua_server_request_t *sr; if (nu->nu_tag) su_free(nh->nh_home, nu->nu_tag), nu->nu_tag = NULL; nu->nu_no_body = 0; if (sip->sip_etag) { nu->nu_appl_etags = 1; nu->nu_tag = su_strdup(nh->nh_home, sip->sip_etag->g_string); } else if (!nu->nu_appl_etags && nu->nu_etags) { su_md5_t md5[1]; unsigned char digest[SU_MD5_DIGEST_SIZE]; sip_payload_t pl[1] = { SIP_PAYLOAD_INIT() }; char token[2 * 16]; su_md5_init(md5); if (sip->sip_payload) *pl = *sip->sip_payload; if (pl->pl_len) su_md5_update(md5, pl->pl_data, pl->pl_len); su_md5_update(md5, &pl->pl_len, sizeof(pl->pl_len)); if (sip->sip_content_type) su_md5_striupdate(md5, sip->sip_content_type->c_type); su_md5_digest(md5, digest); token64_e(token, sizeof token, digest, sizeof digest); token[(sizeof token) - 1] = '\0'; nu->nu_tag = su_strdup(nh->nh_home, token); } if (!nu->nu_requested || !nu->nu_tag) return 0; /* Check if SUBSCRIBE had matching suppression */ for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) if (sr->sr_usage == cr->cr_usage && sr->sr_method == sip_method_subscribe) break; if (sr) { sip_t const *sip = sr->sr_request.sip; sip_suppress_body_if_match_t *sbim; sip_suppress_notify_if_match_t *snim; if (cr->cr_usage->du_ready) { snim = sip_suppress_notify_if_match(sip); if (snim && su_casematch(snim->snim_tag, nu->nu_tag)) { if (nu->nu_requested > nu->nu_expires) nu->nu_expires = nu->nu_requested; nu->nu_requested = 0; return nua_client_return(cr, 202, "NOTIFY Suppressed", msg); } } sbim = sip_suppress_body_if_match(sip); if (sbim && su_casematch(sbim->sbim_tag, nu->nu_tag)) nu->nu_no_body = 1; } #endif return 0; }
static int nua_notify_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { nua_dialog_usage_t *du = cr->cr_usage; struct notifier_usage *nu = nua_dialog_usage_private(du); su_home_t *home = msg_home(msg); sip_time_t now = sip_now(); sip_subscription_state_t *ss = sip->sip_subscription_state; char const *expires; if (du == NULL) /* Subscription has been terminated */ return nua_client_return(cr, SIP_481_NO_TRANSACTION, msg); assert(du && nu); if (du && nua_client_bind(cr, du) < 0) return -1; if (nu->nu_requested) nu->nu_expires = nu->nu_requested; nu->nu_requested = 0; if (nu->nu_expires <= now || du->du_shutdown) { nu->nu_substate = nua_substate_terminated; expires = "expires=0"; } else { expires = su_sprintf(home, "expires=%lu", nu->nu_expires - now); } if (ss == NULL || nua_substate_make(ss->ss_substate) != nu->nu_substate) { if (nu->nu_substate == nua_substate_terminated) expires = nu->nu_expires > now ? "reason=noresource" : "reason=timeout"; ss = sip_subscription_state_format(home, "%s;%s", nua_substate_name(nu->nu_substate), expires); msg_header_insert(msg, (void *)sip, (void *)ss); } else if (nu->nu_substate != nua_substate_terminated) { msg_header_replace_param(home, ss->ss_common, expires); } #if SU_HAVE_EXPERIMENTAL if (nu->nu_tag && !sip->sip_etag) msg_header_add_make(msg, (void *)sip, sip_etag_class, nu->nu_tag); if (nu->nu_no_body) { nu->nu_no_body = 0; msg_header_remove(msg, (void *)sip, (void *)sip->sip_payload); msg_header_remove(msg, (void *)sip, (void *)sip->sip_content_length); } #endif if (nu->nu_substate == nua_substate_terminated) nua_client_set_terminating(cr, 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; nua_client_set_terminating(cr, 0); break; } } } if (du->du_event && !sip->sip_event) sip_add_dup(cr->cr_msg, sip, (sip_header_t *)du->du_event); return nua_base_client_request(cr, msg, sip, tags); }
int nua_notify_server_report(nua_server_request_t *sr, tagi_t const *tags) { nua_handle_t *nh = sr->sr_owner; nua_dialog_usage_t *du = sr->sr_usage; struct event_usage *eu = nua_dialog_usage_private(du); sip_t const *sip = sr->sr_request.sip; enum nua_substate substate = nua_substate_terminated; sip_time_t delta = SIP_TIME_MAX; sip_event_t const *o = sip->sip_event; int retry = -1; int retval; if (eu) { sip_subscription_state_t *subs = sip->sip_subscription_state; substate = eu->eu_substate; if (substate == nua_substate_active || substate == nua_substate_pending) { if (subs && subs->ss_expires) { sip_time_t now = sip_now(); sip_time_t delta0 = strtoul(subs->ss_expires, NULL, 10); if (now + delta0 <= eu->eu_expires) delta = delta0; } } else if (substate == nua_substate_embryonic) { if (subs && subs->ss_reason) { if (su_casematch(subs->ss_reason, "deactivated")) { retry = 0; /* retry immediately */ } else if (su_casematch(subs->ss_reason, "probation")) { retry = 30; if (subs->ss_retry_after) retry = strtoul(subs->ss_retry_after, NULL, 10); if (retry > 3600) retry = 3600; } } } else if (substate == nua_substate_terminated) { sr->sr_terminating = 1; } } retval = nua_base_server_treport(sr, /* can destroy sr */ NUTAG_SUBSTATE(substate), SIPTAG_EVENT(o), TAG_NEXT(tags)); if (retval != 1 || du == NULL) return retval; if (eu->eu_unsolicited) { /* Xyzzy */; } else if (retry >= 0) { /* Try to subscribe again */ /* XXX - this needs through testing */ nua_dialog_remove(nh, nh->nh_ds, du); /* tear down */ nua_dialog_usage_set_refresh_range(du, retry, retry + 5); } else { if (delta < SIP_TIME_MAX) { nua_dialog_usage_set_refresh(du, delta); eu->eu_expires = du->du_refquested + delta; } } return retval; }
int nua_notify_server_preprocess(nua_server_request_t *sr) { nua_dialog_state_t *ds = sr->sr_owner->nh_ds; nua_dialog_usage_t *du; struct event_usage *eu; sip_t const *sip = sr->sr_request.sip; sip_event_t *o = sip->sip_event; enum nua_substate substate = nua_substate_terminated; sip_subscription_state_t *subs = sip->sip_subscription_state; char const *what = "", *reason = NULL; int solicited = 1; du = nua_dialog_usage_get(ds, nua_subscribe_usage, o); if (du == NULL) { if (!sip_is_allowed(NH_PGET(sr->sr_owner, appl_method), SIP_METHOD_NOTIFY)) return SR_STATUS(sr, 481, "Subscription Does Not Exist"); solicited = 0; /* Let application to handle unsolicited NOTIFY */ du = nua_dialog_usage_add(sr->sr_owner, ds, nua_subscribe_usage, o); if (!du) return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR); } sr->sr_usage = du; eu = nua_dialog_usage_private(du); assert(eu); eu->eu_notified++; if (!o || !o->o_id) eu->eu_no_id = 1; if (subs == NULL) { /* Compatibility */ unsigned long delta = eu->eu_delta; if (sip->sip_expires) delta = sip->sip_expires->ex_delta; if (delta == 0) substate = nua_substate_terminated, what = "terminated"; else substate = nua_substate_active, what = "active"; } else if (su_casematch(subs->ss_substate, what = "terminated")) { substate = nua_substate_terminated; reason = subs->ss_reason; if (su_casematch(reason, "deactivated") || su_casematch(reason, "probation")) substate = nua_substate_embryonic; } else if (su_casematch(subs->ss_substate, what = "pending")) { substate = nua_substate_pending; } else /* if (su_casematch(subs->ss_substate, what = "active")) */ { /* Any extended state is considered as active */ what = subs->ss_substate; substate = nua_substate_active; } eu->eu_substate = substate; if (!solicited) eu->eu_unsolicited = 1; SU_DEBUG_5(("nua(%p): %s: %s (%s)\n", (void *)sr->sr_owner, "nua_notify_server_preprocess", what, reason ? reason : "")); if (solicited) return SR_STATUS1(sr, SIP_200_OK); return 0; }
static int nua_subscribe_client_response(nua_client_request_t *cr, int status, char const *phrase, sip_t const *sip) { nua_handle_t *nh = cr->cr_owner; nua_dialog_usage_t *du = cr->cr_usage; struct event_usage *eu = nua_dialog_usage_private(du); enum nua_substate substate; if (eu == NULL || cr->cr_terminated) substate = nua_substate_terminated; else if (status >= 300) substate = eu->eu_substate; else { int win_messenger_enable = NH_PGET(nh, win_messenger_enable); sip_time_t delta, now = sip_now(); du->du_ready = 1; if (eu->eu_substate != nua_substate_terminated) /* If there is no @Expires header, use default value stored in eu_delta */ delta = sip_contact_expires(NULL, sip->sip_expires, sip->sip_date, eu->eu_delta, now); else delta = 0; if (delta > eu->eu_delta) delta = eu->eu_delta; 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, ""); } if (delta > 0) { nua_dialog_usage_set_refresh(du, delta); eu->eu_expires = du->du_refquested + delta; } else { if (eu->eu_substate == nua_substate_terminated) { if (!eu->eu_notified) eu->eu_substate = nua_substate_embryonic; } if (eu->eu_substate != nua_substate_terminated) { /* Wait 32 seconds for NOTIFY. */ delta = 64 * NTA_SIP_T1 / 1000; eu->eu_final_wait = 1; if (!eu->eu_notified && win_messenger_enable) delta = 4 * 60; /* Wait 4 minutes for NOTIFY from Messenger */ nua_dialog_usage_set_refresh_range(du, delta, delta); } else { nua_dialog_usage_reset_refresh(du); } eu->eu_expires = du->du_refquested; } substate = eu->eu_substate; if (substate == nua_substate_terminated) /* let nua_base_client_tresponse to remove usage */ cr->cr_terminated = 1; } return nua_base_client_tresponse(cr, status, phrase, sip, NUTAG_SUBSTATE(substate), SIPTAG_EVENT(du ? du->du_event : NULL), TAG_END()); }