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") +
+ <varname>params_column</varname> (string) + + Name of column containing the parameters of the contact header. + + + + Default value is params. + + + + Set <varname>params_column</varname> parameter + +... +modparam("usrloc", "params_column", "parameters") +... + + +
+
<varname>use_domain</varname> (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