sca: end of line normalization to linux line ending format
authorHenning Westerholt <hw@skalatan.de>
Mon, 9 Sep 2019 19:29:09 +0000 (21:29 +0200)
committerHenning Westerholt <hw@skalatan.de>
Mon, 9 Sep 2019 19:29:09 +0000 (21:29 +0200)
src/modules/sca/sca_util.c

index 434d1df..b586c8f 100644 (file)
-/*\r
- * Copyright (C) 2012 Andrew Mortensen\r
- *\r
- * This file is part of the sca module for Kamailio, a free SIP server.\r
- *\r
- * The sca module is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version\r
- *\r
- * The sca module is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with this program; if not, write to the Free Software\r
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA. 02110-1301 USA\r
- */\r
-#include "sca_common.h"\r
-#include "sca.h"\r
-#include <assert.h>\r
-\r
-#include "sca_util.h"\r
-#include "../../core/dset.h"\r
-#include "../../core/parser/sdp/sdp.h"\r
-\r
-int sca_get_msg_method(sip_msg_t *msg)\r
-{\r
-       assert(msg != NULL);\r
-\r
-       if (msg->first_line.type == SIP_REQUEST) {\r
-               return (msg->REQ_METHOD);\r
-       }\r
-\r
-       return (sca_get_msg_cseq_method(msg));\r
-}\r
-\r
-int sca_get_msg_contact_uri(sip_msg_t *msg, str *contact_uri)\r
-{\r
-       contact_body_t *contact_body;\r
-\r
-       assert(msg != NULL);\r
-       assert(contact_uri != NULL);\r
-\r
-       if (SCA_HEADER_EMPTY(msg->contact)) {\r
-               LM_DBG("Empty Contact header\n");\r
-               contact_uri->s = NULL;\r
-               contact_uri->len = 0;\r
-\r
-               return (0);\r
-       }\r
-\r
-       if (parse_contact(msg->contact) < 0) {\r
-               LM_ERR("Failed to parse Contact header: %.*s\n",\r
-                               STR_FMT(&msg->contact->body));\r
-               return (-1);\r
-       }\r
-       if ((contact_body = (contact_body_t *) msg->contact->parsed) == NULL) {\r
-               LM_ERR("Invalid Contact header: %.*s\n", STR_FMT(&msg->contact->body));\r
-               return (-1);\r
-       }\r
-       if (contact_body->star) {\r
-               LM_ERR("Invalid Contact header: SCA Contact must not be \"*\"\n");\r
-               return (-1);\r
-       }\r
-       if (contact_body->contacts == NULL) {\r
-               LM_ERR("Invalid Contact header: parser found no contacts\n");\r
-               return (-1);\r
-       }\r
-       if (contact_body->contacts->next) {\r
-               LM_ERR("Invalid Contact header: Contact may only contain one URI\n");\r
-               return (-1);\r
-       }\r
-\r
-       contact_uri->s = contact_body->contacts->uri.s;\r
-       contact_uri->len = contact_body->contacts->uri.len;\r
-\r
-       return (1);\r
-}\r
-\r
-int sca_get_msg_cseq_number(sip_msg_t *msg)\r
-{\r
-       int cseq;\r
-\r
-       assert(msg != NULL);\r
-\r
-       if (SCA_HEADER_EMPTY(msg->cseq)) {\r
-               LM_ERR("Empty Cseq header\n");\r
-               return (-1);\r
-       }\r
-       if (str2int(&(get_cseq(msg)->number), (unsigned int *) &cseq) != 0) {\r
-               LM_ERR("Bad Cseq header: %.*s\n", STR_FMT(&msg->cseq->body));\r
-               return (-1);\r
-       }\r
-\r
-       return (cseq);\r
-}\r
-\r
-/*\r
- *  assumes cseq header in msg is already parsed\r
- */\r
-int sca_get_msg_cseq_method(sip_msg_t *msg)\r
-{\r
-       assert(msg != NULL);\r
-\r
-       if (SCA_HEADER_EMPTY(msg->cseq)) {\r
-               LM_ERR("Empty Cseq header\n");\r
-               return (-1);\r
-       }\r
-\r
-       return (get_cseq(msg)->method_id);\r
-}\r
-\r
-int sca_get_msg_from_header(sip_msg_t *msg, struct to_body **from)\r
-{\r
-       struct to_body *f;\r
-\r
-       assert(msg != NULL);\r
-       assert(from != NULL);\r
-\r
-       if (SCA_HEADER_EMPTY(msg->from)) {\r
-               LM_ERR("Empty From header\n");\r
-               return (-1);\r
-       }\r
-       if (parse_from_header(msg) < 0) {\r
-               LM_ERR("Bad From header\n");\r
-               return (-1);\r
-       }\r
-       f = get_from(msg);\r
-       if (SCA_STR_EMPTY(&f->tag_value)) {\r
-               LM_ERR("Bad From header: no tag parameter\n");\r
-               return (-1);\r
-       }\r
-\r
-       // ensure the URI is parsed for future use\r
-       if (parse_uri(f->uri.s, f->uri.len, GET_FROM_PURI(msg)) < 0) {\r
-               LM_ERR("Failed to parse From URI %.*s\n", STR_FMT(&f->uri));\r
-               return (-1);\r
-       }\r
-\r
-       *from = f;\r
-\r
-       return (0);\r
-}\r
-\r
-int sca_get_msg_to_header(sip_msg_t *msg, struct to_body **to)\r
-{\r
-       struct to_body parsed_to;\r
-       struct to_body *t = NULL;\r
-\r
-       assert(msg != NULL);\r
-       assert(to != NULL);\r
-\r
-       if (SCA_HEADER_EMPTY(msg->to)) {\r
-               LM_ERR("Empty To header\n");\r
-               return (-1);\r
-       }\r
-       t = get_to(msg);\r
-       if (t == NULL) {\r
-               parse_to(msg->to->body.s, msg->to->body.s + msg->to->body.len + 1, // end of buffer\r
-               &parsed_to);\r
-               if (parsed_to.error != PARSE_OK) {\r
-                       LM_ERR("Bad To header\n");\r
-                       return (-1);\r
-               }\r
-               t = &parsed_to;\r
-       }\r
-\r
-       // ensure the URI is parsed for future use\r
-       if (parse_uri(t->uri.s, t->uri.len, GET_TO_PURI(msg)) < 0) {\r
-               LM_ERR("Failed to parse To URI %.*s\n", STR_FMT(&t->uri));\r
-               return (-1);\r
-       }\r
-\r
-       *to = t;\r
-\r
-       return (0);\r
-}\r
-\r
-/*\r
- * caller needs to call free_to for *body\r
- */\r
-int sca_build_to_body_from_uri(sip_msg_t *msg, struct to_body **body, str *uri)\r
-{\r
-       assert(msg != NULL);\r
-       assert(body != NULL);\r
-       assert(uri != NULL);\r
-\r
-       *body = pkg_malloc(sizeof(struct to_body));\r
-       if(*body == NULL) {\r
-               LM_ERR("cannot allocate pkg memory\n");\r
-               return(-1);\r
-       }\r
-\r
-       parse_to(uri->s, uri->s + uri->len + 1, *body);\r
-       if ((*body)->error != PARSE_OK) {\r
-               LM_ERR("Bad uri value[%.*s]\n", STR_FMT(uri));\r
-               free_to(*body);\r
-               return(-1);\r
-       }\r
-       return (0);\r
-}\r
-\r
-/*\r
- *  count characters requiring escape as defined by escape_common\r
- */\r
-int sca_uri_display_escapes_count(str *display) {\r
-       int c = 0;\r
-       int i;\r
-\r
-       if (SCA_STR_EMPTY(display)) {\r
-               return (0);\r
-       }\r
-\r
-       for (i = 0; i < display->len; i++) {\r
-               switch (display->s[i]) {\r
-               case '\'':\r
-               case '"':\r
-               case '\\':\r
-               case '\0':\r
-                       c++;\r
-\r
-               default:\r
-                       break;\r
-               }\r
-       }\r
-\r
-       return (c);\r
-}\r
-\r
-int sca_uri_extract_aor(str *uri, str *aor)\r
-{\r
-       char *semi;\r
-\r
-       assert(aor != NULL);\r
-\r
-       if (uri == NULL) {\r
-               aor->s = NULL;\r
-               aor->len = 0;\r
-               return (-1);\r
-       }\r
-\r
-       aor->s = uri->s;\r
-       semi = memchr(uri->s, ';', uri->len);\r
-       if (semi != NULL) {\r
-               aor->len = semi - uri->s;\r
-       } else {\r
-               aor->len = uri->len;\r
-       }\r
-\r
-       return (0);\r
-}\r
-\r
-int sca_uri_build_aor(str *aor, int maxlen, str *contact_uri, str *domain_uri)\r
-{\r
-       char *p;\r
-       char *dp;\r
-       int len;\r
-\r
-       assert(aor != NULL);\r
-       assert(contact_uri != NULL);\r
-       assert(domain_uri != NULL);\r
-\r
-       if (contact_uri->len + domain_uri->len >= maxlen) {\r
-               return (-1);\r
-       }\r
-\r
-       p = memchr(contact_uri->s, '@', contact_uri->len);\r
-       if (p == NULL) {\r
-               // no username, by definition can't be an SCA line\r
-               aor->s = NULL;\r
-               aor->len = 0;\r
-\r
-               return (0);\r
-       }\r
-       dp = memchr(domain_uri->s, '@', domain_uri->len);\r
-       if (dp == NULL) {\r
-               // may be nameless URI\r
-               dp = memchr(domain_uri->s, ':', domain_uri->len);\r
-               if (dp == NULL) {\r
-                       // bad domain URI\r
-                       return (-1);\r
-               }\r
-       }\r
-       dp++;\r
-\r
-       len = p - contact_uri->s;\r
-       memcpy(aor->s, contact_uri->s, len);\r
-       aor->s[len] = '@';\r
-       len += 1;\r
-       aor->len = len;\r
-\r
-       len = domain_uri->len - (dp - domain_uri->s);\r
-       memcpy(aor->s + aor->len, dp, len);\r
-       aor->len += len;\r
-\r
-       return (aor->len);\r
-}\r
-\r
-int sca_aor_create_from_info(str *aor, uri_type type, str *user, str *domain,\r
-               str *port)\r
-{\r
-       str scheme = STR_NULL;\r
-       int len = 0;\r
-\r
-       assert(aor != NULL);\r
-\r
-       uri_type_to_str(type, &scheme);\r
-\r
-       // +1 for ':', +1 for '@'\r
-       len = scheme.len + 1 + user->len + 1 + domain->len;\r
-       if (!SCA_STR_EMPTY(port)) {\r
-               // +1 for ':'\r
-               len += 1 + port->len;\r
-       }\r
-\r
-       aor->s = (char *) pkg_malloc(len);\r
-       if (aor->s == NULL) {\r
-               LM_ERR("sca_aor_create_from_info: pkg_malloc %d bytes failed\n", len);\r
-               return (-1);\r
-       }\r
-\r
-       len = 0;\r
-       SCA_STR_COPY(aor, &scheme);\r
-       len += scheme.len;\r
-\r
-       *(aor->s + len) = ':';\r
-       aor->len++;\r
-       len++;\r
-\r
-       SCA_STR_APPEND(aor, user);\r
-       len += user->len;\r
-\r
-       *(aor->s + len) = '@';\r
-       aor->len++;\r
-       len++;\r
-\r
-       SCA_STR_APPEND(aor, domain);\r
-       len += domain->len;\r
-\r
-       if (!SCA_STR_EMPTY(port)) {\r
-               *(aor->s + len) = ':';\r
-               len += 1;\r
-\r
-               SCA_STR_APPEND(aor, port);\r
-               len += port->len;\r
-       }\r
-\r
-       return (aor->len);\r
-}\r
-\r
-int sca_create_canonical_aor_for_ua(sip_msg_t *msg, str *c_aor, int ua_opts)\r
-{\r
-       struct to_body *tf = NULL;\r
-       sip_uri_t c_uri;\r
-       str tf_aor = STR_NULL;\r
-       str contact_uri = STR_NULL;\r
-       int rc = -1;\r
-\r
-       assert(msg != NULL);\r
-       assert(c_aor != NULL);\r
-\r
-       memset(c_aor, 0, sizeof(str));\r
-\r
-       if ((ua_opts & SCA_AOR_TYPE_AUTO)) {\r
-               if (msg->first_line.type == SIP_REQUEST) {\r
-                       ua_opts = SCA_AOR_TYPE_UAC;\r
-               } else {\r
-                       ua_opts = SCA_AOR_TYPE_UAS;\r
-               }\r
-       }\r
-\r
-       if ((ua_opts & SCA_AOR_TYPE_UAC)) {\r
-               if (sca_get_msg_from_header(msg, &tf) < 0) {\r
-                       LM_ERR("sca_create_canonical_aor: failed to get From header\n");\r
-                       goto done;\r
-               }\r
-       } else {\r
-               if (sca_get_msg_to_header(msg, &tf) < 0) {\r
-                       LM_ERR("sca_create_canonical_aor: failed to get To header\n");\r
-                       goto done;\r
-               }\r
-       }\r
-\r
-       if (sca_uri_extract_aor(&tf->uri, &tf_aor) < 0) {\r
-               LM_ERR("sca_create_canonical_aor: failed to extract AoR from "\r
-                               "URI <%.*s>\n", STR_FMT(&tf->uri));\r
-               goto done;\r
-       }\r
-\r
-       memset(&c_uri, 0, sizeof(sip_uri_t));\r
-       if ((rc = sca_get_msg_contact_uri(msg, &contact_uri)) < 0) {\r
-               LM_ERR("sca_create_canonical_aor: failed to get contact URI from "\r
-                               "Contact <%.*s>\n", STR_FMT(&msg->contact->body));\r
-               goto done;\r
-       }\r
-       if (rc > 0) {\r
-               if (parse_uri(contact_uri.s, contact_uri.len, &c_uri) < 0) {\r
-                       LM_ERR("sca_create_canonical_aor: failed to parse Contact URI "\r
-                                       "<%.*s>\n", STR_FMT(&contact_uri));\r
-                       rc = -1;\r
-                       goto done;\r
-               }\r
-       }\r
-\r
-       if (SCA_STR_EMPTY(&c_uri.user) ||\r
-       SCA_STR_EQ(&c_uri.user, &tf->parsed_uri.user)) {\r
-               // empty contact header or Contact user matches To/From AoR\r
-               c_aor->s = (char *) pkg_malloc(tf_aor.len);\r
-               c_aor->len = tf_aor.len;\r
-               memcpy(c_aor->s, tf_aor.s, tf_aor.len);\r
-       } else {\r
-               // Contact user and To/From user mismatch\r
-               if (sca_aor_create_from_info(c_aor, c_uri.type, &c_uri.user,\r
-                               &tf->parsed_uri.host, &tf->parsed_uri.port) < 0) {\r
-                       LM_ERR("sca_create_canonical_aor: failed to create AoR from "\r
-                                       "Contact <%.*s> and URI <%.*s>\n",\r
-                                       STR_FMT(&contact_uri), STR_FMT(&tf_aor));\r
-                       goto done;\r
-               }\r
-       }\r
-\r
-       rc = 1;\r
-\r
-       done: return (rc);\r
-}\r
-\r
-int sca_create_canonical_aor(sip_msg_t *msg, str *c_aor)\r
-{\r
-       return (sca_create_canonical_aor_for_ua(msg, c_aor, SCA_AOR_TYPE_AUTO));\r
-}\r
-\r
-/*\r
- * XXX this considers any held stream to mean the call is on hold. correct?\r
- */\r
-int sca_call_is_held(sip_msg_t *msg)\r
-{\r
-       sdp_session_cell_t *session;\r
-       sdp_stream_cell_t *stream;\r
-       int n_sess;\r
-       int n_str;\r
-       int is_held = 0;\r
-       int rc;\r
-\r
-       if(sca->cfg->onhold_bflag >= 0) {\r
-               if (isbflagset(0, (flag_t)sca->cfg->onhold_bflag)==1) {\r
-                       LM_DBG("onhold_bflag set, skip parse_sdp and set held\n");\r
-                       return ( 1 );\r
-               }\r
-       }\r
-       rc = parse_sdp(msg);\r
-       if (rc < 0) {\r
-               LM_ERR("sca_call_is_held: parse_sdp body failed\n");\r
-               return (0);\r
-       } else if (rc > 0) {\r
-               LM_DBG("sca_call_is_held: parse_sdp returned %d, no SDP body\n", rc);\r
-               return (0);\r
-       }\r
-\r
-       // Cf. modules_k/textops's exported is_audio_on_hold\r
-       for (n_sess = 0, session = get_sdp_session(msg, n_sess); session != NULL;\r
-                       n_sess++, session = get_sdp_session(msg, n_sess)) {\r
-\r
-               for (n_str = 0, stream = get_sdp_stream(msg, n_sess, n_str);\r
-                               stream != NULL;\r
-                               n_str++, stream = get_sdp_stream(msg, n_sess, n_str)) {\r
-                       if (stream->is_on_hold) {\r
-                               LM_DBG("sca_call_is_held: parse_sdp detected stream is on hold\n");\r
-                               is_held = 1;\r
-                               goto done;\r
-                       }\r
-               }\r
-       }\r
-\r
-       done: return (is_held);\r
-}\r
+/*
+ * 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);
+}