Commit e3e2342b authored by Pekka Pessi's avatar Pekka Pessi
Browse files

nua_register.c: including NUTAG_M_FEATURES() value in contacts generated for...

nua_register.c: including NUTAG_M_FEATURES() value in contacts generated for REGISTER without outbound.

Added tests in test_register.c. Also fixed 423 Min-Expires negotiation
for expires parameters included in Contacts.

darcs-hash:20070209011559-65a35-cf6ddb2c30c2adad5ac2259c7c8ca014496508b1.gz
parent 1d5b61a6
......@@ -119,8 +119,11 @@ struct register_usage {
nua_registration_t *nr_next, **nr_prev, **nr_list; /* Doubly linked list and its head */
sip_from_t *nr_aor; /**< AoR for this registration, NULL if none */
sip_contact_t *nr_contact; /**< Our Contact */
sip_contact_t nr_dcontact[1]; /**< Contact in dialog */
sip_via_t *nr_via; /**< Corresponding Via headers */
unsigned long nr_min_expires; /**< Value from 423 negotiation */
/** Status of registration */
unsigned nr_ready:1;
/** Kind of registration.
......@@ -222,14 +225,13 @@ static void nua_register_usage_peer_info(nua_dialog_usage_t *du,
/* Interface towards outbound_t */
sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh,
su_home_t *home,
int in_dialog,
char const *extra_username,
sip_via_t const *v,
char const *transport,
char const *m_param,
...);
static int nua_stack_outbound_features(nua_handle_t *nh, outbound_t *ob);
static int nua_stack_outbound_refresh(nua_handle_t *,
outbound_t *ob);
......@@ -659,7 +661,6 @@ static int nua_register_client_init(nua_client_request_t *cr,
NH_PISSET(nh, keepalive_stream)
? NH_PGET(nh, keepalive_stream)
: NH_PGET(nh, keepalive));
nua_stack_outbound_features(nh, ob);
if (outbound_set_contact(ob, sip->sip_contact, nr->nr_via, unreg) < 0)
return nua_client_return(cr, 900, "Cannot set outbound contact", msg);
......@@ -676,7 +677,8 @@ int nua_register_client_request(nua_client_request_t *cr,
nua_handle_t *nh = cr->cr_owner;
nua_dialog_usage_t *du = cr->cr_usage;
nua_registration_t *nr;
sip_contact_t *m = NULL, *contacts = sip->sip_contact, *previous = NULL;
sip_contact_t *m, *contacts = sip->sip_contact;
char const *min_expires = NULL;
int unreg;
(void)nh;
......@@ -698,48 +700,58 @@ int nua_register_client_request(nua_client_request_t *cr,
if (m == NULL)
nua_client_terminating(cr);
}
if (cr->cr_terminating) {
/* Remove the expire parameters from contacts */
for (m = sip->sip_contact; m; m = m->m_next) {
if (m->m_url->url_type == url_any) {
/* If there is a '*' in contact list, remove everything else */
while (m != sip->sip_contact)
sip_header_remove(msg, sip, (sip_header_t *)sip->sip_contact);
while (m->m_next)
sip_header_remove(msg, sip, (sip_header_t *)m->m_next);
contacts = m;
break;
}
msg_header_remove_param(m->m_common, "expires");
}
}
}
unreg = cr->cr_terminating;
if (du) {
nr = nua_dialog_usage_private(du);
nr = nua_dialog_usage_private(du);
if (nr) {
if (nr->nr_ob) {
outbound_stop_keepalive(nr->nr_ob);
outbound_start_registering(nr->nr_ob);
}
if (nr->nr_by_stack) {
m = nr->nr_contact;
sip_contact_t *m = nr->nr_contact, *previous = NULL;
outbound_get_contacts(nr->nr_ob, &m, &previous);
sip_add_dup(msg, sip, (sip_header_t *)m);
/* previous is an outdated contact generated by stack
* and it is now unregistered */
if (previous)
sip_add_dup(msg, sip, (sip_header_t *)previous);
}
}
for (m = sip->sip_contact; m; m = m->m_next) {
if (m->m_url->url_type == url_any) {
/* If there is a '*' in contact list, remove everything else */
while (m != sip->sip_contact)
sip_header_remove(msg, sip, (sip_header_t *)sip->sip_contact);
while (m->m_next)
sip_header_remove(msg, sip, (sip_header_t *)m->m_next);
contacts = m;
break;
}
if (!m->m_expires)
continue;
if (unreg) {
/* Remove the expire parameters from contacts */
msg_header_remove_param(m->m_common, "expires");
}
else if (nr && nr->nr_min_expires &&
strtoul(m->m_expires, 0, 10) < nr->nr_min_expires) {
if (min_expires == NULL)
min_expires = su_sprintf(msg_home(msg), "expires=%lu",
nr->nr_min_expires);
msg_header_replace_param(msg_home(msg), m->m_common, min_expires);
}
}
return nua_base_client_trequest(cr, msg, sip,
/* m is stack-generated contact */
SIPTAG_CONTACT(m),
/*
* previous is outdated stack-generated contact
* which is now unregistered
*/
SIPTAG_CONTACT(previous),
TAG_IF(unreg, SIPTAG_EXPIRES_STR("0")),
#if 0
TAG_IF(unreg, NTATAG_SIGCOMP_CLOSE(1)),
......@@ -772,6 +784,11 @@ static int nua_register_client_check_restart(nua_client_request_t *cr,
/* XXX - report an error? */;
}
if (nr && status == 423) {
if (sip->sip_min_expires)
nr->nr_min_expires = sip->sip_min_expires->me_delta;
}
/* Check for status-specific reasons to retry */
if (nua_base_client_check_restart(cr, status, phrase, sip))
return 1;
......@@ -1262,11 +1279,8 @@ int nua_registration_from_via(nua_registration_t **list,
v2[1].v_next = NULL;
#if 1
contact = nua_handle_contact_by_via(nh, home, NULL, v2, protocol, NULL);
#else
contact = sip_contact_create_from_via_with_transport(home, v2, NULL, protocol);
#endif
contact = nua_handle_contact_by_via(nh, home, 0, NULL, v2, protocol, NULL);
v = sip_via_dup(home, v2);
if (!contact || !v) {
......@@ -1277,6 +1291,7 @@ int nua_registration_from_via(nua_registration_t **list,
nr->nr_ready = 1, nr->nr_default = 1, nr->nr_public = public;
nr->nr_secure = contact->m_url->url_type == url_sips;
nr->nr_contact = contact;
*nr->nr_dcontact = *contact, nr->nr_dcontact->m_params = NULL;
nr->nr_via = v;
nr->nr_ip4 = host_is_ip4_address(contact->m_url->url_host);
nr->nr_ip6 = !nr->nr_ip4 && host_is_ip6_reference(contact->m_url->url_host);
......@@ -1446,7 +1461,10 @@ sip_contact_t const *nua_registration_contact(nua_registration_t const *nr)
return m;
}
return nr->nr_contact;
if (nr->nr_contact)
return nr->nr_dcontact;
else
return NULL;
}
/** Return initial route. */
......@@ -1458,7 +1476,7 @@ sip_route_t const *nua_registration_route(nua_registration_t const *nr)
sip_contact_t const *nua_stack_get_contact(nua_registration_t const *nr)
{
nr = nua_registration_by_aor(nr, NULL, NULL, 1);
return nr ? nr->nr_contact : NULL;
return nr && nr->nr_contact ? nr->nr_dcontact : NULL;
}
/** Add a Contact (and Route) header to request */
......@@ -1541,7 +1559,7 @@ int nua_registration_add_contact_and_route(nua_handle_t *nh,
if (!m)
return -1;
if (str0cmp(m_params, u->url_params) == 0)
if (u->url_params && m_params && strstr(u->url_params, m_params) == 0)
m_params = NULL;
m = sip_contact_format(msg_home(msg),
......@@ -1648,7 +1666,7 @@ int nua_registration_set_contact(nua_handle_t *nh,
if (nr0 && nr0->nr_via) {
char const *tport = nr0->nr_via->v_next ? NULL : nr0->nr_via->v_protocol;
m = nua_handle_contact_by_via(nh, nh->nh_home,
m = nua_handle_contact_by_via(nh, nh->nh_home, 0,
NULL, nr0->nr_via, tport, NULL);
}
}
......@@ -1657,6 +1675,7 @@ int nua_registration_set_contact(nua_handle_t *nh,
return -1;
nr->nr_contact = m;
*nr->nr_dcontact = *m, nr->nr_dcontact->m_params = NULL;
nr->nr_ip4 = host_is_ip4_address(m->m_url->url_host);
nr->nr_ip6 = !nr->nr_ip4 && host_is_ip6_reference(m->m_url->url_host);
nr->nr_by_stack = !application_contact;
......@@ -1785,6 +1804,7 @@ static int nua_stack_outbound_credentials(nua_handle_t *nh,
/** @internal Generate a @Contact header. */
sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh,
su_home_t *home,
int in_dialog,
char const *extra_username,
sip_via_t const *v,
char const *transport,
......@@ -1868,7 +1888,7 @@ sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh,
su_strlst_append(l, ";comp="), su_strlst_append(l, comp);
s = NH_PGET(nh, m_params);
if (s)
su_strlst_append(l, s[0] == ';' ? "" : ";"), su_strlst_append(l, s);
s[0] == ';' ? "" : su_strlst_append(l, ";"), su_strlst_append(l, s);
su_strlst_append(l, ">");
va_start(va, m_param);
......@@ -1882,81 +1902,42 @@ sip_contact_t *nua_handle_contact_by_via(nua_handle_t *nh,
va_end(va);
m = sip_contact_make(home, su_strlst_join(l, su_strlst_home(l), ""));
su_strlst_destroy(l);
return m;
}
/** @internal Return a string describing our features. */
static char *nua_handle_features(nua_handle_t *nh)
{
char *retval = NULL;
su_strlst_t *l = su_strlst_create(NULL);
su_home_t *home = su_strlst_home(l);
if (!l)
return NULL;
if (NH_PGET(nh, m_features)) {
char const *m_features = NH_PGET(nh, m_features);
if (m_features[0] != ';')
su_strlst_append(l, ";");
su_strlst_append(l, m_features);
}
if (NH_PGET(nh, callee_caps)) {
sip_allow_t const *allow = NH_PGET(nh, allow);
if (allow) {
/* Skip ";" if this is first one */
su_strlst_append(l, ";methods=\"" + (su_strlst_len(l) == 0));
if (allow->k_items) {
size_t i;
for (i = 0; allow->k_items[i]; i++) {
su_strlst_append(l, allow->k_items[i]);
if (allow->k_items[i + 1])
su_strlst_append(l, ",");
}
if (!in_dialog) {
s = NH_PGET(nh, m_features);
if (s)
s[0] == ';' ? "" : su_strlst_append(l, ";"), su_strlst_append(l, s);
if (NH_PGET(nh, callee_caps)) {
sip_allow_t const *allow = NH_PGET(nh, allow);
if (allow) {
su_strlst_append(l, ";methods=\"");
if (allow->k_items) {
size_t i;
for (i = 0; allow->k_items[i]; i++) {
su_strlst_append(l, allow->k_items[i]);
if (allow->k_items[i + 1])
su_strlst_append(l, ",");
}
}
su_strlst_append(l, "\"");
}
su_strlst_append(l, "\"");
}
if (nh->nh_soa) {
char **media = soa_media_features(nh->nh_soa, 0, home);
while (*media) {
if (su_strlst_len(l))
su_strlst_append(l, ";");
su_strlst_append(l, *media++);
if (nh->nh_soa) {
char **media = soa_media_features(nh->nh_soa, 0, home);
while (*media) {
if (su_strlst_len(l))
su_strlst_append(l, ";");
su_strlst_append(l, *media++);
}
}
}
}
if (su_strlst_len(l))
retval = su_strlst_join(l, nh->nh_home, "");
m = sip_contact_make(home, su_strlst_join(l, su_strlst_home(l), ""));
su_strlst_destroy(l);
return retval;
}
static int nua_stack_outbound_features(nua_handle_t *nh, outbound_t *ob)
{
char *features;
int retval;
if (!nh)
return -1;
if (!ob)
return 0;
features = nua_handle_features(nh);
retval = outbound_set_features(ob, features);
su_free(nh->nh_home, features);
return retval;
return m;
}
......@@ -117,7 +117,6 @@ struct outbound {
char const *ob_instance; /**< Our instance ID */
int32_t ob_reg_id; /**< Flow-id */
char const *ob_features; /**< Feature parameters for rcontact */
sip_contact_t *ob_rcontact; /**< Our contact */
sip_contact_t *ob_dcontact; /**< Contact for dialogs */
sip_contact_t *ob_previous; /**< Stale contact */
......@@ -327,20 +326,6 @@ int outbound_set_options(outbound_t *ob,
return 0;
}
/** Set the feature string (added to the Contact header when registering). */
int outbound_set_features(outbound_t *ob, char *features)
{
char *old_features = (char *)ob->ob_features;
char *new_features = su_strdup(ob->ob_home, features);
if (features && !new_features)
return -1;
ob->ob_features = new_features;
su_free(ob->ob_home, old_features);
return 0;
}
/* ---------------------------------------------------------------------- */
/** Obtain contacts for REGISTER */
......@@ -698,21 +683,32 @@ static int create_keepalive_message(outbound_t *ob, sip_t const *regsip)
{
msg_t *msg = nta_msg_create(ob->ob_nta, MSG_FLG_COMPACT), *previous;
sip_t *osip = sip_object(msg);
sip_accept_contact_t *ac;
char const *p1 = ob->ob_instance;
char const *p2 = ob->ob_features;
sip_contact_t *m = ob->ob_rcontact;
unsigned d = ob->ob_keepalive.interval;
assert(regsip); assert(regsip->sip_request);
if (p1 || p2) {
ac = sip_accept_contact_format(msg_home(msg), "*;require;explicit;%s%s%s",
p1 ? p1 : "",
p2 && p2 ? ";" : "",
p2 ? p2 : "");
msg_header_insert(msg, NULL, (void *)ac);
if (m && m->m_params) {
sip_accept_contact_t *ac;
size_t i;
int features = 0;
ac = sip_accept_contact_make(msg_home(msg), "*;require;explicit");
for (i = 0; m->m_params[i]; i++) {
char const *s = m->m_params[i];
if (!sip_is_callerpref(s))
continue;
features++;
s = su_strdup(msg_home(msg), s);
msg_header_add_param(msg_home(msg), ac->cp_common, s);
}
if (features)
msg_header_insert(msg, NULL, (void *)ac);
else
msg_header_free(msg_home(msg), (void *)ac);
}
if (0 >
......@@ -1008,46 +1004,16 @@ int outbound_contacts_from_via(outbound_t *ob, sip_via_t const *via)
v = v0; *v0 = *via; v0->v_next = NULL;
dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 1,
NULL, v, v->v_protocol, NULL);
if (ob->ob_instance && ob->ob_reg_id != 0)
snprintf(reg_id_param, sizeof reg_id_param, ";reg-id=%u", ob->ob_reg_id);
rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 0,
NULL, v, v->v_protocol,
ob->ob_features ? ob->ob_features : "",
ob->ob_instance, reg_id_param, NULL);
#if 0
char *uri;
/* uri contains < > */
uri = sip_contact_string_from_via(NULL, via, NULL, v->v_protocol);
dcontact = sip_contact_make(home, uri);
if (ob->ob_instance) {
char reg_id[20];
if (ob->ob_instance && ob->ob_reg_id)
snprintf(reg_id, sizeof reg_id, ";reg-id=%u", ob->ob_reg_id);
else
strcpy(reg_id, "");
rcontact = sip_contact_format(home, "%s;%s%s%s%s",
uri, ob->ob_instance, reg_id,
ob->ob_features ? ";" : "",
ob->ob_features ? ob->ob_features : "");
}
else if (ob->ob_features)
rcontact = sip_contact_format(home, "%s;%s", uri, ob->ob_features);
else
rcontact = dcontact;
free(uri);
#endif
v = sip_via_dup(home, v);
if (!rcontact || !dcontact || !v) {
......@@ -1139,7 +1105,7 @@ int outbound_set_contact(outbound_t *ob,
char const *tport = !v->v_next ? v->v_protocol : NULL;
char reg_id_param[20];
dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
dcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 1,
NULL, v, tport, NULL);
if (!dcontact)
return -1;
......@@ -1147,9 +1113,8 @@ int outbound_set_contact(outbound_t *ob,
if (ob->ob_instance && ob->ob_reg_id != 0)
snprintf(reg_id_param, sizeof reg_id_param, ";reg-id=%u", ob->ob_reg_id);
rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home,
rcontact = ob->ob_oo->oo_contact(ob->ob_owner, home, 0,
NULL, v, v->v_protocol,
ob->ob_features ? ob->ob_features : "",
ob->ob_instance, reg_id_param, NULL);
if (!rcontact)
return -1;
......
......@@ -70,8 +70,6 @@ int outbound_set_options(outbound_t *ob,
unsigned dgram_interval,
unsigned stream_interval);
int outbound_set_features(outbound_t *ob, char *features);
int outbound_get_contacts(outbound_t *ob,
sip_contact_t **return_current_contact,
sip_contact_t **return_previous_contact);
......@@ -118,6 +116,7 @@ struct outbound_owner_vtable
int oo_size;
sip_contact_t *(*oo_contact)(outbound_owner_t *,
su_home_t *home,
int used_in_dialog,
char const *extra_username,
sip_via_t const *v,
char const *transport,
......
......@@ -133,6 +133,8 @@ int test_register_to_proxy(struct context *ctx)
NUTAG_KEEPALIVE(1000),
NUTAG_M_DISPLAY("A&A"),
NUTAG_M_USERNAME("a"),
NUTAG_M_PARAMS("foo=bar"),
NUTAG_M_FEATURES("q=0.9"),
SIPTAG_CSEQ(cseq),
TAG_END());
run_a_until(ctx, -1, save_until_final_response);
......@@ -161,6 +163,8 @@ int test_register_to_proxy(struct context *ctx)
/* VC does not dig \" with TEST_S() */
TEST_S(sip->sip_contact->m_display, expect_m_display); }
TEST_S(sip->sip_contact->m_url->url_user, "a");
TEST_1(strstr(sip->sip_contact->m_url->url_params, "foo=bar"));
TEST_S(sip->sip_contact->m_q, "0.9");
TEST(sip->sip_cseq->cs_seq, 14);
if (ctx->nat) {
......@@ -222,8 +226,12 @@ int test_register_to_proxy(struct context *ctx)
TEST_1(c_reg->nh = nua_handle(c->nua, c_reg, TAG_END()));
REGISTER(c, c_reg, c_reg->nh, SIPTAG_TO(c->to),
NUTAG_OUTBOUND(NULL),
NUTAG_M_DISPLAY("C"),
NUTAG_M_USERNAME("c"),
NUTAG_M_PARAMS("c=1"),
NUTAG_M_FEATURES("q=0.987;expires=5"),
NUTAG_CALLEE_CAPS(1),
SIPTAG_EXPIRES_STR("5"), /* Test 423 negotiation */
TAG_END());
run_abc_until(ctx, -1, save_events, -1, save_events,
......@@ -254,6 +262,9 @@ int test_register_to_proxy(struct context *ctx)
TEST_1(sip->sip_contact);
TEST_S(sip->sip_contact->m_display, "C");
TEST_S(sip->sip_contact->m_url->url_user, "c");
TEST_1(strstr(sip->sip_contact->m_url->url_params, "c=1"));
TEST_S(sip->sip_contact->m_q, "0.987");
TEST_1(msg_header_find_param(sip->sip_contact->m_common, "methods="));
TEST_1(!e->next);
free_events_in_list(ctx, c->events);
......@@ -688,6 +699,7 @@ int test_unregister(struct context *ctx)
UNREGISTER(c, c->call, c->call->nh, SIPTAG_TO(c->to),
NUTAG_M_DISPLAY("C"),
NUTAG_M_USERNAME("c"),
NUTAG_M_PARAMS("c=1"),
TAG_END());
run_c_until(ctx, -1, save_until_final_response);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment