Explorar el Código

sca: end of line normalization to linux line ending format

Henning Westerholt hace 6 años
padre
commit
162c32a329
Se han modificado 1 ficheros con 477 adiciones y 477 borrados
  1. 477 477
      src/modules/sca/sca_util.c

+ 477 - 477
src/modules/sca/sca_util.c

@@ -1,477 +1,477 @@
-/*
- * 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 "sca.h"
-#include <assert.h>
-
-#include "sca_util.h"
-#include "../../core/dset.h"
-#include "../../core/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);
-}
-
-/*
- * caller needs to call free_to for *body
- */
-int sca_build_to_body_from_uri(sip_msg_t *msg, struct to_body **body, str *uri)
-{
-	assert(msg != NULL);
-	assert(body != NULL);
-	assert(uri != NULL);
-
-	*body = pkg_malloc(sizeof(struct to_body));
-	if(*body == NULL) {
-		LM_ERR("cannot allocate pkg memory\n");
-		return(-1);
-	}
-
-	parse_to(uri->s, uri->s + uri->len + 1, *body);
-	if ((*body)->error != PARSE_OK) {
-		LM_ERR("Bad uri value[%.*s]\n", STR_FMT(uri));
-		free_to(*body);
-		return(-1);
-	}
-	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;
-
-	if(sca->cfg->onhold_bflag >= 0) {
-		if (isbflagset(0, (flag_t)sca->cfg->onhold_bflag)==1) {
-			LM_DBG("onhold_bflag set, skip parse_sdp and set held\n");
-			return ( 1 );
-		}
-	}
-	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) {
-				LM_DBG("sca_call_is_held: parse_sdp detected stream is on hold\n");
-				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 "sca.h"
+#include <assert.h>
+
+#include "sca_util.h"
+#include "../../core/dset.h"
+#include "../../core/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);
+}
+
+/*
+ * caller needs to call free_to for *body
+ */
+int sca_build_to_body_from_uri(sip_msg_t *msg, struct to_body **body, str *uri)
+{
+	assert(msg != NULL);
+	assert(body != NULL);
+	assert(uri != NULL);
+
+	*body = pkg_malloc(sizeof(struct to_body));
+	if(*body == NULL) {
+		LM_ERR("cannot allocate pkg memory\n");
+		return(-1);
+	}
+
+	parse_to(uri->s, uri->s + uri->len + 1, *body);
+	if ((*body)->error != PARSE_OK) {
+		LM_ERR("Bad uri value[%.*s]\n", STR_FMT(uri));
+		free_to(*body);
+		return(-1);
+	}
+	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;
+
+	if(sca->cfg->onhold_bflag >= 0) {
+		if (isbflagset(0, (flag_t)sca->cfg->onhold_bflag)==1) {
+			LM_DBG("onhold_bflag set, skip parse_sdp and set held\n");
+			return ( 1 );
+		}
+	}
+	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) {
+				LM_DBG("sca_call_is_held: parse_sdp detected stream is on hold\n");
+				is_held = 1;
+				goto done;
+			}
+		}
+	}
+
+	done: return (is_held);
+}