diff --git a/modules/sca/sca_call_info.c b/modules/sca/sca_call_info.c index 7f5dd13aa3f..062f4f763bd 100644 --- a/modules/sca/sca_call_info.c +++ b/modules/sca/sca_call_info.c @@ -530,6 +530,11 @@ int sca_call_info_seize_held_call(sip_msg_t *msg, sca_call_info *call_info, int slot_idx = -1; int rc = -1; + LM_DBG( "From-AOR:%.*s To-AOR:%.*s From-URI:<%.*s> To-URI:<%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d\n", + STR_FMT(from_aor), STR_FMT(to_aor),STR_FMT(&from->uri), + STR_FMT(&to->uri), STR_FMT(contact_uri), call_info->index); + slot_idx = sca_hash_table_index_for_key(sca->appearances, from_aor); sca_hash_table_lock_index(sca->appearances, slot_idx); @@ -729,8 +734,8 @@ static int sca_call_info_uri_update(str *aor, sca_call_info *call_info, } dialog.id.s = dlg_buf; - if (sca_dialog_build_from_tags(&dialog, sizeof(dlg_buf), call_id, to_tag, - from_tag) < 0) { + if (sca_dialog_build_from_tags(&dialog, sizeof(dlg_buf), call_id, from_tag, + to_tag) < 0) { LM_ERR("sca_call_info_uri_update: Failed to build dialog from tags\n"); return (-1); } @@ -803,6 +808,10 @@ static int sca_call_info_is_line_seize_reinvite(sip_msg_t *msg, str ruri_aor; int state; + LM_DBG("For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Call-Info: appearance-index=%d\n", + STR_FMT(from_aor), STR_FMT(to_aor), STR_FMT(&from->uri), + STR_FMT(&to->uri), call_info->index); // a handset in an SCA group is attempting to seize a held line if: // the RURI, From URI and To URI are identical; @@ -834,6 +843,8 @@ static int sca_call_info_is_line_seize_reinvite(sip_msg_t *msg, STR_FMT(to_aor), STR_FMT(from_aor), call_info->index); return (0); } + LM_DBG("reINVITE to %.*s from %.*s appearance-index %d (seizing held line)\n", + STR_FMT(to_aor), STR_FMT(from_aor), call_info->index); return (1); } @@ -934,6 +945,11 @@ int sca_call_info_invite_request_handler(sip_msg_t *msg, int state = SCA_APPEARANCE_STATE_UNKNOWN; int rc = -1; + LM_DBG("For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d\n", + STR_FMT(from_aor), STR_FMT(to_aor),STR_FMT(&from->uri), STR_FMT(&to->uri), + STR_FMT(contact_uri), call_info->index); + // if we get here, one of the legs is an SCA endpoint. we want to know // when the e2e ACK comes in so we can notify other members of the group. if (sca->tm_api->register_tmcb(msg, NULL, TMCB_E2EACK_IN, @@ -1016,6 +1032,11 @@ int sca_call_info_invite_reply_18x_handler(sip_msg_t *msg, int rc = -1; int notify = 0; + LM_DBG("For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d", + STR_FMT(from_aor), STR_FMT(to_aor),STR_FMT(&from->uri), STR_FMT(&to->uri), + STR_FMT(contact_uri), call_info->index); + switch (msg->REPLY_STATUS) { case 180: case 183: @@ -1169,6 +1190,11 @@ static int sca_call_info_invite_reply_200_handler(sip_msg_t *msg, int slot_idx = -1; int rc = -1; + LM_DBG("For From-AOR %.*s To-AOR: %.*s: From: <%.*s> To: <%.*s> " + "Contact: <%.*s> Call-Info: appearance-index=%d\n", + STR_FMT(from_aor), STR_FMT(to_aor),STR_FMT(&from->uri), STR_FMT(&to->uri), + STR_FMT(contact_uri), call_info->index); + if (SCA_CALL_INFO_IS_SHARED_CALLEE(call_info)) { rc = sca_call_info_uri_update(to_aor, call_info, from, to, contact_uri, &msg->callid->body); @@ -1940,6 +1966,11 @@ int sca_call_info_update(sip_msg_t *msg, char *p1, char *p2) goto done; } + LM_DBG( "Calling Dispatch Id: %d handler with From-AOR: %.*s To-AOR: %.*s " + "From-URI: <%.*s> To-URI: <%.*s> Contact-URI: <%.*s>\n", + i, STR_FMT(&from_aor), STR_FMT(&to_aor),STR_FMT(&from->uri), + STR_FMT(&to->uri), STR_FMT(&contact_uri)); + rc = call_info_dispatch[i].handler(msg, &call_info, from, to, &from_aor, &to_aor, &contact_uri); if (rc < 0) { diff --git a/modules/sca/sca_dialog.c b/modules/sca/sca_dialog.c index 5fc73b8ba37..a5e692404c7 100644 --- a/modules/sca/sca_dialog.c +++ b/modules/sca/sca_dialog.c @@ -32,6 +32,9 @@ int sca_dialog_build_from_tags(sca_dialog *dialog, int maxlen, str *call_id, assert(call_id != NULL); assert(from_tag != NULL); + LM_DBG( "From-Tag: %.*s To-Tag: %.*s CallId: %.*s\n", + STR_FMT(from_tag), STR_FMT(to_tag), STR_FMT(call_id)); + len = call_id->len + from_tag->len; if (!SCA_STR_EMPTY(to_tag)) { len += to_tag->len; @@ -74,6 +77,8 @@ int sca_dialog_create_replaces_header(sca_dialog *dlg, str *replaces_hdr) assert(replaces_hdr != NULL); + LM_DBG( "Called\n" ); + if (SCA_STR_EMPTY(&dlg->call_id) || SCA_STR_EMPTY(&dlg->from_tag) || SCA_STR_EMPTY(&dlg->to_tag)) { LM_ERR("sca_dialog_create_replaces_header: dialog %.*s is not a " diff --git a/modules/sca/sca_notify.c b/modules/sca/sca_notify.c index b4d5315f15b..daa4e0c2b72 100644 --- a/modules/sca/sca_notify.c +++ b/modules/sca/sca_notify.c @@ -22,6 +22,7 @@ #include #include "sca.h" +#include "sca_appearance.h" #include "sca_call_info.h" #include "sca_event.h" #include "sca_notify.h" @@ -261,8 +262,14 @@ static int sca_notify_subscriber_internal(sca_mod *scam, sca_subscription *sub, { uac_req_t request; dlg_t *dlg = NULL; + str state_str = STR_NULL; int rc = -1; + sca_appearance_state_to_str(sub->state, &state_str); + LM_DBG("SCA: NOTIFYing subscriber '%.*s' of event '%s' with a state of '%.*s' to index '%d'\n", + STR_FMT(&sub->subscriber), sca_event_name_from_type(sub->event), + STR_FMT(&state_str), sub->index); + dlg = sca_notify_dlg_for_subscription(sub); if (dlg == NULL) { LM_ERR("Failed to create dlg_t for %s NOTIFY to %.*s\n", @@ -298,8 +305,9 @@ int sca_notify_subscriber(sca_mod *scam, sca_subscription *sub, int app_idx) str headers = STR_NULL; char hdrbuf[SCA_HEADERS_MAX_LEN]; - headers.s = hdrbuf; + LM_DBG("NOTIFYing subscriber because of a SUBSCRIPTION request\n"); + headers.s = hdrbuf; if (sca_notify_build_headers_from_info(&headers, sizeof(hdrbuf), scam, sub, app_idx) < 0) { LM_ERR("Failed to build NOTIFY headers\n"); @@ -328,8 +336,10 @@ int sca_notify_call_info_subscribers(sca_mod *scam, str *subscription_aor) assert(scam->subscriptions != NULL); assert(!SCA_STR_EMPTY(subscription_aor)); - event_name = sca_event_name_from_type(SCA_EVENT_TYPE_CALL_INFO); + LM_DBG("Notifying ALL subscribers of AOR %.*s due to a SUBSCRIBTION request\n", + STR_FMT(subscription_aor)); + event_name = sca_event_name_from_type(SCA_EVENT_TYPE_CALL_INFO); if (subscription_aor->len + strlen(event_name) >= sizeof(keybuf)) { LM_ERR("Hash key %.*s + %s is too long\n", STR_FMT(subscription_aor), event_name); diff --git a/modules/sca/sca_util.c b/modules/sca/sca_util.c index d1adaaa682f..af31cf51ede 100644 --- a/modules/sca/sca_util.c +++ b/modules/sca/sca_util.c @@ -1,446 +1,446 @@ -/* - * Copyright (C) 2012 Andrew Mortensen - * - * This file is part of the sca module for Kamailio, a free SIP server. - * - * The sca module is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version - * - * The sca module is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA. 02110-1301 USA - */ -#include "sca_common.h" - -#include - -#include "sca_util.h" - -#include "../../parser/sdp/sdp.h" -extern int log_stderr; -int sca_get_msg_method(sip_msg_t *msg) -{ - assert(msg != NULL); - - if (msg->first_line.type == SIP_REQUEST) { - return (msg->REQ_METHOD); - } - - return (sca_get_msg_cseq_method(msg)); -} - -int sca_get_msg_contact_uri(sip_msg_t *msg, str *contact_uri) -{ - contact_body_t *contact_body; - - assert(msg != NULL); - assert(contact_uri != NULL); - - if (SCA_HEADER_EMPTY(msg->contact)) { - LM_DBG("Empty Contact header\n"); - contact_uri->s = NULL; - contact_uri->len = 0; - - return (0); - } - - if (parse_contact(msg->contact) < 0) { - LM_ERR("Failed to parse Contact header: %.*s\n", - STR_FMT(&msg->contact->body)); - return (-1); - } - if ((contact_body = (contact_body_t *) msg->contact->parsed) == NULL) { - LM_ERR("Invalid Contact header: %.*s\n", STR_FMT(&msg->contact->body)); - return (-1); - } - if (contact_body->star) { - LM_ERR("Invalid Contact header: SCA Contact must not be \"*\"\n"); - return (-1); - } - if (contact_body->contacts == NULL) { - LM_ERR("Invalid Contact header: parser found no contacts\n"); - return (-1); - } - if (contact_body->contacts->next) { - LM_ERR("Invalid Contact header: Contact may only contain one URI\n"); - return (-1); - } - - contact_uri->s = contact_body->contacts->uri.s; - contact_uri->len = contact_body->contacts->uri.len; - - return (1); -} - -int sca_get_msg_cseq_number(sip_msg_t *msg) -{ - int cseq; - - assert(msg != NULL); - - if (SCA_HEADER_EMPTY(msg->cseq)) { - LM_ERR("Empty Cseq header\n"); - return (-1); - } - if (str2int(&(get_cseq(msg)->number), (unsigned int *) &cseq) != 0) { - LM_ERR("Bad Cseq header: %.*s\n", STR_FMT(&msg->cseq->body)); - return (-1); - } - - return (cseq); -} - -/* - * assumes cseq header in msg is already parsed - */ -int sca_get_msg_cseq_method(sip_msg_t *msg) -{ - assert(msg != NULL); - - if (SCA_HEADER_EMPTY(msg->cseq)) { - LM_ERR("Empty Cseq header\n"); - return (-1); - } - - return (get_cseq(msg)->method_id); -} - -int sca_get_msg_from_header(sip_msg_t *msg, struct to_body **from) -{ - struct to_body *f; - - assert(msg != NULL); - assert(from != NULL); - - if (SCA_HEADER_EMPTY(msg->from)) { - LM_ERR("Empty From header\n"); - return (-1); - } - if (parse_from_header(msg) < 0) { - LM_ERR("Bad From header\n"); - return (-1); - } - f = get_from(msg); - if (SCA_STR_EMPTY(&f->tag_value)) { - LM_ERR("Bad From header: no tag parameter\n"); - return (-1); - } - - // ensure the URI is parsed for future use - if (parse_uri(f->uri.s, f->uri.len, GET_FROM_PURI(msg)) < 0) { - LM_ERR("Failed to parse From URI %.*s\n", STR_FMT(&f->uri)); - return (-1); - } - - *from = f; - - return (0); -} - -int sca_get_msg_to_header(sip_msg_t *msg, struct to_body **to) -{ - struct to_body parsed_to; - struct to_body *t = NULL; - - assert(msg != NULL); - assert(to != NULL); - - if (SCA_HEADER_EMPTY(msg->to)) { - LM_ERR("Empty To header\n"); - return (-1); - } - t = get_to(msg); - if (t == NULL) { - parse_to(msg->to->body.s, msg->to->body.s + msg->to->body.len + 1, // end of buffer - &parsed_to); - if (parsed_to.error != PARSE_OK) { - LM_ERR("Bad To header\n"); - return (-1); - } - t = &parsed_to; - } - - // ensure the URI is parsed for future use - if (parse_uri(t->uri.s, t->uri.len, GET_TO_PURI(msg)) < 0) { - LM_ERR("Failed to parse To URI %.*s\n", STR_FMT(&t->uri)); - return (-1); - } - - *to = t; - - return (0); -} - -/* - * count characters requiring escape as defined by escape_common - */ -int sca_uri_display_escapes_count(str *display) { - int c = 0; - int i; - - if (SCA_STR_EMPTY(display)) { - return (0); - } - - for (i = 0; i < display->len; i++) { - switch (display->s[i]) { - case '\'': - case '"': - case '\\': - case '\0': - c++; - - default: - break; - } - } - - return (c); -} - -int sca_uri_extract_aor(str *uri, str *aor) -{ - char *semi; - - assert(aor != NULL); - - if (uri == NULL) { - aor->s = NULL; - aor->len = 0; - return (-1); - } - - aor->s = uri->s; - semi = memchr(uri->s, ';', uri->len); - if (semi != NULL) { - aor->len = semi - uri->s; - } else { - aor->len = uri->len; - } - - return (0); -} - -int sca_uri_build_aor(str *aor, int maxlen, str *contact_uri, str *domain_uri) -{ - char *p; - char *dp; - int len; - - assert(aor != NULL); - assert(contact_uri != NULL); - assert(domain_uri != NULL); - - if (contact_uri->len + domain_uri->len >= maxlen) { - return (-1); - } - - p = memchr(contact_uri->s, '@', contact_uri->len); - if (p == NULL) { - // no username, by definition can't be an SCA line - aor->s = NULL; - aor->len = 0; - - return (0); - } - dp = memchr(domain_uri->s, '@', domain_uri->len); - if (dp == NULL) { - // may be nameless URI - dp = memchr(domain_uri->s, ':', domain_uri->len); - if (dp == NULL) { - // bad domain URI - return (-1); - } - } - dp++; - - len = p - contact_uri->s; - memcpy(aor->s, contact_uri->s, len); - aor->s[len] = '@'; - len += 1; - aor->len = len; - - len = domain_uri->len - (dp - domain_uri->s); - memcpy(aor->s + aor->len, dp, len); - aor->len += len; - - return (aor->len); -} - -int sca_aor_create_from_info(str *aor, uri_type type, str *user, str *domain, - str *port) -{ - str scheme = STR_NULL; - int len = 0; - - assert(aor != NULL); - - uri_type_to_str(type, &scheme); - - // +1 for ':', +1 for '@' - len = scheme.len + 1 + user->len + 1 + domain->len; - if (!SCA_STR_EMPTY(port)) { - // +1 for ':' - len += 1 + port->len; - } - - aor->s = (char *) pkg_malloc(len); - if (aor->s == NULL) { - LM_ERR("sca_aor_create_from_info: pkg_malloc %d bytes failed\n", len); - return (-1); - } - - len = 0; - SCA_STR_COPY(aor, &scheme); - len += scheme.len; - - *(aor->s + len) = ':'; - aor->len++; - len++; - - SCA_STR_APPEND(aor, user); - len += user->len; - - *(aor->s + len) = '@'; - aor->len++; - len++; - - SCA_STR_APPEND(aor, domain); - len += domain->len; - - if (!SCA_STR_EMPTY(port)) { - *(aor->s + len) = ':'; - len += 1; - - SCA_STR_APPEND(aor, port); - len += port->len; - } - - return (aor->len); -} - -int sca_create_canonical_aor_for_ua(sip_msg_t *msg, str *c_aor, int ua_opts) -{ - struct to_body *tf = NULL; - sip_uri_t c_uri; - str tf_aor = STR_NULL; - str contact_uri = STR_NULL; - int rc = -1; - - assert(msg != NULL); - assert(c_aor != NULL); - - memset(c_aor, 0, sizeof(str)); - - if ((ua_opts & SCA_AOR_TYPE_AUTO)) { - if (msg->first_line.type == SIP_REQUEST) { - ua_opts = SCA_AOR_TYPE_UAC; - } else { - ua_opts = SCA_AOR_TYPE_UAS; - } - } - - if ((ua_opts & SCA_AOR_TYPE_UAC)) { - if (sca_get_msg_from_header(msg, &tf) < 0) { - LM_ERR("sca_create_canonical_aor: failed to get From header\n"); - goto done; - } - } else { - if (sca_get_msg_to_header(msg, &tf) < 0) { - LM_ERR("sca_create_canonical_aor: failed to get To header\n"); - goto done; - } - } - - if (sca_uri_extract_aor(&tf->uri, &tf_aor) < 0) { - LM_ERR("sca_create_canonical_aor: failed to extract AoR from " - "URI <%.*s>\n", STR_FMT(&tf->uri)); - goto done; - } - - memset(&c_uri, 0, sizeof(sip_uri_t)); - if ((rc = sca_get_msg_contact_uri(msg, &contact_uri)) < 0) { - LM_ERR("sca_create_canonical_aor: failed to get contact URI from " - "Contact <%.*s>\n", STR_FMT(&msg->contact->body)); - goto done; - } - if (rc > 0) { - if (parse_uri(contact_uri.s, contact_uri.len, &c_uri) < 0) { - LM_ERR("sca_create_canonical_aor: failed to parse Contact URI " - "<%.*s>\n", STR_FMT(&contact_uri)); - rc = -1; - goto done; - } - } - - if (SCA_STR_EMPTY(&c_uri.user) || - SCA_STR_EQ(&c_uri.user, &tf->parsed_uri.user)) { - // empty contact header or Contact user matches To/From AoR - c_aor->s = (char *) pkg_malloc(tf_aor.len); - c_aor->len = tf_aor.len; - memcpy(c_aor->s, tf_aor.s, tf_aor.len); - } else { - // Contact user and To/From user mismatch - if (sca_aor_create_from_info(c_aor, c_uri.type, &c_uri.user, - &tf->parsed_uri.host, &tf->parsed_uri.port) < 0) { - LM_ERR("sca_create_canonical_aor: failed to create AoR from " - "Contact <%.*s> and URI <%.*s>\n", - STR_FMT(&contact_uri), STR_FMT(&tf_aor)); - goto done; - } - } - - rc = 1; - - done: return (rc); -} - -int sca_create_canonical_aor(sip_msg_t *msg, str *c_aor) -{ - return (sca_create_canonical_aor_for_ua(msg, c_aor, SCA_AOR_TYPE_AUTO)); -} - -/* - * XXX this considers any held stream to mean the call is on hold. correct? - */ -int sca_call_is_held(sip_msg_t *msg) -{ - sdp_session_cell_t *session; - sdp_stream_cell_t *stream; - int n_sess; - int n_str; - int is_held = 0; - int rc; - - rc = parse_sdp(msg); - if (rc < 0) { - LM_ERR("sca_call_is_held: parse_sdp body failed\n"); - return (0); - } else if (rc > 0) { - LM_DBG("sca_call_is_held: parse_sdp returned %d, no SDP body\n", rc); - return (0); - } - - // Cf. modules_k/textops's exported is_audio_on_hold - for (n_sess = 0, session = get_sdp_session(msg, n_sess); session != NULL; - n_sess++, session = get_sdp_session(msg, n_sess)) { - - for (n_str = 0, stream = get_sdp_stream(msg, n_sess, n_str); - stream != NULL; - n_str++, stream = get_sdp_stream(msg, n_sess, n_str)) { - if (stream->is_on_hold) { - is_held = 1; - goto done; - } - } - } - - done: return (is_held); -} +/* + * Copyright (C) 2012 Andrew Mortensen + * + * This file is part of the sca module for Kamailio, a free SIP server. + * + * The sca module is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version + * + * The sca module is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA. 02110-1301 USA + */ +#include "sca_common.h" + +#include + +#include "sca_util.h" + +#include "../../parser/sdp/sdp.h" + +int sca_get_msg_method(sip_msg_t *msg) +{ + assert(msg != NULL); + + if (msg->first_line.type == SIP_REQUEST) { + return (msg->REQ_METHOD); + } + + return (sca_get_msg_cseq_method(msg)); +} + +int sca_get_msg_contact_uri(sip_msg_t *msg, str *contact_uri) +{ + contact_body_t *contact_body; + + assert(msg != NULL); + assert(contact_uri != NULL); + + if (SCA_HEADER_EMPTY(msg->contact)) { + LM_DBG("Empty Contact header\n"); + contact_uri->s = NULL; + contact_uri->len = 0; + + return (0); + } + + if (parse_contact(msg->contact) < 0) { + LM_ERR("Failed to parse Contact header: %.*s\n", + STR_FMT(&msg->contact->body)); + return (-1); + } + if ((contact_body = (contact_body_t *) msg->contact->parsed) == NULL) { + LM_ERR("Invalid Contact header: %.*s\n", STR_FMT(&msg->contact->body)); + return (-1); + } + if (contact_body->star) { + LM_ERR("Invalid Contact header: SCA Contact must not be \"*\"\n"); + return (-1); + } + if (contact_body->contacts == NULL) { + LM_ERR("Invalid Contact header: parser found no contacts\n"); + return (-1); + } + if (contact_body->contacts->next) { + LM_ERR("Invalid Contact header: Contact may only contain one URI\n"); + return (-1); + } + + contact_uri->s = contact_body->contacts->uri.s; + contact_uri->len = contact_body->contacts->uri.len; + + return (1); +} + +int sca_get_msg_cseq_number(sip_msg_t *msg) +{ + int cseq; + + assert(msg != NULL); + + if (SCA_HEADER_EMPTY(msg->cseq)) { + LM_ERR("Empty Cseq header\n"); + return (-1); + } + if (str2int(&(get_cseq(msg)->number), (unsigned int *) &cseq) != 0) { + LM_ERR("Bad Cseq header: %.*s\n", STR_FMT(&msg->cseq->body)); + return (-1); + } + + return (cseq); +} + +/* + * assumes cseq header in msg is already parsed + */ +int sca_get_msg_cseq_method(sip_msg_t *msg) +{ + assert(msg != NULL); + + if (SCA_HEADER_EMPTY(msg->cseq)) { + LM_ERR("Empty Cseq header\n"); + return (-1); + } + + return (get_cseq(msg)->method_id); +} + +int sca_get_msg_from_header(sip_msg_t *msg, struct to_body **from) +{ + struct to_body *f; + + assert(msg != NULL); + assert(from != NULL); + + if (SCA_HEADER_EMPTY(msg->from)) { + LM_ERR("Empty From header\n"); + return (-1); + } + if (parse_from_header(msg) < 0) { + LM_ERR("Bad From header\n"); + return (-1); + } + f = get_from(msg); + if (SCA_STR_EMPTY(&f->tag_value)) { + LM_ERR("Bad From header: no tag parameter\n"); + return (-1); + } + + // ensure the URI is parsed for future use + if (parse_uri(f->uri.s, f->uri.len, GET_FROM_PURI(msg)) < 0) { + LM_ERR("Failed to parse From URI %.*s\n", STR_FMT(&f->uri)); + return (-1); + } + + *from = f; + + return (0); +} + +int sca_get_msg_to_header(sip_msg_t *msg, struct to_body **to) +{ + struct to_body parsed_to; + struct to_body *t = NULL; + + assert(msg != NULL); + assert(to != NULL); + + if (SCA_HEADER_EMPTY(msg->to)) { + LM_ERR("Empty To header\n"); + return (-1); + } + t = get_to(msg); + if (t == NULL) { + parse_to(msg->to->body.s, msg->to->body.s + msg->to->body.len + 1, // end of buffer + &parsed_to); + if (parsed_to.error != PARSE_OK) { + LM_ERR("Bad To header\n"); + return (-1); + } + t = &parsed_to; + } + + // ensure the URI is parsed for future use + if (parse_uri(t->uri.s, t->uri.len, GET_TO_PURI(msg)) < 0) { + LM_ERR("Failed to parse To URI %.*s\n", STR_FMT(&t->uri)); + return (-1); + } + + *to = t; + + return (0); +} + +/* + * count characters requiring escape as defined by escape_common + */ +int sca_uri_display_escapes_count(str *display) { + int c = 0; + int i; + + if (SCA_STR_EMPTY(display)) { + return (0); + } + + for (i = 0; i < display->len; i++) { + switch (display->s[i]) { + case '\'': + case '"': + case '\\': + case '\0': + c++; + + default: + break; + } + } + + return (c); +} + +int sca_uri_extract_aor(str *uri, str *aor) +{ + char *semi; + + assert(aor != NULL); + + if (uri == NULL) { + aor->s = NULL; + aor->len = 0; + return (-1); + } + + aor->s = uri->s; + semi = memchr(uri->s, ';', uri->len); + if (semi != NULL) { + aor->len = semi - uri->s; + } else { + aor->len = uri->len; + } + + return (0); +} + +int sca_uri_build_aor(str *aor, int maxlen, str *contact_uri, str *domain_uri) +{ + char *p; + char *dp; + int len; + + assert(aor != NULL); + assert(contact_uri != NULL); + assert(domain_uri != NULL); + + if (contact_uri->len + domain_uri->len >= maxlen) { + return (-1); + } + + p = memchr(contact_uri->s, '@', contact_uri->len); + if (p == NULL) { + // no username, by definition can't be an SCA line + aor->s = NULL; + aor->len = 0; + + return (0); + } + dp = memchr(domain_uri->s, '@', domain_uri->len); + if (dp == NULL) { + // may be nameless URI + dp = memchr(domain_uri->s, ':', domain_uri->len); + if (dp == NULL) { + // bad domain URI + return (-1); + } + } + dp++; + + len = p - contact_uri->s; + memcpy(aor->s, contact_uri->s, len); + aor->s[len] = '@'; + len += 1; + aor->len = len; + + len = domain_uri->len - (dp - domain_uri->s); + memcpy(aor->s + aor->len, dp, len); + aor->len += len; + + return (aor->len); +} + +int sca_aor_create_from_info(str *aor, uri_type type, str *user, str *domain, + str *port) +{ + str scheme = STR_NULL; + int len = 0; + + assert(aor != NULL); + + uri_type_to_str(type, &scheme); + + // +1 for ':', +1 for '@' + len = scheme.len + 1 + user->len + 1 + domain->len; + if (!SCA_STR_EMPTY(port)) { + // +1 for ':' + len += 1 + port->len; + } + + aor->s = (char *) pkg_malloc(len); + if (aor->s == NULL) { + LM_ERR("sca_aor_create_from_info: pkg_malloc %d bytes failed\n", len); + return (-1); + } + + len = 0; + SCA_STR_COPY(aor, &scheme); + len += scheme.len; + + *(aor->s + len) = ':'; + aor->len++; + len++; + + SCA_STR_APPEND(aor, user); + len += user->len; + + *(aor->s + len) = '@'; + aor->len++; + len++; + + SCA_STR_APPEND(aor, domain); + len += domain->len; + + if (!SCA_STR_EMPTY(port)) { + *(aor->s + len) = ':'; + len += 1; + + SCA_STR_APPEND(aor, port); + len += port->len; + } + + return (aor->len); +} + +int sca_create_canonical_aor_for_ua(sip_msg_t *msg, str *c_aor, int ua_opts) +{ + struct to_body *tf = NULL; + sip_uri_t c_uri; + str tf_aor = STR_NULL; + str contact_uri = STR_NULL; + int rc = -1; + + assert(msg != NULL); + assert(c_aor != NULL); + + memset(c_aor, 0, sizeof(str)); + + if ((ua_opts & SCA_AOR_TYPE_AUTO)) { + if (msg->first_line.type == SIP_REQUEST) { + ua_opts = SCA_AOR_TYPE_UAC; + } else { + ua_opts = SCA_AOR_TYPE_UAS; + } + } + + if ((ua_opts & SCA_AOR_TYPE_UAC)) { + if (sca_get_msg_from_header(msg, &tf) < 0) { + LM_ERR("sca_create_canonical_aor: failed to get From header\n"); + goto done; + } + } else { + if (sca_get_msg_to_header(msg, &tf) < 0) { + LM_ERR("sca_create_canonical_aor: failed to get To header\n"); + goto done; + } + } + + if (sca_uri_extract_aor(&tf->uri, &tf_aor) < 0) { + LM_ERR("sca_create_canonical_aor: failed to extract AoR from " + "URI <%.*s>\n", STR_FMT(&tf->uri)); + goto done; + } + + memset(&c_uri, 0, sizeof(sip_uri_t)); + if ((rc = sca_get_msg_contact_uri(msg, &contact_uri)) < 0) { + LM_ERR("sca_create_canonical_aor: failed to get contact URI from " + "Contact <%.*s>\n", STR_FMT(&msg->contact->body)); + goto done; + } + if (rc > 0) { + if (parse_uri(contact_uri.s, contact_uri.len, &c_uri) < 0) { + LM_ERR("sca_create_canonical_aor: failed to parse Contact URI " + "<%.*s>\n", STR_FMT(&contact_uri)); + rc = -1; + goto done; + } + } + + if (SCA_STR_EMPTY(&c_uri.user) || + SCA_STR_EQ(&c_uri.user, &tf->parsed_uri.user)) { + // empty contact header or Contact user matches To/From AoR + c_aor->s = (char *) pkg_malloc(tf_aor.len); + c_aor->len = tf_aor.len; + memcpy(c_aor->s, tf_aor.s, tf_aor.len); + } else { + // Contact user and To/From user mismatch + if (sca_aor_create_from_info(c_aor, c_uri.type, &c_uri.user, + &tf->parsed_uri.host, &tf->parsed_uri.port) < 0) { + LM_ERR("sca_create_canonical_aor: failed to create AoR from " + "Contact <%.*s> and URI <%.*s>\n", + STR_FMT(&contact_uri), STR_FMT(&tf_aor)); + goto done; + } + } + + rc = 1; + + done: return (rc); +} + +int sca_create_canonical_aor(sip_msg_t *msg, str *c_aor) +{ + return (sca_create_canonical_aor_for_ua(msg, c_aor, SCA_AOR_TYPE_AUTO)); +} + +/* + * XXX this considers any held stream to mean the call is on hold. correct? + */ +int sca_call_is_held(sip_msg_t *msg) +{ + sdp_session_cell_t *session; + sdp_stream_cell_t *stream; + int n_sess; + int n_str; + int is_held = 0; + int rc; + + rc = parse_sdp(msg); + if (rc < 0) { + LM_ERR("sca_call_is_held: parse_sdp body failed\n"); + return (0); + } else if (rc > 0) { + LM_DBG("sca_call_is_held: parse_sdp returned %d, no SDP body\n", rc); + return (0); + } + + // Cf. modules_k/textops's exported is_audio_on_hold + for (n_sess = 0, session = get_sdp_session(msg, n_sess); session != NULL; + n_sess++, session = get_sdp_session(msg, n_sess)) { + + for (n_str = 0, stream = get_sdp_stream(msg, n_sess, n_str); + stream != NULL; + n_str++, stream = get_sdp_stream(msg, n_sess, n_str)) { + if (stream->is_on_hold) { + is_held = 1; + goto done; + } + } + } + + done: return (is_held); +}