/** Send request. * * @retval 0 success * @retval -1 if error occurred, but event has not been sent, * and caller has to destroy request message @ msg * @retval -2 if error occurred, event has not been sent * @retval >=1 if error event has been sent */ int nua_base_client_request(nua_client_request_t *cr, msg_t *msg, sip_t *sip, tagi_t const *tags) { nua_handle_t *nh = cr->cr_owner; int proxy_is_set = NH_PISSET(nh, proxy); url_string_t * proxy = NH_PGET(nh, proxy); if (nh->nh_auth) { if (cr->cr_challenged || NH_PGET(nh, auth_cache) == nua_auth_cache_dialog) { if (auc_authorize(&nh->nh_auth, msg, sip) < 0) return nua_client_return(cr, 900, "Cannot add credentials", msg); } } cr->cr_seq = sip->sip_cseq->cs_seq; /* Save last sequence number */ assert(cr->cr_orq == NULL); cr->cr_orq = nta_outgoing_mcreate(nh->nh_nua->nua_nta, nua_client_orq_response, nua_client_request_ref(cr), NULL, msg, TAG_IF(proxy_is_set, NTATAG_DEFAULT_PROXY(proxy)), TAG_NEXT(tags)); if (cr->cr_orq == NULL) { nua_client_request_unref(cr); return -1; } return 0; }
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); }
/**Initialize client request for sending. * * This function is called when the request is taken from queue and sent. * * @retval 0 if request is pending * @retval >=1 if error event has been sent */ static int nua_client_init_request0(nua_client_request_t *cr) { nua_handle_t *nh = cr->cr_owner; nua_t *nua = nh->nh_nua; nua_dialog_state_t *ds = nh->nh_ds; msg_t *msg = NULL; sip_t *sip; url_string_t const *url = NULL; tagi_t const *t; int has_contact = 0; int error = 0; if (!cr->cr_method_name) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL); if (cr->cr_msg) return nua_client_request_try(cr); cr->cr_answer_recv = 0, cr->cr_offer_sent = 0; cr->cr_offer_recv = 0, cr->cr_answer_sent = 0; cr->cr_terminated = 0, cr->cr_graceful = 0; nua_stack_init_handle(nua, nh, cr->cr_tags); if (cr->cr_method == sip_method_cancel) { if (cr->cr_methods->crm_init) { error = cr->cr_methods->crm_init(cr, NULL, NULL, cr->cr_tags); if (error) return error; } if (cr->cr_methods->crm_send) return cr->cr_methods->crm_send(cr, NULL, NULL, cr->cr_tags); else return nua_base_client_request(cr, NULL, NULL, cr->cr_tags); } if (!cr->cr_methods->crm_template || cr->cr_methods->crm_template(cr, &msg, cr->cr_tags) == 0) msg = nua_client_request_template(cr); sip = sip_object(msg); if (!sip) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (nh->nh_tags) { for (t = nh->nh_tags; t; t = t_next(t)) { if (t->t_tag == siptag_contact || t->t_tag == siptag_contact_str) has_contact = 1; else if (t->t_tag == nutag_url) url = (url_string_t const *)t->t_value; } } /**@par Populating SIP Request Message with Tagged Arguments * * The tagged arguments can be used to pass values for any SIP headers * to the stack. When the INVITE message (or any other SIP message) is * created, the tagged values saved with nua_handle() are used first, * next the tagged values given with the operation (nua_invite()) are * added. * * When multiple tags for the same header are specified, the behaviour * depends on the header type. If only a single header field can be * included in a SIP message, the latest non-NULL value is used, e.g., * @Subject. However, if the SIP header can consist of multiple lines or * header fields separated by comma, e.g., @Accept, all the tagged * values are concatenated. * * However, if a tag value is #SIP_NONE (-1 casted as a void pointer), * the values from previous tags are ignored. */ for (t = cr->cr_tags; t; t = t_next(t)) { if (t->t_tag == siptag_contact || t->t_tag == siptag_contact_str) has_contact = 1; else if (t->t_tag == nutag_url) url = (url_string_t const *)t->t_value; else if (t->t_tag == nutag_dialog) { cr->cr_dialog = t->t_value > 1; cr->cr_contactize = t->t_value >= 1; } else if (t->t_tag == nutag_auth && t->t_value) { /* XXX ignoring errors */ if (nh->nh_auth) auc_credentials(&nh->nh_auth, nh->nh_home, (char *)t->t_value); } } if (cr->cr_method == sip_method_register && url == NULL) url = (url_string_t const *)NH_PGET(nh, registrar); if ((t = cr->cr_tags)) { if (sip_add_tagis(msg, sip, &t) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); } /** * Now, the target URI for the request needs to be determined. * * For initial requests, values from tags are used. If NUTAG_URL() is * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given, * it is used as target URI. If neither is given, the complete request * line already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR() * is used. At this point, the target URI is stored in the request line, * together with method name and protocol version ("SIP/2.0"). The * initial dialog information is also created: @CallID, @CSeq headers * are generated, if they do not exist, and a tag is added to the @From * header. */ if (!ds->ds_leg) { if (ds->ds_remote_tag && ds->ds_remote_tag[0] && sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (sip->sip_from == NULL && sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (sip->sip_to == NULL && cr->cr_method == sip_method_register && sip_add_dup_as(msg, sip, sip_to_class, (sip_header_t *)sip->sip_from) < 0) { return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); } } else { if (ds->ds_route) url = NULL; } if (url && nua_client_set_target(cr, (url_t *)url) < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); cr->cr_has_contact = has_contact; if (cr->cr_methods->crm_init) { error = cr->cr_methods->crm_init(cr, msg, sip, cr->cr_tags); if (error < -1) msg = NULL; if (error < 0) return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg); if (error != 0) return error; } cr->cr_msg = msg; cr->cr_sip = sip; return nua_client_request_try(cr); }
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); }
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; }