diff --git a/db/schema/location.xml b/db/schema/location.xml
index ff2aa2fd2fe..56583de0dbc 100644
--- a/db/schema/location.xml
+++ b/db/schema/location.xml
@@ -9,7 +9,7 @@
location
- 1013
+ 1014
&MYSQL_TABLE_TYPE;
Persistent user location information for the usrloc module. More information can be found at: &OPENSIPS_MOD_DOC;usrloc.html
@@ -177,4 +177,13 @@
Optional information specific to each registration
+
+
+ params
+ text
+ 512
+
+
+ Parameters of the Contact header
+
diff --git a/lib/reg/ci.c b/lib/reg/ci.c
index ef9c81c1465..2bac4ea44ec 100644
--- a/lib/reg/ci.c
+++ b/lib/reg/ci.c
@@ -110,6 +110,14 @@ ucontact_info_t *pack_ci(struct sip_msg* _m, contact_t* _c, unsigned int _e,
ci.received = path_received;
}
+ if (parse_headers(_m, HDR_CONTACT_F, 0) != -1) {
+ ci.params = get_first_contact(_m)->params;
+ } else {
+ if(_c && _c->params) {
+ ci.params = _c->params;
+ }
+ }
+
if (ownership_tag)
ci.shtag = *ownership_tag;
diff --git a/lib/str_buffer.c b/lib/str_buffer.c
new file mode 100644
index 00000000000..d827afc9dc0
--- /dev/null
+++ b/lib/str_buffer.c
@@ -0,0 +1,302 @@
+/*
+ * str_buffer.c - A str_buffer for building strings without knowing the size.
+ *
+ * Author: Larry Laffer (larrylaffer130@gmail.com)
+ * Copyright (C) 2025 Larry Laffer
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips 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
+ *
+ * opensips 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 "str_buffer.h"
+
+#include "../mem/mem.h"
+#include "../dprint.h"
+
+#include
+
+/* generic logging helper for allocation errors in private system memory */
+#ifdef SYS_MALLOC
+#define PKG_MEM_ERROR LM_ERR("could not allocate private memory from sys pool\n")
+#define PKG_MEM_CRITICAL LM_CRIT("could not allocate private memory from sys pool\n")
+
+#ifdef __SUNPRO_C
+#define PKG_MEM_ERROR_FMT(...) LM_ERR("could not allocate private memory from sys pool" __VA_ARGS__)
+#define PKG_MEM_CRITICAL_FMT(...) LM_CRIT("could not allocate private memory from sys pool" __VA_ARGS__)
+#else
+#define PKG_MEM_ERROR_FMT(fmt, args...) LM_ERR("could not allocate private memory from sys pool - " fmt , ## args)
+#define PKG_MEM_CRITICAL_FMT(fmt, args...) LM_CRIT("could not allocate private memory from sys pool - " fmt , ## args)
+#endif
+
+/* generic logging helper for allocation errors in private memory pool */
+#else
+
+#define PKG_MEM_ERROR LM_ERR("could not allocate private memory from pkg pool\n")
+#define PKG_MEM_CRITICAL LM_CRIT("could not allocate private memory from pkg pool\n")
+
+#ifdef __SUNPRO_C
+#define PKG_MEM_ERROR_FMT(...) LM_ERR("could not allocate private memory from pkg pool" __VA_ARGS__)
+#define PKG_MEM_CRITICAL_FMT(...) LM_CRIT("could not allocate private memory from pkg pool" __VA_ARGS__)
+#else
+#define PKG_MEM_ERROR_FMT(fmt, args...) LM_ERR("could not allocate private memory from pkg pool - " fmt , ## args)
+#define PKG_MEM_CRITICAL_FMT(fmt, args...) LM_CRIT("could not allocate private memory from pkg pool - " fmt , ## args)
+#endif
+
+#endif /* SYS_MALLOC */
+
+/**
+ * @brief Resize str_buffer storage if current usage and len will not fit in it.
+ *
+ * @param buf str_buffer to resize
+ * @param len len that needs to fit in str_buffer
+ * @return 1 on success else 0
+ */
+static inline int resizeIfRequired(str_buffer *buf, int len)
+{
+ if(buf->storage.len + len >= (1 << buf->scaling) * BUFFER_SCALE_PERCENT) {
+ char *res = NULL;
+
+ while(buf->storage.len + len
+ >= (1 << buf->scaling) * BUFFER_SCALE_PERCENT) {
+ ++buf->scaling;
+ }
+
+ res = pkg_realloc(buf->storage.s, 1 << buf->scaling);
+ if(!res) {
+ buf->error = 1;
+ PKG_MEM_ERROR;
+ return 0;
+ }
+
+ buf->storage.s = res;
+ }
+
+ return 1;
+}
+
+/**
+ * @brief Calculate the strlen after the replacement in format string.
+ *
+ * @param fmt format string
+ * @param args argument list
+ * @return length of string after the replacement plus 1 because of \0
+ */
+static inline int lengthAfterReplaced(char *fmt, va_list args)
+{
+ int len = 0;
+ va_list tmpArgs;
+
+ va_copy(tmpArgs, args);
+ len = vsnprintf(NULL, 0, fmt, tmpArgs) + 1;
+ va_end(tmpArgs);
+
+ return len;
+}
+
+/**
+ * @brief Append a given format with replaced values to the str_buffer.
+ *
+ * @param buf str_buffer where to append
+ * @param src char* format to append
+ * @param len length of the char*
+ * @param args replacement for the format
+ * @return 1 on success else 0
+ */
+static inline int str_buffer_append_varg(
+ str_buffer *buf, char *src, int len, va_list args)
+{
+ va_list tmpArgs;
+ char *tmp = NULL;
+ int replacedLen = 0;
+
+ if(!buf) {
+ LM_BUG("Wrong usage passing NULL as the buffer\n");
+ return 0;
+ }
+ if(!src || len == 0) {
+ return 1;
+ }
+
+ // temp one bigger than original and zero filled, to make sure its zero terminated
+ tmp = pkg_malloc(sizeof(char) * len + 1);
+ if(!tmp) {
+ buf->error = 1;
+ PKG_MEM_ERROR;
+ return 0;
+ }
+ memset(tmp, 0, sizeof(char) * len + 1);
+ memcpy(tmp, src, len);
+
+ replacedLen = lengthAfterReplaced(tmp, args);
+ if(!resizeIfRequired(buf, replacedLen)) {
+ pkg_free(tmp);
+ return 0;
+ }
+
+ va_copy(tmpArgs, args);
+ len = vsnprintf(
+ buf->storage.s + buf->storage.len, 1 << buf->scaling, tmp, tmpArgs);
+ buf->storage.len += len;
+ va_end(tmpArgs);
+
+ pkg_free(tmp);
+
+ return 1;
+}
+
+str_buffer *new_str_buffer(void)
+{
+ str_buffer *buf = NULL;
+ buf = pkg_malloc(sizeof(str_buffer));
+ if(!buf) {
+ PKG_MEM_ERROR;
+ return NULL;
+ }
+
+ buf->storage.s = pkg_malloc(1 << BUFFER_START_BLOCK_SIZE * sizeof(char));
+ if(!buf->storage.s) {
+ PKG_MEM_ERROR;
+ pkg_free(buf);
+ return NULL;
+ }
+ memset(buf->storage.s, 0, 1 << BUFFER_START_BLOCK_SIZE * sizeof(char));
+
+ buf->storage.len = 0;
+ buf->scaling = BUFFER_START_BLOCK_SIZE;
+ buf->error = 0;
+
+ return buf;
+}
+
+void free_str_buffer(str_buffer *buf)
+{
+ if(!buf) {
+ LM_BUG("Wrong usage passing NULL as the buffer\n");
+ return;
+ }
+
+ if(buf->storage.s) {
+ pkg_free(buf->storage.s);
+ }
+
+ pkg_free(buf);
+}
+
+int str_buffer_has_error(str_buffer *buf)
+{
+ if(!buf) {
+ LM_BUG("Wrong usage passing NULL as the buffer\n");
+ return 0;
+ }
+
+ return buf->error;
+}
+
+int str_buffer_append_str(str_buffer *buf, str *src)
+{
+ if(!src) {
+ return 1;
+ }
+
+ return str_buffer_append_char_ptr(buf, src->s, src->len);
+}
+
+int str_buffer_append_char_ptr(str_buffer *buf, char *src, int len)
+{
+ if(!buf) {
+ LM_BUG("Wrong usage passing NULL as the buffer\n");
+ return 0;
+ }
+ if(!src || len == 0) {
+ return 1;
+ }
+ if(!resizeIfRequired(buf, len)) {
+ return 0;
+ }
+
+ memcpy(buf->storage.s + buf->storage.len, src, len);
+ buf->storage.len += len;
+
+ return 1;
+}
+
+int str_buffer_append_str_fmt(str_buffer *buf, str *src, ...)
+{
+ int res = 0;
+ va_list args;
+
+ if(!src) {
+ return 1;
+ }
+
+ va_start(args, src);
+ res = str_buffer_append_varg(buf, src->s, src->len, args);
+ va_end(args);
+
+ return res;
+}
+
+int str_buffer_append_char_ptr_fmt(str_buffer *buf, char *src, int len, ...)
+{
+ int res = 0;
+ va_list args;
+
+ va_start(args, len);
+ res = str_buffer_append_varg(buf, src, len, args);
+ va_end(args);
+
+ return res;
+}
+
+int str_buffer_append_int(str_buffer *buf, int val)
+{
+ return str_buffer_append_char_ptr_fmt(buf, "%d", 2, val);
+}
+
+int str_buffer_to_str(str_buffer *buf, str *dest)
+{
+ if(!dest) {
+ LM_BUG("Wrong usage passing NULL as the destination\n");
+ return 0;
+ }
+
+ return str_buffer_to_char_ptr(buf, &dest->s, &dest->len);
+}
+
+int str_buffer_to_char_ptr(str_buffer *buf, char **dest, int *len)
+{
+ if(!buf) {
+ LM_BUG("Wrong usage passing NULL as the buffer\n");
+ return 0;
+ }
+ if(!dest) {
+ LM_BUG("Wrong usage passing NULL as the destination\n");
+ return 0;
+ }
+
+ *dest = pkg_malloc(sizeof(char) * buf->storage.len + 1);
+ if(!*dest) {
+ PKG_MEM_ERROR;
+ return 0;
+ }
+ memset(*dest, 0, sizeof(char) * buf->storage.len + 1);
+ memcpy(*dest, buf->storage.s, buf->storage.len);
+ if(len) {
+ *len = buf->storage.len;
+ }
+
+ return 1;
+}
diff --git a/lib/str_buffer.h b/lib/str_buffer.h
new file mode 100644
index 00000000000..cef79385462
--- /dev/null
+++ b/lib/str_buffer.h
@@ -0,0 +1,130 @@
+/*
+ * str_buffer.h - A str_buffer for building strings without knowing the size.
+ *
+ * Author: Larry Laffer (larrylaffer130@gmail.com)
+ * Copyright (C) 2025 Larry Laffer
+ *
+ * This file is part of opensips, a free SIP server.
+ *
+ * opensips 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
+ *
+ * opensips 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
+ */
+
+
+#ifndef STR_BUFFER_H_
+#define STR_BUFFER_H_
+
+#include "../str.h"
+
+/** start storage chunk size, exponent for the power of two */
+#define BUFFER_START_BLOCK_SIZE 10
+/** resize threshold */
+#define BUFFER_SCALE_PERCENT 0.9
+
+/**
+ * @brief Structure storing the str_buffer data.
+ *
+ */
+typedef struct _str_buffer
+{
+ str storage;
+ int scaling;
+ int error;
+} str_buffer;
+
+/**
+ * @brief Create a new str_buffer.
+ *
+ * @return NULL or new str_buffer
+ */
+str_buffer *new_str_buffer(void);
+/**
+ * @brief Destroy a given str_buffer.
+ *
+ * @param buf str_buffer to destroy
+ */
+void free_str_buffer(str_buffer *buf);
+
+/**
+ * @brief Test whether there were issues while filling the str_buffer.
+ *
+ * @param buf str_buffer to test
+ * @return 1 if issues exists else 0
+ */
+int str_buffer_has_error(str_buffer *buf);
+
+/**
+ * @brief Append a given str to the str_buffer.
+ *
+ * @param buf str_buffer where to append
+ * @param src str to append
+ * @return 1 on success else 0
+ */
+int str_buffer_append_str(str_buffer *buf, str *src);
+/**
+ * @brief Append a given char* with len to the str_buffer.
+ *
+ * @param buf str_buffer where to append
+ * @param src char* to append
+ * @param len length of the char*
+ * @return 1 on success else 0
+ */
+int str_buffer_append_char_ptr(str_buffer *buf, char *src, int len);
+/**
+ * @brief Append a give str format with replaced values to the str_buffer.
+ *
+ * @param buf str_buffer where to append
+ * @param src str format to append
+ * @param VARARGS replacement for the format
+ * @return 1 on success else 0
+ */
+int str_buffer_append_str_fmt(str_buffer *buf, str *src, ...);
+/**
+ * @brief Append a given char* format with replaced values to the str_buffer.
+ *
+ * @param buf str_buffer where to append
+ * @param src char* format to append
+ * @param len length of the char*
+ * @param VARARGS replacement for the format
+ * @return 1 on success else 0
+ */
+int str_buffer_append_char_ptr_fmt(str_buffer *buf, char *src, int len, ...);
+/**
+ * @brief Append a give int to the str_buffer.
+ *
+ * @param buf str_buffer where to append
+ * @param val int to append
+ * @return 1 on success else 0
+ */
+int str_buffer_append_int(str_buffer *buf, int val);
+
+/**
+ * @brief Save str_buffer content as str.
+ *
+ * @param buf str_buffer to save
+ * @param dest str where to save str_buffer content
+ * @return 1 on success else 0
+ */
+int str_buffer_to_str(str_buffer *buf, str *dest);
+/**
+ * @brief Save str_buffer content as char*.
+ *
+ * @param buf str_buffer to save
+ * @param dest char* where to save str_buffer content
+ * @param len if not null len will be filled with the char pointer length
+ * @return 1 on success else 0
+ */
+int str_buffer_to_char_ptr(str_buffer *buf, char **dest, int *len);
+
+#endif
diff --git a/modules/pua_reginfo/notify.c b/modules/pua_reginfo/notify.c
index 1443822b8ee..2d2b17f7318 100644
--- a/modules/pua_reginfo/notify.c
+++ b/modules/pua_reginfo/notify.c
@@ -234,11 +234,8 @@ int process_body(str notify_body, udomain_t *domain)
str contact_uri = {0, 0};
str contact_params = {0, 0};
str param = {0, 0};
- str received = {0, 0};
- str path = {0, 0};
- str user_agent = {0, 0};
- int state, event, expires, result, final_result = RESULT_ERROR;
- char *expires_char, *cseq_char;
+ int state = STATE_UNKNOWN, event, expires, result, final_result = RESULT_ERROR;
+ char *expires_char = NULL, *cseq_char = NULL,*state_attr = NULL, *event_attr = NULL, *param_name = NULL;
int cseq = 0;
int len;
urecord_t *ul_record = NULL;
@@ -265,8 +262,12 @@ int process_body(str notify_body, udomain_t *domain)
/* Only process registration sub-items */
if(xmlStrcasecmp(registrations->name, BAD_CAST "registration") != 0)
goto next_registration;
- state = reginfo_parse_state(
- xmlGetAttrContentByName(registrations, "state"));
+ state_attr = xmlGetAttrContentByName(registrations, "state");
+ if(state_attr) {
+ state = reginfo_parse_state(state_attr);
+ xmlFree(state_attr);
+ state_attr = NULL;
+ }
if(state == STATE_UNKNOWN) {
LM_ERR("No state for this contact!\n");
goto next_registration;
@@ -288,10 +289,10 @@ int process_body(str notify_body, udomain_t *domain)
if(reginfo_use_domain) {
aor_key.s = uri;
+ aor_key.len = strlen(aor_key.s);
} else {
- aor_key.s = parsed_aor.user.s;
+ aor_key = parsed_aor.user;
}
- aor_key.len = strlen(aor_key.s);
/* Now let's lock that domain for this AOR: */
ul.lock_udomain(domain, &aor_key);
/* and retrieve the user-record for this user: */
@@ -346,31 +347,15 @@ int process_body(str notify_body, udomain_t *domain)
callid.s = xmlGetAttrContentByName(contacts, "id");
callid.len = strlen(callid.s);
LM_DBG("Got Call-ID \"%.*s\"\n", callid.len, callid.s);
- received.s = xmlGetAttrContentByName(contacts, "received");
- if(received.s == NULL) {
- LM_DBG("No received for this contact!\n");
- received.len = 0;
- } else {
- received.len = strlen(received.s);
- }
-
- path.s = xmlGetAttrContentByName(contacts, "path");
- if(path.s == NULL) {
- LM_DBG("No path for this contact!\n");
- path.len = 0;
- } else {
- path.len = strlen(path.s);
- }
- user_agent.s = xmlGetAttrContentByName(contacts, "user_agent");
- if(user_agent.s == NULL) {
- LM_DBG("No user_agent for this contact!\n");
- user_agent.len = 0;
- } else {
- user_agent.len = strlen(user_agent.s);
+ event_attr = xmlGetAttrContentByName(contacts, "event");
+ if(event_attr == NULL) {
+ LM_ERR("No event for this contact!\n");
+ goto next_contact;
}
- event = reginfo_parse_event(
- xmlGetAttrContentByName(contacts, "event"));
+ event = reginfo_parse_event(event_attr);
+ xmlFree(event_attr);
+ event_attr = NULL;
if(event == EVENT_UNKNOWN) {
LM_ERR("No event for this contact!\n");
goto next_contact;
@@ -381,6 +366,8 @@ int process_body(str notify_body, udomain_t *domain)
goto next_contact;
}
expires = atoi(expires_char);
+ xmlFree(expires_char);
+ expires_char = NULL;
if(expires < 0) {
LM_ERR("No valid expires for this contact!\n");
goto next_contact;
@@ -397,6 +384,8 @@ int process_body(str notify_body, udomain_t *domain)
LM_WARN("No valid cseq for this contact!\n");
}
}
+ xmlFree(cseq_char);
+ cseq_char = NULL;
//
// en,fr
@@ -408,12 +397,18 @@ int process_body(str notify_body, udomain_t *domain)
if(xmlStrcasecmp(params->name, BAD_CAST "unknown-param")
!= 0)
goto next_param;
- len += 1 /* ; */
- + strlen(xmlGetAttrContentByName(params, "name"));
+ param_name = xmlGetAttrContentByName(params, "name");
+ len += 1 /* ; */ + strlen(param_name);
param.s = (char *)xmlNodeGetContent(params);
param.len = strlen(param.s);
if(param.len > 0)
len += 1 /* = */ + param.len;
+ xmlFree(param_name);
+ param_name = NULL;
+ xmlFree(param.s);
+ param.s = NULL;
+ param.len = 0;
+
next_param:
params = params->next;
}
@@ -430,10 +425,11 @@ int process_body(str notify_body, udomain_t *domain)
if(xmlStrcasecmp(params->name, BAD_CAST "unknown-param")
!= 0)
goto next_param2;
+
+ param_name = xmlGetAttrContentByName(params, "name");
contact_params.len += snprintf(
contact_params.s + contact_params.len,
- len - contact_params.len, ";%s",
- xmlGetAttrContentByName(params, "name"));
+ len - contact_params.len, ";%s", param_name);
param.s = (char *)xmlNodeGetContent(params);
param.len = strlen(param.s);
if(param.len > 0)
@@ -445,6 +441,12 @@ int process_body(str notify_body, udomain_t *domain)
LM_DBG("Contact params are: %.*s\n", contact_params.len,
contact_params.s);
+ xmlFree(param_name);
+ param_name = NULL;
+ xmlFree(param.s);
+ param.s = NULL;
+ param.len = 0;
+
next_param2:
params = params->next;
}
@@ -489,10 +491,20 @@ int process_body(str notify_body, udomain_t *domain)
/* Process the result */
if(final_result != RESULT_CONTACTS_FOUND)
final_result = result;
+
+ xmlFree(contact_uri.s);
+ contact_uri.s = NULL;
+ contact_uri.len = 0;
next_uri:
+
uris = uris->next;
}
next_contact:
+ if(callid.s) {
+ xmlFree(callid.s);
+ callid.s = NULL;
+ callid.len = 0;
+ }
contacts = contacts->next;
}
}
@@ -502,7 +514,16 @@ int process_body(str notify_body, udomain_t *domain)
/* Unlock the domain for this AOR: */
if(aor_key.len > 0)
ul.unlock_udomain(domain, &aor_key);
-
+ if(callid.s) {
+ xmlFree(callid.s);
+ callid.s = NULL;
+ callid.len = 0;
+ }
+ if(aor.s) {
+ xmlFree(aor.s);
+ aor.s = NULL;
+ aor.len = 0;
+ }
registrations = registrations->next;
}
error:
diff --git a/modules/pua_reginfo/usrloc_cb.c b/modules/pua_reginfo/usrloc_cb.c
index dd8a8891a12..c767f6e3282 100644
--- a/modules/pua_reginfo/usrloc_cb.c
+++ b/modules/pua_reginfo/usrloc_cb.c
@@ -21,6 +21,7 @@
*/
#include "usrloc_cb.h"
#include "pua_reginfo.h"
+#include "../../lib/str_buffer.h"
#include
#include "../pua/pua.h"
#include "../presence/bind_presence.h"
@@ -50,6 +51,20 @@ Call-ID: 9ad9f89f-164d-bb86-1072-52e7e9eb5025.
.
*/
+/** Formats ::str string for use in printf-like functions.
+ * This is a macro that prepares a ::str string for use in functions which
+ * use printf-like formatting strings. This macro is necessary because
+ * ::str strings do not have to be zero-terminated and thus it is necessary
+ * to provide printf-like functions with the number of characters in the
+ * string manually. Here is an example how to use the macro:
+ * \code printf("%.*s\n", STR_FMT(var));\endcode Note well that the correct
+ * sequence in the formatting string is %.*, see the man page of printf for
+ * more details.
+ */
+#define STR_FMT(_pstr_) \
+ ((_pstr_ != (str *)0) ? (_pstr_)->len : 0), \
+ ((_pstr_ != (str *)0) ? (_pstr_)->s : "")
+
static int _pua_reginfo_self_op = 0;
static pres_ev_t *reginfo_event = NULL;
@@ -59,89 +74,217 @@ void pua_reginfo_update_self_op(int v)
_pua_reginfo_self_op = v;
}
-str r_active = str_init("active");
-str r_terminated = str_init("terminated");
-str r_registered = str_init("registered");
-str r_refreshed = str_init("refreshed");
-str r_expired = str_init("expired");
-str r_unregistered = str_init("unregistered");
#define VERSION_HOLDER "00000000000"
str reginfo_key_etag = str_init("reginfo_etag");
-str *build_reginfo_full(urecord_t *record, ucontact_t *contact, str aor[], unsigned int aor_count, int type, int * count)
+
+static str xml_start = str_init("\n");
+
+static str r_full = str_init("full");
+static str r_reginfo_s = str_init(
+ "\n"); // before 74 but calculated 76
+static str r_reginfo_e = str_init("\n");
+
+static str r_active = str_init("active");
+static str r_terminated = str_init("terminated");
+static str registration_s = str_init(
+ "\t\n");
+static str registration_e = str_init("\t\n");
+
+//richard: we only use reg unreg refrsh and expire
+static str r_registered = str_init("registered");
+static str r_refreshed = str_init("refreshed");
+static str r_expired = str_init("expired");
+static str r_unregistered = str_init("unregistered");
+static str contact_s =
+ str_init("\t\t\n");
+
+static str contact_s_q =
+ str_init("\t\t\n");
+static str contact_s_params_with_body = str_init(
+ "\t\t\"%.*s\"\n");
+/**NOTIFY XML needs < to be replaced by < and > to be replaced by >*/
+/*For params that need to be fixed we pass in str removing first and last character and replace them with < and >**/
+static str contact_s_params_with_body_fix = str_init(
+ "\t\t\"<%.*s>\"\n");
+static str contact_s_params_no_body = str_init(
+ "\t\t\n"); // 1, but 47
+static str contact_e = str_init("\t\t\n"); // 13, but 14
+
+static str uri_s = str_init("\t\t\t");
+static str uri_e = str_init("\n");
+
+/*We currently only support certain unknown params to be sent in NOTIFY bodies
+ This prevents having compatability issues with UEs including non-standard params in contact header
+ Supported params:
+ */
+static str param_q = str_init("q");
+static str param_video = str_init("video");
+static str param_expires = str_init("expires");
+static str param_sip_instance = str_init("+sip.instance");
+static str param_3gpp_smsip = str_init("+g.3gpp.smsip");
+static str param_3gpp_icsi_ref = str_init("+g.3gpp.icsi-ref");
+
+static int inline supported_param(str *param_name)
+{
+
+ if(strncasecmp(param_name->s, param_q.s, param_name->len) == 0) {
+ return 0;
+ } else if(strncasecmp(param_name->s, param_video.s, param_name->len) == 0) {
+ return 0;
+ } else if(strncasecmp(param_name->s, param_expires.s, param_name->len)
+ == 0) {
+ return 0;
+ } else if(strncasecmp(param_name->s, param_sip_instance.s, param_name->len)
+ == 0) {
+ return 0;
+ } else if(strncasecmp(param_name->s, param_3gpp_smsip.s, param_name->len)
+ == 0) {
+ return 0;
+ } else if(strncasecmp(param_name->s, param_3gpp_icsi_ref.s, param_name->len)
+ == 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static void process_xml_for_contact(str_buffer *buffer, ucontact_t *ptr, int expires, str state, str event)
+{
+ param_t *param;
+ if(ptr->q != -1) {
+ float q = (float)ptr->q / 1000;
+
+ str_buffer_append_str_fmt(buffer, &contact_s_q, ptr,
+ STR_FMT(&state), STR_FMT(&event), expires, q, STR_FMT(&ptr->callid));
+ } else {
+ // XXX: before was it contact_s_q but no q so changed to contact_s
+ str_buffer_append_str_fmt(buffer, &contact_s, ptr,
+ STR_FMT(&state), STR_FMT(&event), expires, STR_FMT(&ptr->callid));
+ }
+
+ LM_DBG("Appending contact address: <%.*s>\n", STR_FMT(&ptr->c));
+
+ str_buffer_append_str(buffer, &uri_s);
+ str_buffer_append_str(buffer, &ptr->c);
+ str_buffer_append_str(buffer, &uri_e);
+
+ param = ptr->params;
+ while(param) {
+ if(supported_param(¶m->name) != 0) {
+ param = param->next;
+ continue;
+ }
+
+ if(param->body.len > 0) {
+ LM_DBG("This contact has params name: [%.*s] body [%.*s]\n",
+ param->name.len, param->name.s, param->body.len, param->body.s);
+
+ if(param->body.s[0] == '<'
+ && param->body.s[param->body.len - 1] == '>') {
+ str tmp = STR_NULL;
+ LM_DBG("This param body starts with '<' and ends with '>' we "
+ "will clean these for the NOTIFY XML with < and "
+ ">\n");
+
+ tmp.len = param->body.len - 2;
+ tmp.s = param->body.s + 1;
+
+ str_buffer_append_str_fmt(buffer, &contact_s_params_with_body_fix,
+ param->name.len, param->name.s, tmp.len, tmp.s);
+ } else {
+ str_buffer_append_str_fmt(buffer, &contact_s_params_with_body,
+ param->name.len, param->name.s,
+ param->body.len, param->body.s);
+ }
+ } else {
+ LM_DBG("This contact has params name: [%.*s] \n",
+ STR_FMT(¶m->name));
+
+ str_buffer_append_str_fmt(
+ buffer, &contact_s_params_no_body, STR_FMT(¶m->name));
+ }
+
+ param = param->next;
+ }
+
+ str_buffer_append_str(buffer, &contact_e);
+}
+
+str build_reginfo_full(urecord_t *record, ucontact_t *contact, str aor[], unsigned int aor_count, int type, int * count)
{
- xmlDocPtr doc = NULL;
- xmlNodePtr root_node = NULL;
- xmlNodePtr registration_node = NULL;
- xmlNodePtr contact_node = NULL;
- xmlNodePtr uri_node = NULL;
- str *body = NULL;
+ str_buffer *buffer = NULL;
+ str x = STR_NULL;
str state = STR_NULL;
str event = STR_NULL;
ucontact_t *ptr;
- char buf[512];
- int reg_active = 0;
time_t cur_time = time(0);
int expires = 0;
int i = 0;
- /* create the XML-Body */
- doc = xmlNewDoc(BAD_CAST "1.0");
- if(doc == 0) {
- LM_ERR("Unable to create XML-Doc\n");
- return NULL;
+ buffer = new_str_buffer();
+ if(!buffer) {
+ LM_ERR("Error allocating str_buffer\n");
+ return x;
}
-
- root_node = xmlNewNode(NULL, BAD_CAST "reginfo");
- if(root_node == 0) {
- LM_ERR("Unable to create reginfo-XML-Element\n");
- xmlFreeDoc(doc);
- return NULL;
+ str_buffer_append_str(buffer, &xml_start);
+ str_buffer_append_str_fmt(
+ buffer, &r_reginfo_s, VERSION_HOLDER, STR_FMT(&r_full));
+
+ ptr = record->contacts;
+ LM_DBG("Records %p\n", ptr);
+ while(ptr) {
+ if(ptr == contact) {
+ switch(type) {
+ //richard we only use registered and refreshed and expired and unregistered
+ case UL_CONTACT_INSERT:
+ case UL_CONTACT_UPDATE:
+ state = r_active;
+ break;
+ case UL_CONTACT_EXPIRE:
+ case UL_CONTACT_DELETE:
+ state = r_terminated;
+ break;
+ default:
+ state = r_active;
+ }
+ } else {
+ if (VALID_CONTACT(ptr, cur_time)) {
+ state = r_active;
+ } else {
+ state = r_terminated;
+ }
+ }
+ ptr = ptr->next;
}
- /* This is our Root-Element: */
- xmlDocSetRootElement(doc, root_node);
-
- xmlNewProp(root_node, BAD_CAST "xmlns",
- BAD_CAST "urn:ietf:params:xml:ns:reginfo");
-
- /* we set the version to 0 but it should be set to the correct value in the pua module */
- xmlNewProp(root_node, BAD_CAST "version", BAD_CAST VERSION_HOLDER);
- xmlNewProp(root_node, BAD_CAST "state", BAD_CAST "full");
for (i = 0; i < aor_count; i++) {
/* Registration Node */
- registration_node =
- xmlNewChild(root_node, NULL, BAD_CAST "registration", NULL);
- if(registration_node == NULL) {
- LM_ERR("while adding child\n");
- goto error;
- }
- reg_active = 0;
-
- /* Add the properties to this Node for AOR and ID: */
- xmlNewProp(registration_node, BAD_CAST "aor", BAD_CAST aor[i].s);
- snprintf(buf, sizeof(buf), "%p.%i", record, i);
- xmlNewProp(registration_node, BAD_CAST "id", BAD_CAST buf);
+ LM_DBG("Registration Node for AOR %.*s [%.*s]\n", aor[i].len, aor[i].s, STR_FMT(&state));
+ str_buffer_append_str_fmt(buffer, ®istration_s,
+ STR_FMT(&aor[i]), record, STR_FMT(&state));
ptr = record->contacts;
LM_DBG("Records %p\n", ptr);
*count = 0;
while(ptr) {
expires = (int)(ptr->expires - cur_time);
- LM_DBG("Contact %.*s (Expires %i, now %i, in %i)\n", ptr->c.len, ptr->c.s, (int)ptr->expires, (int)cur_time, expires);
+ LM_DBG(" Contact %.*s (Expires %i, now %i, in %i)\n", ptr->c.len, ptr->c.s, (int)ptr->expires, (int)cur_time, expires);
+ *count = *count + 1;
if(ptr == contact) {
switch(type) {
//richard we only use registered and refreshed and expired and unregistered
case UL_CONTACT_INSERT:
state = r_active;
event = r_registered;
- reg_active = 1;
break;
case UL_CONTACT_UPDATE:
state = r_active;
event = r_refreshed;
- reg_active = 1;
break;
case UL_CONTACT_EXPIRE:
state = r_terminated;
@@ -156,133 +299,45 @@ str *build_reginfo_full(urecord_t *record, ucontact_t *contact, str aor[], unsig
default:
state = r_active;
event = r_registered;
- reg_active = 1;
}
} else {
if (VALID_CONTACT(ptr, cur_time)) {
state = r_active;
event = r_registered;
- reg_active = 1;
} else {
state = r_terminated;
event = r_expired;
expires = 0;
}
}
- *count = *count + 1;
- LM_DBG("Contact %.*s\n", ptr->c.len, ptr->c.s);
- /* Contact-Node */
- contact_node = xmlNewChild(
- registration_node, NULL, BAD_CAST "contact", NULL);
- if(contact_node == NULL) {
- LM_ERR("while adding child\n");
- goto error;
- }
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%p", ptr);
- xmlNewProp(contact_node, BAD_CAST "id", BAD_CAST buf);
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.*s", state.len, state.s);
- xmlNewProp(contact_node, BAD_CAST "state", BAD_CAST buf);
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.*s", event.len, event.s);
- xmlNewProp(
- contact_node, BAD_CAST "event", BAD_CAST buf);
- memset(buf, 0, sizeof(buf));
- snprintf(
- buf, sizeof(buf), "%i", (int)expires);
- xmlNewProp(contact_node, BAD_CAST "expires", BAD_CAST buf);
- if(ptr->q != Q_UNSPECIFIED) {
- float q = (float)ptr->q / 1000;
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.3f", q);
- xmlNewProp(contact_node, BAD_CAST "q", BAD_CAST buf);
- }
- /* CallID Attribute */
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.*s", ptr->callid.len, ptr->callid.s);
- xmlNewProp(contact_node, BAD_CAST "callid", BAD_CAST buf);
-
- /* CSeq Attribute */
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%d", ptr->cseq);
- xmlNewProp(contact_node, BAD_CAST "cseq", BAD_CAST buf);
-
- if (ptr->received.len) {
- /* received Attribute */
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.*s", ptr->received.len,
- ptr->received.s);
- xmlNewProp(contact_node, BAD_CAST "received", BAD_CAST buf);
- }
-
- if (ptr->path.len) {
- /* path Attribute */
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.*s", ptr->path.len, ptr->path.s);
- xmlNewProp(contact_node, BAD_CAST "path", BAD_CAST buf);
- }
-
- if (ptr->user_agent.len) {
- /* user_agent Attribute */
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.*s", ptr->user_agent.len,
- ptr->user_agent.s);
- xmlNewProp(contact_node, BAD_CAST "user_agent", BAD_CAST buf);
- }
- /* URI-Node */
- memset(buf, 0, sizeof(buf));
- snprintf(buf, sizeof(buf), "%.*s", ptr->c.len, ptr->c.s);
- uri_node = xmlNewChild(
- contact_node, NULL, BAD_CAST "uri", BAD_CAST buf);
- if(uri_node == NULL) {
- LM_ERR("while adding child\n");
- goto error;
- }
+ process_xml_for_contact(buffer, ptr, expires, state, event);
ptr = ptr->next;
}
- /* add registration state (at least one active contact): */
- if(reg_active == 0)
- xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "terminated");
- else
- xmlNewProp(registration_node, BAD_CAST "state", BAD_CAST "active");
+ str_buffer_append_str(buffer, ®istration_e);
}
- /* create the body */
- body = (str *)pkg_malloc(sizeof(str));
- if(body == NULL) {
- LM_ERR("while allocating memory\n");
- return NULL;
+ str_buffer_append_str(buffer, &r_reginfo_e);
+
+ if(str_buffer_has_error(buffer)) {
+ LM_ERR("str_buffer had memory allocating errors while building\n");
+ } else if(!str_buffer_to_str(buffer, &x)) {
+ LM_ERR("no more pkg memory\n");
}
- memset(body, 0, sizeof(str));
- /* Write the XML into the body */
- xmlDocDumpFormatMemory(
- doc, (unsigned char **)(void *)&body->s, &body->len, 1);
+ free_str_buffer(buffer);
- /*free the document */
- xmlFreeDoc(doc);
- xmlCleanupParser();
+ LM_DBG("Returned full reg-info: [%.*s]\n", STR_FMT(&x));
- return body;
-error:
- if(body) {
- if(body->s)
- xmlFree(body->s);
- pkg_free(body);
- }
- if(doc)
- xmlFreeDoc(doc);
- return NULL;
+ return x;
}
void reginfo_usrloc_cb(void *binding, ul_cb_type type, ul_cb_extra *_) {
ucontact_t *contact = (ucontact_t*)binding;
urecord_t *record = NULL;
event_t ev;
- str *body = NULL;
+ str body = {NULL, 0};
publ_info_t publ;
presentity_t presentity;
str content_type;
@@ -303,9 +358,9 @@ void reginfo_usrloc_cb(void *binding, ul_cb_type type, ul_cb_extra *_) {
str s, str_dup;
/* Get the URecord for the contact */
- LM_DBG("Searching urecord for contact-AOR %.*s (Contact %.*s)\n",
+ LM_ERR("Searching urecord for contact-AOR %.*s (Contact %.*s), type %i\n",
contact->aor->len, contact->aor->s,
- contact->c.len, contact->c.s);
+ contact->c.len, contact->c.s, (int)type);
ul.get_urecord(ul_domain, contact->aor, &record);
if (record == NULL) {
LM_DBG("Unable to get urecord for contact-AOR %.*s (Contact %.*s)\n",
@@ -393,12 +448,12 @@ void reginfo_usrloc_cb(void *binding, ul_cb_type type, ul_cb_extra *_) {
/* Build the XML-Body: */
body = build_reginfo_full(record, contact, aorlist, aor_count, type, &count);
-
- if(body == NULL || body->s == NULL) {
+ if(body.s == NULL) {
LM_ERR("Error on creating XML-Body for publish\n");
goto error;
}
- LM_DBG("XML-Body (%i entries):\n%.*s\n", count, body->len, body->s);
+
+ LM_DBG("XML-Body (%i entries):\n%.*s\n", count, body.len, body.s);
if (pres.update_presentity != NULL) {
if (reginfo_event == NULL) {
@@ -444,7 +499,12 @@ void reginfo_usrloc_cb(void *binding, ul_cb_type type, ul_cb_extra *_) {
presentity.new_etag.len, presentity.new_etag.s,
presentity.old_etag.len, presentity.old_etag.s);
- presentity.body = *body;
+ presentity.body = body;
+
+ memset(&new_value, 0, sizeof(int_str_t));
+ new_value.is_str = 1;
+ new_value.s = presentity.new_etag;
+ ul.put_urecord_key(record, ®info_key_etag, &new_value);
/* query the database and update or insert */
if(pres.update_presentity(&presentity) <0)
@@ -456,11 +516,6 @@ void reginfo_usrloc_cb(void *binding, ul_cb_type type, ul_cb_extra *_) {
LM_DBG("etag_new = %i, new_etag %.*s, old_etag %.*s\n", presentity.etag_new,
presentity.new_etag.len, presentity.new_etag.s,
presentity.old_etag.len, presentity.old_etag.s);
-
- memset(&new_value, 0, sizeof(int_str_t));
- new_value.is_str = 1;
- new_value.s = presentity.new_etag;
- ul.put_urecord_key(record, ®info_key_etag, &new_value);
}
if (publish_reginfo && pua.send_publish != NULL) {
@@ -471,7 +526,7 @@ void reginfo_usrloc_cb(void *binding, ul_cb_type type, ul_cb_extra *_) {
memset(&publ, 0, sizeof(publ_info_t));
publ.pres_uri = &uri;
- publ.body = body;
+ publ.body = &body;
id_buf_len = snprintf(id_buf, sizeof(id_buf), "REGINFO_PUBLISH.%.*s@%.*s",
record->aor.len, record->aor.s, record->domain->len, record->domain->s);
publ.id.s = id_buf;
@@ -496,11 +551,8 @@ void reginfo_usrloc_cb(void *binding, ul_cb_type type, ul_cb_extra *_) {
error:
for (i = 0; i < aor_count; i++)
pkg_free(aorlist[i].s);
- if(body) {
- if(body->s)
- xmlFree(body->s);
- pkg_free(body);
- }
+ if(body.s)
+ pkg_free(body.s);
return;
}
diff --git a/modules/usrloc/doc/usrloc_admin.xml b/modules/usrloc/doc/usrloc_admin.xml
index d68a3260d3b..31bb2df7eeb 100644
--- a/modules/usrloc/doc/usrloc_admin.xml
+++ b/modules/usrloc/doc/usrloc_admin.xml
@@ -640,6 +640,26 @@ modparam("usrloc", "attr_column", "attributes")
+
+ params_column (string)
+
+ Name of column containing the parameters of the contact header.
+
+
+
+ Default value is params
.
+
+
+
+ Set params_column parameter
+
+...
+modparam("usrloc", "params_column", "parameters")
+...
+
+
+
+
use_domain (integer)
diff --git a/modules/usrloc/ucontact.c b/modules/usrloc/ucontact.c
index 3ba5bc88f3e..422b93fae4b 100644
--- a/modules/usrloc/ucontact.c
+++ b/modules/usrloc/ucontact.c
@@ -42,6 +42,7 @@
#include "../../dprint.h"
#include "../../db/db.h"
#include "../../db/db_insertq.h"
+#include "../../lib/str_buffer.h"
#include "ul_mod.h"
#include "ul_callback.h"
@@ -56,6 +57,12 @@
#include "usrloc.h"
#include "kv_store.h"
+/**
+ * @brief Format for param string building.
+ *
+ */
+static str param_fmt = str_init("%.*s%s%.*s%s");
+
/*
* Determines the IP address of the next hop on the way to given contact based
* on following URIs: path URI -> received URI -> contact URI
@@ -103,6 +110,9 @@ new_ucontact(str* _dom, str* _aor, str* _contact, ucontact_info_t* _ci)
struct sip_uri ct_uri;
ucontact_t *c;
int_str_t shtag, *shtagp;
+ param_t *prev = NULL;
+ param_t *curr, *param;
+ int first = 1;
c = (ucontact_t*)shm_malloc(sizeof(ucontact_t));
if (!c) {
@@ -149,6 +159,38 @@ new_ucontact(str* _dom, str* _aor, str* _contact, ucontact_info_t* _ci)
if (shm_str_dup( &c->attr, _ci->attr) < 0) goto mem_error;
}
+ /*Copy parameter list into shm**/
+ param = _ci->params;
+ while(param) {
+ /*Copy first param in curr*/
+ curr = shm_malloc(sizeof (param_t));
+ curr->len = param->len;
+ curr->type = param->type;
+ curr->next = 0;
+ if (param->body.len > 0 && param->body.s) {
+ if (shm_str_dup(&curr->body, ¶m->body) < 0) goto mem_error;
+ } else {
+ curr->body.s = 0;
+ curr->body.len = 0;
+ }
+ if (param->name.len > 0 && param->name.s) {
+ if (shm_str_dup(&curr->name, ¶m->name) < 0) goto mem_error;
+ } else {
+ curr->name.s = 0;
+ curr->name.len = 0;
+ }
+
+ if(first) {
+ c->params = curr;
+ first = 0;
+ } else {
+ prev->next = curr;
+ }
+ prev = curr;
+ param = param->next;
+ }
+
+
if (_ci->cdb_key.s && _ci->cdb_key.len) {
if (shm_str_dup( &c->cdb_key, &_ci->cdb_key) < 0) goto mem_error;
}
@@ -211,6 +253,16 @@ new_ucontact(str* _dom, str* _aor, str* _contact, ucontact_info_t* _ci)
if (c->cdb_key.s) shm_free(c->cdb_key.s);
if (c->shtag.s) shm_free(c->shtag.s);
if (c->kv_storage) store_destroy(c->kv_storage);
+ if (c->params) {
+ param = c->params;
+ while(param) {
+ curr = param;
+ param = param->next;
+ if (curr->name.s) shm_free(curr->name.s);
+ if (curr->body.s) shm_free(curr->body.s);
+ shm_free(curr);
+ }
+ }
shm_free(c);
return NULL;
}
@@ -222,6 +274,8 @@ new_ucontact(str* _dom, str* _aor, str* _contact, ucontact_info_t* _ci)
*/
void free_ucontact(ucontact_t* _c)
{
+ param_t *curr, *param;
+
if (!_c) return;
if (_c->flags & FL_EXTRA_HOP)
@@ -237,6 +291,16 @@ void free_ucontact(ucontact_t* _c)
if (_c->cdb_key.s) shm_free(_c->cdb_key.s);
if (_c->shtag.s) shm_free(_c->shtag.s);
if (_c->kv_storage) store_destroy(_c->kv_storage);
+ if (_c->params) {
+ param = _c->params;
+ while(param) {
+ curr = param;
+ param = param->next;
+ if (curr->name.s) shm_free(curr->name.s);
+ if (curr->body.s) shm_free(curr->body.s);
+ shm_free(curr);
+ }
+ }
skip_fields:
shm_free( _c );
@@ -520,6 +584,8 @@ int db_insert_ucontact(ucontact_t* _c,query_list_t **ins_list, int update)
{
int nr_vals = UL_COLS - 1;
int start = 0;
+ str_buffer *buffer = NULL;
+ str params = STR_NULL;
static db_ps_t myI_ps = NULL;
static db_ps_t myR_ps = NULL;
@@ -555,6 +621,7 @@ int db_insert_ucontact(ucontact_t* _c,query_list_t **ins_list, int update)
keys[15] = &sip_instance_col;
keys[16] = &kv_store_col;
keys[17] = &attr_col;
+ keys[18] = ¶ms_col;
keys[UL_COLS - 1] = &domain_col; /* "domain" always stays last */
memset(vals, 0, sizeof vals);
@@ -653,6 +720,49 @@ int db_insert_ucontact(ucontact_t* _c,query_list_t **ins_list, int update)
vals[17].val.str_val.len = _c->attr.len;
}
+ vals[18].type = DB_STR;
+ if (_c->params == 0) {
+ vals[18].nul = 1;
+ } else {
+ buffer = new_str_buffer();
+ if(!buffer) {
+ LM_ERR("Error allocating str_buffer\n");
+ return -1;
+ }
+
+ {
+ param_t *param = _c->params;
+ while(param) {
+ if(param->name.len > 0) {
+ if(param->body.len > 0) {
+ str_buffer_append_str_fmt(buffer, ¶m_fmt,
+ param->name.len, param->name.s, "=", param->body.len, param->body.s,
+ param->next ? ";" : "");
+ } else {
+ str_buffer_append_str_fmt(buffer, ¶m_fmt,
+ param->name.len, param->name.s, "", 0, NULL,
+ param->next ? ";" : "");
+ }
+ }
+
+ param = param->next;
+ }
+ }
+
+ if(str_buffer_has_error(buffer)) {
+ LM_ERR("str_buffer had memory allocating errors while building\n");
+ free_str_buffer(buffer);
+ return -1;
+ } else if(!str_buffer_to_str(buffer, ¶ms)) {
+ LM_ERR("str_buffer unable to get result\n");
+ free_str_buffer(buffer);
+ return -1;
+ }
+
+ free_str_buffer(buffer);
+ vals[18].val.str_val = params;
+ }
+
if (use_domain) {
vals[UL_COLS - 1].type = DB_STR;
@@ -698,9 +808,11 @@ int db_insert_ucontact(ucontact_t* _c,query_list_t **ins_list, int update)
}
store_free_buffer(&vals[16].val.str_val);
+ pkg_free(params.s);
return 0;
out_err:
store_free_buffer(&vals[16].val.str_val);
+ pkg_free(params.s);
return -1;
}
diff --git a/modules/usrloc/ucontact.h b/modules/usrloc/ucontact.h
index 238e38eb4c8..49db40a24db 100644
--- a/modules/usrloc/ucontact.h
+++ b/modules/usrloc/ucontact.h
@@ -126,6 +126,7 @@ typedef struct ucontact {
time_t last_modified; /*!< When the record was last modified */
unsigned int methods; /*!< Supported methods */
str attr; /*!< Additional registration info */
+ param_t *params; /*!< Params header details> */
struct proxy_l next_hop;/*!< SIP-wise determined next hop */
unsigned short label; /*!< label to find the contact in contact list>*/
int sipping_latency; /*!< useconds; not restart-persistent >*/
@@ -167,6 +168,7 @@ typedef struct ucontact_info {
time_t last_modified;
str *packed_kv_storage;
str *attr;
+ param_t *params; /*!< Params header details> */
str shtag;
str cdb_key;
int refresh_time;
diff --git a/modules/usrloc/udomain.c b/modules/usrloc/udomain.c
index f9f6d0ce60e..80e5770a5fe 100644
--- a/modules/usrloc/udomain.c
+++ b/modules/usrloc/udomain.c
@@ -167,6 +167,7 @@ cdb_ctdict2info(const cdb_dict_t *ct_fields, str *contact)
static ucontact_info_t ci;
static str callid, ua, received, host, path, instance;
static str attr;
+ param_hooks_t hooks;
struct list_head *_;
cdb_pair_t *pair;
@@ -217,9 +218,19 @@ cdb_ctdict2info(const cdb_dict_t *ct_fields, str *contact)
ci.methods = pair->val.val.i32;
break;
case 'p':
- path = pair->val.val.st;
- ci.path = &path;
- break;
+ switch (pair->key.name.s[2]) {
+ // path
+ case 't':
+ path = pair->val.val.st;
+ ci.path = &path;
+ break;
+ // params
+ case 'r':
+ if(parse_params(&pair->val.val.st, CLASS_CONTACT, &hooks, &ci.params) < 0) {
+ LM_WARN("Error while parsing parameters: %.*s\n", pair->val.val.st.len, pair->val.val.st.s);
+ }
+ break;
+ }
case 'q':
ci.q = pair->val.val.i32;
break;
@@ -271,9 +282,10 @@ static inline ucontact_info_t* dbrow2info(db_val_t *vals, str *contact)
{
static ucontact_info_t ci;
static str callid, ua, received, host, path, instance;
- static str attr, packed_kv;
+ static str attr, packed_kv, params;
int port, proto;
char *p;
+ param_hooks_t hooks;
memset( &ci, 0, sizeof(ucontact_info_t));
@@ -416,6 +428,17 @@ static inline ucontact_info_t* dbrow2info(db_val_t *vals, str *contact)
ci.attr = &attr;
+ params.s = (char*)VAL_STRING(vals+17);
+ if (VAL_NULL(vals+17) || !attr.s) {
+ params.s = NULL;
+ params.len = 0;
+ } else {
+ params.len = strlen(params.s);
+ if(parse_params(¶ms, CLASS_CONTACT, &hooks, &ci.params) < 0) {
+ LM_WARN("Error while parsing parameters: %.*s\n", params.len, params.s);
+ }
+ }
+
return &ci;
}
@@ -464,6 +487,7 @@ int preload_udomain(db_con_t* _c, udomain_t* _d)
columns[15] = &sip_instance_col;
columns[16] = &kv_store_col;
columns[17] = &attr_col;
+ columns[18] = ¶ms_col;
columns[UL_COLS - 1] = &domain_col; /* "domain" always stays last */
if (ul_dbf.use_table(_c, _d->name) < 0) {
@@ -765,6 +789,7 @@ urecord_t* db_load_urecord(db_con_t* _c, udomain_t* _d, str *_aor)
columns[14] = &sip_instance_col;
columns[15] = &kv_store_col;
columns[16] = &attr_col;
+ columns[17] = ¶ms_col;
if (desc_time_order)
order = &last_mod_col;
diff --git a/modules/usrloc/ul_cluster.c b/modules/usrloc/ul_cluster.c
index 9a4d0f100b6..a66f3c8cc4f 100644
--- a/modules/usrloc/ul_cluster.c
+++ b/modules/usrloc/ul_cluster.c
@@ -21,6 +21,7 @@
*/
#include "../../forward.h"
+#include "../../lib/str_buffer.h"
#include "ul_cluster.h"
#include "ul_mod.h"
@@ -32,6 +33,12 @@ str contact_repl_cap = str_init("usrloc-contact-repl");
struct clusterer_binds clusterer_api;
str ul_shtag_key = str_init("_st");
+/**
+ * @brief Format for param string building.
+ *
+ */
+static str param_fmt = str_init("%.*s%s%.*s%s");
+
int ul_init_cluster(void)
{
if (location_cluster == 0)
@@ -208,6 +215,8 @@ void bin_push_contact(bin_packet_t *packet, urecord_t *r, ucontact_t *c,
const struct ct_match *match)
{
str st;
+ str_buffer *buffer = NULL;
+ str params = STR_NULL;
bin_push_str(packet, r->domain);
bin_push_str(packet, &r->aor);
@@ -247,6 +256,39 @@ void bin_push_contact(bin_packet_t *packet, urecord_t *r, ucontact_t *c,
bin_push_str(packet, &st);
store_free_buffer(&st);
+ buffer = new_str_buffer();
+ if(!buffer) {
+ LM_ERR("Error allocating str_buffer\n");
+ } else {
+ param_t *param = c->params;
+ while(param) {
+ if(param->name.len > 0) {
+ if(param->body.len > 0) {
+ str_buffer_append_str_fmt(buffer, ¶m_fmt,
+ param->name.len, param->name.s, "=", param->body.len, param->body.s,
+ param->next ? ";" : "");
+ } else {
+ str_buffer_append_str_fmt(buffer, ¶m_fmt,
+ param->name.len, param->name.s, "", 0, NULL,
+ param->next ? ";" : "");
+ }
+ }
+
+ param = param->next;
+ }
+ }
+
+ if(str_buffer_has_error(buffer)) {
+ LM_ERR("str_buffer had memory allocating errors while building\n");
+ free_str_buffer(buffer);
+ } else if(!str_buffer_to_str(buffer, ¶ms)) {
+ LM_ERR("str_buffer unable to get result\n");
+ free_str_buffer(buffer);
+ }
+ free_str_buffer(buffer);
+ bin_push_str(packet, ¶ms);
+ pkg_free(params.s);
+
bin_push_ctmatch(packet, match);
}
@@ -465,7 +507,6 @@ static int receive_urecord_insert(bin_packet_t *packet)
bin_pop_str(packet, &kv_str);
r->kv_storage = store_deserialize(&kv_str);
}
-
out:
unlock_udomain(domain, &aor);
@@ -515,7 +556,7 @@ static int receive_ucontact_insert(bin_packet_t *packet)
{
static ucontact_info_t ci;
static str d, aor, contact_str, callid,
- user_agent, path, attr, st, sock, kv_str, cflags_str;
+ user_agent, path, attr, st, sock, kv_str, cflags_str, params_str;
udomain_t *domain;
urecord_t *record;
ucontact_t *contact;
@@ -524,6 +565,7 @@ static int receive_ucontact_insert(bin_packet_t *packet)
unsigned int rlabel;
struct ct_match cmatch = {CT_MATCH_NONE, NULL};
short pkg_ver = get_bin_pkg_version(packet);
+ param_hooks_t hooks;
memset(&ci, 0, sizeof ci);
@@ -589,8 +631,16 @@ static int receive_ucontact_insert(bin_packet_t *packet)
bin_pop_str(packet, &st);
memcpy(&ci.last_modified, st.s, sizeof ci.last_modified);
- bin_pop_str(packet, &kv_str);
- ci.packed_kv_storage = &kv_str;
+ if (pkg_ver >= UL_BIN_V5) {
+ bin_pop_str(packet, &kv_str);
+ ci.packed_kv_storage = &kv_str;
+ }
+ if (pkg_ver >= UL_BIN_V6) {
+ bin_pop_str(packet, ¶ms_str);
+ if(parse_params(¶ms_str, CLASS_CONTACT, &hooks, &ci.params) < 0) {
+ LM_WARN("Error while parsing parameters: %.*s\n", params_str.len, params_str.s);
+ }
+ }
if (pkg_ver <= UL_BIN_V2)
cmatch = (struct ct_match){CT_MATCH_CONTACT_CALLID, NULL};
diff --git a/modules/usrloc/ul_cluster.h b/modules/usrloc/ul_cluster.h
index 8caf2c51a9a..448978fa455 100644
--- a/modules/usrloc/ul_cluster.h
+++ b/modules/usrloc/ul_cluster.h
@@ -42,7 +42,8 @@
#define UL_BIN_V3 3 // added "cmatch" (default: CT_MATCH_CONTACT_CALLID)
#define UL_BIN_V4 4 // changed 'ct.cflags' from int bitmask to string repr
#define UL_BIN_V5 5 // added 'r.kv_storage' to AoR INSERT packets
-#define UL_BIN_VERSION UL_BIN_V5
+#define UL_BIN_V6 6 // added 'r.params' to AoR INSERT packets
+#define UL_BIN_VERSION UL_BIN_V6
extern int location_cluster;
extern struct clusterer_binds clusterer_api;
diff --git a/modules/usrloc/ul_mod.c b/modules/usrloc/ul_mod.c
index 55331b88f5e..eff6947d730 100644
--- a/modules/usrloc/ul_mod.c
+++ b/modules/usrloc/ul_mod.c
@@ -83,6 +83,7 @@
#define SIP_INSTANCE_COL "sip_instance"
#define KV_STORE_COL "kv_store"
#define ATTR_COL "attr"
+#define PARAMS_COL "params"
static int mod_init(void); /*!< Module initialization */
static void destroy(void); /*!< Module destroy */
@@ -133,6 +134,7 @@ str kv_store_col = str_init(KV_STORE_COL); /*!< Name of column containing ge
str attr_col = str_init(ATTR_COL); /*!< Name of column containing additional info */
str sip_instance_col = str_init(SIP_INSTANCE_COL);
str contactid_col = str_init(CONTACTID_COL);
+str params_col = str_init(PARAMS_COL);
str db_url = STR_NULL; /*!< Database URL */
str cdb_url = STR_NULL; /*!< Cache Database URL */
@@ -254,16 +256,19 @@ static const param_export_t params[] = {
{"methods_column", STR_PARAM, &methods_col.s },
{"sip_instance_column",STR_PARAM, &sip_instance_col.s},
{"kv_store_column", STR_PARAM, &kv_store_col.s },
+ {"params_column", STR_PARAM, ¶ms_col.s },
{"mi_dump_kv_store", INT_PARAM, &mi_dump_kv_store },
{"latency_event_min_us", INT_PARAM, &latency_event_min_us },
{"latency_event_min_us_delta", INT_PARAM, &latency_event_min_us_delta },
{"attr_column", STR_PARAM, &attr_col.s },
+ {"params_column", STR_PARAM, ¶ms_col.s },
{"matching_mode", INT_PARAM, &matching_mode },
{"cseq_delay", INT_PARAM, &cseq_delay },
{"hash_size", INT_PARAM, &ul_hash_size },
{"nat_bflag", STR_PARAM, &nat_bflag_str },
{"contact_refresh_timer", INT_PARAM, &ct_refresh_timer },
+
/* data replication through clusterer using TCP binary packets */
{ "location_cluster", INT_PARAM, &location_cluster },
{ "ha_cluster", INT_PARAM, &ul_ha_cluster },
diff --git a/modules/usrloc/ul_mod.h b/modules/usrloc/ul_mod.h
index 512cb503ea8..1a97f30d4cb 100644
--- a/modules/usrloc/ul_mod.h
+++ b/modules/usrloc/ul_mod.h
@@ -97,9 +97,9 @@ static inline int tags_in_use(void)
*/
-#define UL_TABLE_VERSION 1013
+#define UL_TABLE_VERSION 1014
-#define UL_COLS 19
+#define UL_COLS 20
extern str contactid_col;
extern str user_col;
extern str domain_col;
@@ -119,6 +119,7 @@ extern str kv_store_col;
extern str attr_col;
extern str last_mod_col;
extern str sip_instance_col;
+extern str params_col;
extern str db_url;
extern enum usrloc_modes db_mode;
diff --git a/modules/usrloc/urecord.c b/modules/usrloc/urecord.c
index 159fda567a2..25ff65e6fa4 100644
--- a/modules/usrloc/urecord.c
+++ b/modules/usrloc/urecord.c
@@ -40,6 +40,7 @@
#include "../../ut.h"
#include "../../hash_func.h"
#include "../../db/db_insertq.h"
+#include "../../lib/str_buffer.h"
#include "ul_mod.h"
#include "utime.h"
@@ -491,12 +492,20 @@ int db_delete_urecord(urecord_t* _r)
return 0;
}
+/**
+ * @brief Format for param string building.
+ *
+ */
+static str param_fmt = str_init("%.*s%s%.*s%s");
+
int cdb_add_ct_update(cdb_dict_t *updates, const ucontact_t *ct, char remove)
{
cdb_pair_t *pair;
cdb_dict_t *ct_fields;
cdb_key_t contacts_key;
str printed_flags;
+ str_buffer *buffer = NULL;
+ str params = STR_NULL;
cdb_key_init(&contacts_key, "contacts");
@@ -583,8 +592,52 @@ int cdb_add_ct_update(cdb_dict_t *updates, const ucontact_t *ct, char remove)
return -1;
}
+ if (ct->params == 0) {
+ if (CDB_DICT_ADD_NULL(ct_fields, "params") != 0)
+ return -1;
+ } else {
+ buffer = new_str_buffer();
+ if(!buffer) {
+ LM_ERR("Error allocating str_buffer\n");
+ return -1;
+ }
+
+ {
+ param_t *param = ct->params;
+ while(param) {
+ if(param->name.len > 0) {
+ if(param->body.len > 0) {
+ str_buffer_append_str_fmt(buffer, ¶m_fmt,
+ param->name.len, param->name.s, "=", param->body.len, param->body.s,
+ param->next ? ";" : "");
+ } else {
+ str_buffer_append_str_fmt(buffer, ¶m_fmt,
+ param->name.len, param->name.s, "", 0, NULL,
+ param->next ? ";" : "");
+ }
+ }
+
+ param = param->next;
+ }
+ }
+
+ if(str_buffer_has_error(buffer)) {
+ LM_ERR("str_buffer had memory allocating errors while building\n");
+ free_str_buffer(buffer);
+ return -1;
+ } else if(!str_buffer_to_str(buffer, ¶ms)) {
+ LM_ERR("str_buffer unable to get result\n");
+ free_str_buffer(buffer);
+ return -1;
+ }
+ free_str_buffer(buffer);
+ if (CDB_DICT_ADD_STR(ct_fields, "params", ¶ms) != 0)
+ return -1;
+ }
+
done:
cdb_dict_add(pair, updates);
+ pkg_free(params.s);
return 0;
}
diff --git a/scripts/db_berkeley/opensips/location b/scripts/db_berkeley/opensips/location
index 0395417fad8..a7e0fbfbe2f 100644
--- a/scripts/db_berkeley/opensips/location
+++ b/scripts/db_berkeley/opensips/location
@@ -1,5 +1,5 @@
METADATA_COLUMNS
-contact_id(int) username(str) domain(str) contact(str) received(str) path(str) expires(int) q(double) callid(str) cseq(int) last_modified(datetime) flags(int) cflags(str) user_agent(str) socket(str) methods(int) sip_instance(str) kv_store(str) attr(str)
+contact_id(int) username(str) domain(str) contact(str) received(str) path(str) expires(int) q(double) callid(str) cseq(int) last_modified(datetime) flags(int) cflags(str) user_agent(str) socket(str) methods(int) sip_instance(str) kv_store(str) attr(str) params(str)
METADATA_KEY
1 2
METADATA_READONLY
@@ -7,4 +7,4 @@ METADATA_READONLY
METADATA_LOGFLAGS
0
METADATA_DEFAULTS
-NIL|''|NULL|NIL|NULL|NULL|NIL|1.0|'Default-Call-ID'|13|'1900-01-01 00:00:01'|0|NULL|''|NULL|NULL|NULL|NULL|NULL
+NIL|''|NULL|NIL|NULL|NULL|NIL|1.0|'Default-Call-ID'|13|'1900-01-01 00:00:01'|0|NULL|''|NULL|NULL|NULL|NULL|NULL|NULL
diff --git a/scripts/db_berkeley/opensips/version b/scripts/db_berkeley/opensips/version
index bb4823cde84..b2eae91faa8 100644
--- a/scripts/db_berkeley/opensips/version
+++ b/scripts/db_berkeley/opensips/version
@@ -95,7 +95,7 @@ jwt_secrets|1
load_balancer|
load_balancer|3
location|
-location|1013
+location|1014
missed_calls|
missed_calls|5
presentity|
diff --git a/scripts/dbtext/opensips/location b/scripts/dbtext/opensips/location
index 3f75041112c..69ce96aa268 100644
--- a/scripts/dbtext/opensips/location
+++ b/scripts/dbtext/opensips/location
@@ -1 +1 @@
-contact_id(long,auto) username(string) domain(string,null) contact(string) received(string,null) path(string,null) expires(int) q(double) callid(string) cseq(int) last_modified(int) flags(int) cflags(string,null) user_agent(string) socket(string,null) methods(int,null) sip_instance(string,null) kv_store(string,null) attr(string,null)
+contact_id(long,auto) username(string) domain(string,null) contact(string) received(string,null) path(string,null) expires(int) q(double) callid(string) cseq(int) last_modified(int) flags(int) cflags(string,null) user_agent(string) socket(string,null) methods(int,null) sip_instance(string,null) kv_store(string,null) attr(string,null) params(string,null)
diff --git a/scripts/dbtext/opensips/version b/scripts/dbtext/opensips/version
index b674b35cb22..328642e7199 100644
--- a/scripts/dbtext/opensips/version
+++ b/scripts/dbtext/opensips/version
@@ -40,7 +40,7 @@ janus:1
jwt_profiles:1
jwt_secrets:1
load_balancer:3
-location:1013
+location:1014
missed_calls:5
presentity:5
pua:9
diff --git a/scripts/mysql/usrloc-create.sql b/scripts/mysql/usrloc-create.sql
index 83b85dc2d2b..66a774dd034 100644
--- a/scripts/mysql/usrloc-create.sql
+++ b/scripts/mysql/usrloc-create.sql
@@ -1,4 +1,4 @@
-INSERT INTO version (table_name, table_version) values ('location','1013');
+INSERT INTO version (table_name, table_version) values ('location','1014');
CREATE TABLE location (
contact_id BIGINT(10) UNSIGNED AUTO_INCREMENT PRIMARY KEY NOT NULL,
username CHAR(64) DEFAULT '' NOT NULL,
@@ -18,6 +18,7 @@ CREATE TABLE location (
methods INT(11) DEFAULT NULL,
sip_instance CHAR(255) DEFAULT NULL,
kv_store TEXT(512) DEFAULT NULL,
- attr CHAR(255) DEFAULT NULL
+ attr CHAR(255) DEFAULT NULL,
+ params TEXT(512) DEFAULT NULL
) ENGINE=InnoDB;
diff --git a/scripts/oracle/usrloc-create.sql b/scripts/oracle/usrloc-create.sql
index f27e5e1ab7a..f5b53ce4a1e 100644
--- a/scripts/oracle/usrloc-create.sql
+++ b/scripts/oracle/usrloc-create.sql
@@ -1,4 +1,4 @@
-INSERT INTO version (table_name, table_version) values ('location','1013');
+INSERT INTO version (table_name, table_version) values ('location','1014');
CREATE TABLE location (
contact_id BIGINT(10) PRIMARY KEY,
username VARCHAR2(64) DEFAULT '',
@@ -18,7 +18,8 @@ CREATE TABLE location (
methods NUMBER(10) DEFAULT NULL,
sip_instance VARCHAR2(255) DEFAULT NULL,
kv_store CLOB(512) DEFAULT NULL,
- attr VARCHAR2(255) DEFAULT NULL
+ attr VARCHAR2(255) DEFAULT NULL,
+ params CLOB(512) DEFAULT NULL
);
CREATE OR REPLACE TRIGGER location_tr
diff --git a/scripts/pi_http/pi_framework.xml b/scripts/pi_http/pi_framework.xml
index 3104ae4137a..6b264d058c0 100644
--- a/scripts/pi_http/pi_framework.xml
+++ b/scripts/pi_http/pi_framework.xml
@@ -1133,6 +1133,7 @@
sip_instanceDB_STR
kv_storeDB_BLOB
attrDB_STR
+ paramsDB_BLOB