diff --git a/lib/sliblist.c b/lib/sliblist.c
index 282b2f48fd9..0620dc43fa3 100644
--- a/lib/sliblist.c
+++ b/lib/sliblist.c
@@ -215,3 +215,35 @@ void slinkedl_list_destroy(slinkedl_list_t *list)
return;
}
+int slinkedl_delete(slinkedl_list_t *list, slinkedl_match_f *func, void *data)
+{
+ slinkedl_element_t *prev = NULL;
+ slinkedl_element_t *element;
+ int ret = 0;
+
+ if (!list || !func)
+ return 0;
+
+ element = list->head;
+ while (element) {
+ ret = (*func)(element->data, data, NULL);
+ if (ret) {
+ if (prev)
+ prev->next = element->next;
+ else
+ list->head = element->next;
+
+ /* fix tail if we removed the last element */
+ if (element == list->tail)
+ list->tail = prev;
+
+ list->dealloc(element);
+ return ret;
+ }
+
+ prev = element;
+ element = element->next;
+ }
+
+ return 0; /* not found */
+}
diff --git a/lib/sliblist.h b/lib/sliblist.h
index c82cb3be7ed..3878d00f037 100644
--- a/lib/sliblist.h
+++ b/lib/sliblist.h
@@ -157,6 +157,18 @@ int slinkedl_replace(slinkedl_list_t *list, slinkedl_match_f match,
*/
void *slinkedl_peek(slinkedl_list_t *list);
+/**
+ * Delete from the list the first element that matches is found
+ *
+ * @psram lit The list to traverse.
+ * @param match_func The function used for matching the list element to delete
+ * @param data The data to be used by run_data function.
+ * @return The return code from last match_func call.
+ * @see slinkedl_match_f()
+ */
+int slinkedl_delete(slinkedl_list_t *list, slinkedl_match_f *match_func, void *data);
+
+
/**
* Destroy the list.
* Any element in the list will be silently destroyed.
diff --git a/mi/mi.h b/mi/mi.h
index e5fb13d22d3..1ab44765dfd 100644
--- a/mi/mi.h
+++ b/mi/mi.h
@@ -27,7 +27,7 @@
#include "../str.h"
#include "item.h"
-#define MAX_MI_PARAMS 10
+#define MAX_MI_PARAMS 20
#define MAX_MI_RECIPES 48
/* async MI command */
diff --git a/modules/uac_registrant/doc/uac_registrant_admin.xml b/modules/uac_registrant/doc/uac_registrant_admin.xml
index 80e98c845a3..ace02ea273d 100644
--- a/modules/uac_registrant/doc/uac_registrant_admin.xml
+++ b/modules/uac_registrant/doc/uac_registrant_admin.xml
@@ -521,6 +521,26 @@ modparam("uac_registrant", "state_column", "status")
+
+ reregister_expiry_percentage (integer)
+
+ Percentage describing how much sooner a RE-REGISTER needs to be send based on the Expiry. a 100 value means the RE-REGISTER will be send right on the edge of expiry ( old behavior ), which might lead to registration loss. a 90 value means the RE-REGISTER will be sent sooner , at 90% of the Expiry, etc.
+
+
+
+ Default value is 100
.
+
+
+
+ Set reregister_expiry_percentage
parameter
+
+...
+modparam("uac_registrant", "reregister_expiry_percentage", 90)
+...
+
+
+
+
@@ -687,6 +707,271 @@ opensips-cli -x mi reg_force_register sip:alice@opensips.org sip:alice@127.0.0.
+
+ reg_upsert
+ Inserts or updates the in-memory contents of the AOR/Contact/Registrar. No Database queries are done when calling this MI command, all parameters are passed via MI
+ Name: reg_upsert
+ Parameters:
+
+
+ aor - URI defining the address
+
+
+ contact - Contact URI
+
+
+ registrar - URI pointing to the remote registrar
+
+
+ proxy - URI of a registration proxy
+
+
+ third_party_registrant - 3rd party registrant
+
+
+ username - the username for auth purposes
+
+
+
+ password - the password for auth purposes
+
+
+ binding_params - params to be added to the registration
+
+
+ expiry - number of seconds that the registration will be valid
+
+
+ forced_socket - opensips socket to send out the register out through
+
+
+ cluster_shtag - the sharing tag for this registration
+
+
+ state - 0 for enabled, 1 for disabled
+
+
+ MI FIFO Command Format:
+
+opensips-cli -x mi reg_upsert aor=sip:vlad@test.com contact=sip:test@localhost registrar=sip:127.0.0.1:5061 proxy="" third_party_registrant="" username="vlad" password="1234" binding_params="" expiry=60 forced_socket="" cluster_shtag="" state=0
+
+
+
+
+ reg_delete
+ Deletes the in-memory contents of the AOR/Contact/Registrar. No Database queries are done when calling this MI command, all parameters are passed via MI
+ Name: reg_delete
+ Parameters:
+
+
+ aor - URI defining the address
+
+
+ contact - Contact URI
+
+
+ registrar - URI pointing to the remote registrar
+
+
+ MI FIFO Command Format:
+
+opensips-cli -x mi reg_delete aor=sip:vlad@test.com contact=sip:test@localhost registrar=sip:127.0.0.1:5061
+
+
+
+
+
+
+ Exported Events
+
+
+ E_REGISTRANT_REGISTERING
+
+
+ This event is raised when the module sent the initial REGISTER and started the registration process.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_AUTHENTICATING
+
+
+ This event is raised when the initial REGISTER has been challenged and a new REGISTER with credentials has been sent out.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_REGISTERED
+
+
+ This event is raised when a REGISTER has been 200 OKd.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_REGISTER_TIMEOUT
+
+
+ This event is raised when a REGISTER received no reply from the registrar.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_INTERNAL_ERROR
+
+
+ This event is raised when a REGISTER procesing was stopped due to an internal OpenSIPS error.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_WRONG_CREDENTIALS
+
+
+ This event is raised when a REGISTER with credentials was still rejected by the registrar
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_REGISTRAR_ERROR
+
+
+ This event is raised when a REGISTER is rejected by the registrar with a non-standard sip code.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_UNREGISTERING
+
+
+ This event is raised when a de-REGISTER is sent by OpenSIPS.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
+
+
+ E_REGISTRANT_AUTHENTICATING_UNREGISTER
+
+
+ This event is raised when a de-REGISTER is challenged and auth is sent by OpenSIPS.
+
+ Parameters:
+
+
+ aor - the AOR
+
+
+ contact - the Contact
+
+
+ registrar - the Registrar
+
+
+
+
diff --git a/modules/uac_registrant/reg_events.c b/modules/uac_registrant/reg_events.c
new file mode 100644
index 00000000000..2fe3472fb16
--- /dev/null
+++ b/modules/uac_registrant/reg_events.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2019 OpenSIPS Project
+ *
+ * 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 "../../evi/evi_params.h"
+#include "../../evi/evi_modules.h"
+
+#include "reg_events.h"
+
+static str reg_event_aor = str_init("aor");
+static str reg_event_contact = str_init("contact");
+static str reg_event_registrar = str_init("registrar");
+
+static str reg_registering_ev_name = str_init("E_REGISTRANT_REGISTERING");
+static event_id_t reg_registering_ev_id = EVI_ERROR;
+static evi_params_p reg_registering_event_params;
+static evi_param_p reg_registering_aor_p, reg_registering_contact_p, reg_registering_registrar_p;
+
+static str reg_authenticating_ev_name = str_init("E_REGISTRANT_AUTHENTICATING");
+static event_id_t reg_authenticating_ev_id = EVI_ERROR;
+static evi_params_p reg_authenticating_event_params;
+static evi_param_p reg_authenticating_aor_p, reg_authenticating_contact_p, reg_authenticating_registrar_p;
+
+static str reg_registered_ev_name = str_init("E_REGISTRANT_REGISTERED");
+static event_id_t reg_registered_ev_id = EVI_ERROR;
+static evi_params_p reg_registered_event_params;
+static evi_param_p reg_registered_aor_p, reg_registered_contact_p, reg_registered_registrar_p;
+
+static str reg_register_timeout_ev_name = str_init("E_REGISTRANT_REGISTER_TIMEOUT");
+static event_id_t reg_register_timeout_ev_id = EVI_ERROR;
+static evi_params_p reg_register_timeout_event_params;
+static evi_param_p reg_register_timeout_aor_p, reg_register_timeout_contact_p, reg_register_timeout_registrar_p;
+
+static str reg_internal_error_ev_name = str_init("E_REGISTRANT_INTERNAL_ERROR");
+static event_id_t reg_internal_error_ev_id = EVI_ERROR;
+static evi_params_p reg_internal_error_event_params;
+static evi_param_p reg_internal_error_aor_p, reg_internal_error_contact_p, reg_internal_error_registrar_p;
+
+static str reg_wrong_credentials_ev_name = str_init("E_REGISTRANT_WRONG_CREDENTIALS");
+static event_id_t reg_wrong_credentials_ev_id = EVI_ERROR;
+static evi_params_p reg_wrong_credentials_event_params;
+static evi_param_p reg_wrong_credentials_aor_p, reg_wrong_credentials_contact_p, reg_wrong_credentials_registrar_p;
+
+static str reg_registrar_error_ev_name = str_init("E_REGISTRANT_REGISTRAR_ERROR");
+static event_id_t reg_registrar_error_ev_id = EVI_ERROR;
+static evi_params_p reg_registrar_error_event_params;
+static evi_param_p reg_registrar_error_aor_p, reg_registrar_error_contact_p, reg_registrar_error_registrar_p;
+
+static str reg_unregistering_ev_name = str_init("E_REGISTRANT_UNREGISTERING");
+static event_id_t reg_unregistering_ev_id = EVI_ERROR;
+static evi_params_p reg_unregistering_event_params;
+static evi_param_p reg_unregistering_aor_p, reg_unregistering_contact_p, reg_unregistering_registrar_p;
+
+static str reg_authenticating_unregister_ev_name = str_init("E_REGISTRANT_AUTHENTICATING_UNREGISTER");
+static event_id_t reg_authenticating_unregister_ev_id = EVI_ERROR;
+static evi_params_p reg_authenticating_unregister_event_params;
+static evi_param_p reg_authenticating_unregister_aor_p, reg_authenticating_unregister_contact_p, reg_authenticating_unregister_registrar_p;
+
+#define EVI_PUBLISH_OR_FAIL(_ev_id, _ev_name) \
+ do { \
+ (_ev_id) = evi_publish_event((_ev_name)); \
+ if ((_ev_id) == EVI_ERROR) { \
+ LM_ERR("cannot register %.*s event\n", \
+ (_ev_name).len, (_ev_name).s); \
+ return -1; \
+ } \
+ } while (0)
+
+#define EVI_PARAMS_ALLOC_OR_FAIL(_params_ptr) \
+ do { \
+ (_params_ptr) = pkg_malloc(sizeof(evi_params_t)); \
+ if ((_params_ptr) == NULL) { \
+ LM_ERR("no more pkg mem\n"); \
+ return -1; \
+ } \
+ memset((_params_ptr), 0, sizeof(evi_params_t)); \
+ } while (0)
+
+#define EVI_PARAM_CREATE_OR_FAIL(_out_param, _params, _spec) \
+ do { \
+ (_out_param) = evi_param_create((_params), (_spec)); \
+ if ((_out_param) == NULL) { \
+ LM_ERR("Failed to create param \n"); \
+ return -1; \
+ } \
+ } while (0)
+
+#define INIT_REG_EVENT(_prefix) \
+ do { \
+ EVI_PUBLISH_OR_FAIL(_prefix##_ev_id, _prefix##_ev_name); \
+ EVI_PARAMS_ALLOC_OR_FAIL(_prefix##_event_params); \
+ EVI_PARAM_CREATE_OR_FAIL(_prefix##_aor_p, \
+ _prefix##_event_params, ®_event_aor); \
+ EVI_PARAM_CREATE_OR_FAIL(_prefix##_contact_p, \
+ _prefix##_event_params, ®_event_contact); \
+ EVI_PARAM_CREATE_OR_FAIL(_prefix##_registrar_p, \
+ _prefix##_event_params, ®_event_registrar); \
+ } while (0)
+
+int init_registrant_events(void)
+{
+ INIT_REG_EVENT(reg_registering);
+ INIT_REG_EVENT(reg_authenticating);
+ INIT_REG_EVENT(reg_registered);
+ INIT_REG_EVENT(reg_register_timeout);
+ INIT_REG_EVENT(reg_internal_error);
+ INIT_REG_EVENT(reg_wrong_credentials);
+ INIT_REG_EVENT(reg_registrar_error);
+ INIT_REG_EVENT(reg_unregistering);
+ INIT_REG_EVENT(reg_authenticating_unregister);
+
+ return 0;
+}
+
+#define REG_EVI_SET_STR_OR_RETURN(_param, _str, _errmsg) \
+ do { \
+ if (evi_param_set_str((_param), (_str)) < 0) { \
+ LM_ERR(_errmsg); \
+ return; \
+ } \
+ } while (0)
+
+#define RAISE_REG_EVENT(_prefix, _rec) \
+ do { \
+ if (!evi_probe_event(_prefix##_ev_id)) \
+ return; \
+ REG_EVI_SET_STR_OR_RETURN(_prefix##_aor_p, \
+ &(_rec)->td.rem_uri, \
+ "cannot set AOR parameter\n"); \
+ REG_EVI_SET_STR_OR_RETURN(_prefix##_contact_p, \
+ &(_rec)->contact_uri, \
+ "cannot set Contact parameter\n"); \
+ REG_EVI_SET_STR_OR_RETURN(_prefix##_registrar_p, \
+ &(_rec)->td.rem_target, \
+ "cannot set Registrar parameter\n"); \
+ if (evi_raise_event(_prefix##_ev_id, _prefix##_event_params) < 0) \
+ LM_ERR("cannot raise %.*s event\n", \
+ _prefix##_ev_name.len, \
+ _prefix##_ev_name.s); \
+ } while (0)
+
+void raise_registering_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_registering, rec);
+}
+
+void raise_authenticating_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_authenticating, rec);
+}
+
+void raise_registered_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_registered, rec);
+}
+
+void raise_register_timeout_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_register_timeout, rec);
+}
+
+void raise_internal_error_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_internal_error, rec);
+}
+
+void raise_wrong_credentials_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_wrong_credentials, rec);
+}
+
+void raise_registrar_error_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_registrar_error, rec);
+}
+
+void raise_unregistering_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_unregistering, rec);
+}
+
+void raise_authenticating_unregister_event(reg_record_t *rec)
+{
+ RAISE_REG_EVENT(reg_authenticating_unregister, rec);
+}
diff --git a/modules/uac_registrant/reg_events.h b/modules/uac_registrant/reg_events.h
new file mode 100644
index 00000000000..6cd31267963
--- /dev/null
+++ b/modules/uac_registrant/reg_events.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 OpenSIPS Project
+ *
+ * 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 _UAC_REGISTRANT_EVENTS_H_
+#define _UAC_REGISTRANT_EVENTS_H_
+
+#include "reg_records.h"
+
+int init_registrant_events(void);
+void raise_registering_event(reg_record_t *rec);
+void raise_authenticating_event(reg_record_t *rec);
+void raise_registered_event(reg_record_t *rec);
+void raise_register_timeout_event(reg_record_t *rec);
+void raise_internal_error_event(reg_record_t *rec);
+void raise_wrong_credentials_event(reg_record_t *rec);
+void raise_registrar_error_event(reg_record_t *rec);
+void raise_unregistering_event(reg_record_t *rec);
+void raise_authenticating_unregister_event(reg_record_t *rec);
+
+#endif
diff --git a/modules/uac_registrant/reg_records.c b/modules/uac_registrant/reg_records.c
index 36b1ab38e79..4a92a059d0e 100644
--- a/modules/uac_registrant/reg_records.c
+++ b/modules/uac_registrant/reg_records.c
@@ -27,6 +27,7 @@
#include
#include
+#include "reg_events.h"
#include "reg_records.h"
extern unsigned int default_expires;
@@ -131,10 +132,11 @@ int match_reload_record(void *e_data, void *data, void *n_data)
!str_strcmp(&coords->registrar, &rec->td.rem_target)) {
if (!(new_rec->flags®_ENABLED) && rec->flags®_ENABLED &&
rec->state == REGISTERED_STATE) {
- if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1)
- rec->state = UNREGISTERING_STATE;
- else
- rec->state = INTERNAL_ERROR_STATE;
+ if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1) {
+ reg_change_state(rec,UNREGISTERING_STATE);
+ } else {
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
+ }
} else if (new_rec->flags®_ENABLED && rec->flags®_ENABLED &&
rec->state == REGISTERED_STATE) {
memcpy(new_rec->td.id.call_id.s, rec->td.id.call_id.s,
@@ -144,6 +146,8 @@ int match_reload_record(void *e_data, void *data, void *n_data)
new_rec->td.loc_seq.value = rec->td.loc_seq.value;
new_rec->last_register_sent = rec->last_register_sent;
new_rec->registration_timeout = rec->registration_timeout;
+ /* we are not migrating state here, just inheriting,
+ * do not call reg_change_state */
new_rec->state = rec->state;
}
@@ -350,3 +354,42 @@ void destroy_reg_htable(void) {
}
}
+void reg_change_state(reg_record_t *rec, int new_state)
+{
+ rec->state = new_state;
+ switch (new_state) {
+ case NOT_REGISTERED_STATE:
+ /* NO-OP, just initializing */
+ break;
+ case REGISTERING_STATE:
+ raise_registering_event(rec);
+ break;
+ case AUTHENTICATING_STATE:
+ raise_authenticating_event(rec);
+ break;
+ case REGISTERED_STATE:
+ raise_registered_event(rec);
+ break;
+ case REGISTER_TIMEOUT_STATE:
+ raise_register_timeout_event(rec);
+ break;
+ case INTERNAL_ERROR_STATE:
+ raise_internal_error_event(rec);
+ break;
+ case WRONG_CREDENTIALS_STATE:
+ raise_wrong_credentials_event(rec);
+ break;
+ case REGISTRAR_ERROR_STATE:
+ raise_registrar_error_event(rec);
+ break;
+ case UNREGISTERING_STATE:
+ raise_unregistering_event(rec);
+ break;
+ case AUTHENTICATING_UNREGISTER_STATE:
+ raise_authenticating_unregister_event(rec);
+ break;
+ default:
+ LM_ERR("Unhandled new state %d \n",new_state);
+ break;
+ }
+}
diff --git a/modules/uac_registrant/reg_records.h b/modules/uac_registrant/reg_records.h
index a7b35cf3af4..340c541d826 100644
--- a/modules/uac_registrant/reg_records.h
+++ b/modules/uac_registrant/reg_records.h
@@ -122,4 +122,6 @@ int add_record(uac_reg_map_t *uac, str *now, unsigned int mode,
record_coords_t *coords);
void reg_print_record(reg_record_t *rec);
+void reg_change_state(reg_record_t *rec, int new_state);
+
#endif
diff --git a/modules/uac_registrant/registrant.c b/modules/uac_registrant/registrant.c
index 9bce900d172..d9374728d5e 100644
--- a/modules/uac_registrant/registrant.c
+++ b/modules/uac_registrant/registrant.c
@@ -41,6 +41,7 @@
#include "reg_records.h"
#include "reg_db_handler.h"
#include "clustering.h"
+#include "reg_events.h"
#define UAC_REGISTRAR_URI_PARAM 1
@@ -102,6 +103,11 @@ static mi_response_t *mi_reg_disable(const mi_params_t *params,
static mi_response_t *mi_reg_force_register(const mi_params_t *params,
struct mi_handler *async_hdl);
+static mi_response_t *mi_reg_upsert(const mi_params_t *params,
+ struct mi_handler *async_hdl);
+static mi_response_t *mi_reg_delete(const mi_params_t *params,
+ struct mi_handler *async_hdl);
+
int send_register(unsigned int hash_index, reg_record_t *rec, str *auth_hdr);
int send_unregister(unsigned int hash_index, reg_record_t *rec, str *auth_hdr,
unsigned int all_contacts);
@@ -114,6 +120,7 @@ uac_auth_api_t uac_auth_api;
unsigned int default_expires = 3600;
unsigned int timer_interval = 100;
unsigned int failure_retry_interval = 0;
+unsigned int reregister_exp_percentage=100;
reg_table_t reg_htable = NULL;
unsigned int reg_hsize = 1;
@@ -159,6 +166,7 @@ static const param_export_t params[]= {
{"forced_socket_column", STR_PARAM, &forced_socket_column.s},
{"cluster_shtag_column", STR_PARAM, &cluster_shtag_column.s},
{"state_column", STR_PARAM, &state_column.s},
+ {"reregister_expiry_percentage", INT_PARAM, &reregister_exp_percentage},
{0,0,0}
};
@@ -187,6 +195,14 @@ static const mi_export_t mi_cmds[] = {
{mi_reg_force_register, {"aor", "contact", "registrar", 0}},
{EMPTY_MI_RECIPE}}
},
+ {"reg_upsert", 0, 0, 0, {
+ {mi_reg_upsert, {"aor", "contact", "registrar","proxy","third_party_registrant","username","password","binding_params","expiry","forced_socket","cluster_shtag","state", 0}},
+ {EMPTY_MI_RECIPE}}
+ },
+ { "reg_delete", 0, 0, 0, {
+ {mi_reg_delete, {"aor", "contact", "registrar", 0}},
+ {EMPTY_MI_RECIPE}}
+ },
{EMPTY_MI_EXPORT}
};
@@ -306,6 +322,16 @@ static int mod_init(void)
return -1;
}
+ if (init_registrant_events() < 0) {
+ LM_ERR("Failed to create the registrant events \n");
+ return -1;
+ }
+
+ if (reregister_exp_percentage <= 0 || reregister_exp_percentage > 100) {
+ LM_ERR("Earlier reregistration expiry percentage needs to be in the (0...100] range \n");
+ return -1;
+ }
+
return 0;
}
@@ -426,7 +452,7 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
case AUTHENTICATING_UNREGISTER_STATE:
if (!(rec->flags®_ENABLED)) {
/* succesfully unREGISTERED */
- rec->state = NOT_REGISTERED_STATE;
+ reg_change_state(rec,NOT_REGISTERED_STATE);
rec->registration_timeout = 0;
} else {
/* registrant got enabled while waiting for a reply to
@@ -438,10 +464,10 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
new_call_id_ftag_4_record(rec, &str_now);
if(send_register(cb_param->hash_index, rec, NULL)==1) {
rec->last_register_sent = now;
- rec->state = REGISTERING_STATE;
+ reg_change_state(rec,REGISTERING_STATE);
} else {
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
- rec->state = INTERNAL_ERROR_STATE;
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
}
@@ -479,9 +505,9 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
if (bindings_counter>1) {
LM_DBG("got [%d] bindings\n", bindings_counter);
if(send_unregister(cb_param->hash_index, rec, NULL, 1)==1) {
- rec->state = UNREGISTERING_STATE;
+ reg_change_state(rec,UNREGISTERING_STATE);
} else {
- rec->state = INTERNAL_ERROR_STATE;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
break;
}
@@ -536,13 +562,13 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
/* registrant got disabled while waiting for a reply to
* a previous register */
if(send_unregister(cb_param->hash_index, rec, NULL, 0)==1) {
- rec->state = UNREGISTERING_STATE;
+ reg_change_state(rec,UNREGISTERING_STATE);
} else {
- rec->state = INTERNAL_ERROR_STATE;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
} else {
/* succesfully unREGISTERED */
- rec->state = NOT_REGISTERED_STATE;
+ reg_change_state(rec,NOT_REGISTERED_STATE);
rec->registration_timeout = 0;
}
} else {
@@ -557,14 +583,15 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
new_call_id_ftag_4_record(rec, &str_now);
if(send_register(cb_param->hash_index, rec, NULL)==1) {
rec->last_register_sent = now;
- rec->state = REGISTERING_STATE;
+ reg_change_state(rec,REGISTERING_STATE);
} else {
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
- rec->state = INTERNAL_ERROR_STATE;
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
} else {
/* succesfully REGISTERED */
- rec->state = REGISTERED_STATE;
+ reg_change_state(rec,REGISTERED_STATE);
+
if (exp) rec->expires = exp;
if (rec->expires <= timer_interval) {
LM_ERR("Please decrease timer_interval=[%u]"
@@ -572,7 +599,7 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
timer_interval, rec->expires,
rec->td.rem_uri.len, rec->td.rem_uri.s);
}
- rec->registration_timeout = now + rec->expires - timer_interval;
+ rec->registration_timeout = now + (rec->expires*reregister_exp_percentage/100) - timer_interval;
}
}
@@ -589,8 +616,8 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
if (rec->auth_user.s==NULL || rec->auth_user.len==0 ||
rec->auth_password.s==NULL || rec->auth_password.len==0) {
LM_ERR("Credentials not provisioned\n");
- rec->state = WRONG_CREDENTIALS_STATE;
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
+ reg_change_state(rec,WRONG_CREDENTIALS_STATE);
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
/* action successfully completed on current list element */
return 1; /* exit list traversal */
}
@@ -624,8 +651,8 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
/* We already sent an authenticated REGISTER and we are still challanged! */
LM_WARN("Wrong credentials for [%.*s]\n",
rec->td.rem_uri.len, rec->td.rem_uri.s);
- rec->state = WRONG_CREDENTIALS_STATE;
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
+ reg_change_state(rec,WRONG_CREDENTIALS_STATE);
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
/* action successfully completed on current list element */
return 1; /* exit list traversal */
default:
@@ -664,16 +691,16 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
switch(rec->state) {
case REGISTERING_STATE:
if(send_register(cb_param->hash_index, rec, new_hdr)==1) {
- rec->state = AUTHENTICATING_STATE;
+ reg_change_state(rec,AUTHENTICATING_STATE);
} else {
- rec->state = INTERNAL_ERROR_STATE;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
break;
case UNREGISTERING_STATE:
if(send_unregister(cb_param->hash_index, rec, new_hdr, 0)==1) {
- rec->state = AUTHENTICATING_UNREGISTER_STATE;
+ reg_change_state(rec,AUTHENTICATING_UNREGISTER_STATE);
} else {
- rec->state = INTERNAL_ERROR_STATE;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
break;
default:
@@ -693,29 +720,30 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
}
if (0 == parse_min_expires(msg)) {
rec->expires = (unsigned int)(long)msg->min_expires->parsed;
- if(send_register(cb_param->hash_index, rec, NULL)==1)
- rec->state = REGISTERING_STATE;
- else
- rec->state = INTERNAL_ERROR_STATE;
+ if(send_register(cb_param->hash_index, rec, NULL)==1) {
+ reg_change_state(rec,REGISTERING_STATE);
+ } else {
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
+ }
} else {
- rec->state = REGISTRAR_ERROR_STATE;
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
+ reg_change_state(rec,REGISTRAR_ERROR_STATE);
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
}
break;
- case 408: /* Interval Too Brief */
- rec->state = REGISTER_TIMEOUT_STATE;
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
+ case 408: /* No reply or Timeout */
+ reg_change_state(rec,REGISTER_TIMEOUT_STATE);
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
break;
default:
if(statuscode<400 && statuscode>=300) {
LM_ERR("Redirection not implemented yet\n");
- rec->state = INTERNAL_ERROR_STATE;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
} else {
/* we got an error from the server */
- rec->state = REGISTRAR_ERROR_STATE;
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
+ reg_change_state(rec,REGISTRAR_ERROR_STATE);
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
}
}
@@ -723,13 +751,11 @@ int run_reg_tm_cback(void *e_data, void *data, void *r_data)
/* action successfully completed on current list element */
return 1; /* exit list traversal */
done:
- rec->state = INTERNAL_ERROR_STATE;
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
return -1; /* exit list traversal */
}
-
-
void reg_tm_cback(struct cell *t, int type, struct tmcb_params *ps)
{
reg_tm_cb_t *cb_param;
@@ -761,8 +787,8 @@ void reg_tm_cback(struct cell *t, int type, struct tmcb_params *ps)
/* Initialize slinkedl run traversal data */
tm_cback_data.t = t;
- tm_cback_data.ps = ps;
- tm_cback_data.cb_param = cb_param;
+ tm_cback_data.ps = ps;
+ tm_cback_data.cb_param = cb_param;
tm_cback_data.now = now;
lock_get(®_htable[cb_param->hash_index].lock);
@@ -963,32 +989,32 @@ int run_timer_check(void *e_data, void *data, void *r_data)
new_call_id_ftag_4_record(rec, s_now);
if(send_register(i, rec, NULL)==1) {
rec->last_register_sent = now;
- rec->state = REGISTERING_STATE;
+ reg_change_state(rec,REGISTERING_STATE);
} else {
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
- rec->state = INTERNAL_ERROR_STATE;
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
} else {
if(send_unregister(i, rec, NULL, 0)==1) {
- rec->state = UNREGISTERING_STATE;
+ reg_change_state(rec,UNREGISTERING_STATE);
} else {
- rec->state = INTERNAL_ERROR_STATE;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
}
break;
case REGISTERED_STATE:
/* check if we need to re-register */
- if (now < rec->registration_timeout) {
+ if (now + timer_interval < rec->registration_timeout) {
break;
}
case NOT_REGISTERED_STATE:
if (rec->flags®_ENABLED) {
if(send_register(i, rec, NULL)==1) {
rec->last_register_sent = now;
- rec->state = REGISTERING_STATE;
+ reg_change_state(rec,REGISTERING_STATE);
} else {
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
- rec->state = INTERNAL_ERROR_STATE;
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
}
break;
@@ -1051,7 +1077,7 @@ static int cluster_shtag_check(void *e_data, void *data, void *r_data)
0==memcmp(rec->cluster_shtag.s, shtag_data->tag->s, shtag_data->tag->len)) {
/* this record matches the shtag + cluster_id, so we need to de-active it */
LM_DBG("Moving record to NOT_REGISTERED_STATE\n");
- rec->state = NOT_REGISTERED_STATE;
+ reg_change_state(rec,NOT_REGISTERED_STATE);
}
@@ -1249,6 +1275,18 @@ int run_mi_reg_list_record(void *e_data, void *data, void *r_data)
return 0; /* continue search */
}
+int run_mi_reg_matching(void *e_data, void*data, void *unused)
+{
+ reg_record_t *rec = (reg_record_t*)e_data;
+ record_coords_t *coords = (record_coords_t *)data;
+
+ if (!str_strcmp(&coords->contact, &rec->contact_uri) &&
+ !str_strcmp(&coords->registrar, &rec->td.rem_target))
+ return 1;
+ else
+ return 0; /* continue search */
+}
+
static mi_response_t *mi_get_coords(const mi_params_t *params, record_coords_t *coords)
{
if (get_mi_string_param(params, "aor", &coords->aor.s, &coords->aor.len) < 0)
@@ -1347,7 +1385,7 @@ static mi_response_t *mi_reg_reload(const mi_params_t *params,
reg_htable[i].s_list = NULL;
}
reg_htable[i].s_list = slinkedl_init(®_alloc, ®_free);
- if (reg_htable[i].p_list == NULL) {
+ if (reg_htable[i].s_list == NULL) {
LM_ERR("oom while allocating list\n");
err = 1;
}
@@ -1424,10 +1462,10 @@ int run_mi_reg_enable(void *e_data, void *data, void *r_data)
if(send_register((unsigned long)coords->extra, rec, NULL)==1) {
rec->last_register_sent = now;
- rec->state = REGISTERING_STATE;
+ reg_change_state(rec,REGISTERING_STATE);
} else {
- rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:rec->expires) - timer_interval;
- rec->state = INTERNAL_ERROR_STATE;
+ rec->registration_timeout = now + (failure_retry_interval?failure_retry_interval:(rec->expires*reregister_exp_percentage/100)) - timer_interval;
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
}
}
@@ -1450,10 +1488,11 @@ int run_mi_reg_disable(void *e_data, void *data, void *r_data)
!str_strcmp(&coords->registrar, &rec->td.rem_target)) {
if (rec->flags®_ENABLED) {
if (rec->state == REGISTERED_STATE) {
- if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1)
- rec->state = UNREGISTERING_STATE;
- else
- rec->state = INTERNAL_ERROR_STATE;
+ if(send_unregister((unsigned long)coords->extra, rec, NULL, 0)==1) {
+ reg_change_state(rec,UNREGISTERING_STATE);
+ } else {
+ reg_change_state(rec,INTERNAL_ERROR_STATE);
+ }
}
rec->flags &= ~REG_ENABLED;
@@ -1564,3 +1603,294 @@ static mi_response_t *mi_reg_force_register(const mi_params_t *params,
return init_mi_result_ok();
}
+static mi_response_t *mi_reg_upsert(const mi_params_t *params,
+ struct mi_handler *async_hdl)
+{
+ uac_reg_map_t uac_param;
+ str aor;
+ str contact;
+ str registrar;
+ str proxy;
+ str third_party_registrant;
+ str username;
+ str password;
+ str binding_params;
+ int expiry;
+ str forced_socket;
+ str sharing_tag;
+ int state;
+ struct sip_uri uri;
+ param_hooks_t hooks;
+ param_t *c_params = NULL;
+ param_t *_param = NULL;
+ str _param_str = {NULL, 0};
+ int reg_id_found = 0;
+ int sip_instance_found = 0;
+ char *p;
+ str s;
+ str now = {NULL, 0};
+ int ret;
+ int len;
+ int mode = REG_DB_LOAD_RECORD;
+ record_coords_t coords;
+
+ if (get_mi_string_param(params, "aor", &aor.s, &aor.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "contact",
+ &contact.s, &contact.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "registrar",
+ ®istrar.s, ®istrar.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "proxy",
+ &proxy.s, &proxy.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "third_party_registrant",
+ &third_party_registrant.s, &third_party_registrant.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "username",
+ &username.s, &username.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "password",
+ &password.s, &password.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "binding_params",
+ &binding_params.s, &binding_params.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_int_param(params, "expiry",&expiry) < 0) {
+ return init_mi_param_error();
+ }
+ if (get_mi_string_param(params, "forced_socket",
+ &forced_socket.s, &forced_socket.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_string_param(params, "cluster_shtag",
+ &sharing_tag.s, &sharing_tag.len) < 0)
+ return init_mi_param_error();
+ if (get_mi_int_param(params, "state",&state) < 0)
+ return init_mi_param_error();
+
+ p = int2str((unsigned long)(time(0)), &len);
+ if (p && len>0) {
+ now.s = (char *)pkg_malloc(len);
+ if(now.s) {
+ memcpy(now.s, p, len); now.len = len;
+ } else {
+ return init_mi_error(503, MI_SSTR("Internal Error"));
+ }
+ }
+
+ memset(&uac_param, 0, sizeof(uac_reg_map_t));
+
+ uac_param.registrar_uri = registrar;
+ if (parse_uri(uac_param.registrar_uri.s,uac_param.registrar_uri.len, &uri)<0) {
+ LM_ERR("cannot parse registrar uri [%.*s]\n",
+ uac_param.registrar_uri.len,
+ uac_param.registrar_uri.s);
+
+ return init_mi_param_error();
+ }
+
+ if (uri.user.s && uri.user.len) {
+ LM_ERR("registrant uri must not have user [%.*s]\n",
+ uri.user.len, uri.user.s);
+
+ return init_mi_param_error();
+ }
+
+ uac_param.proxy_uri = proxy;
+ if (uac_param.proxy_uri.len) {
+ if (parse_uri(uac_param.proxy_uri.s,
+ uac_param.proxy_uri.len, &uri)<0) {
+ LM_ERR("cannot parse proxy uri [%.*s]\n",
+ uac_param.proxy_uri.len,
+ uac_param.proxy_uri.s);
+ return init_mi_param_error();
+ }
+ if (uri.user.s && uri.user.len) {
+ LM_ERR("proxy uri must not have user [%.*s]\n",
+ uri.user.len, uri.user.s);
+ return init_mi_param_error();
+ }
+ } else {
+ uac_param.proxy_uri.s = NULL;
+ }
+
+ uac_param.to_uri = aor;
+ if (!uac_param.to_uri.s) {
+ LM_ERR("empty AOR provided\n");
+ return init_mi_param_error();
+ }
+ if (parse_uri(uac_param.to_uri.s,uac_param.to_uri.len,&uri)<0) {
+ LM_ERR("cannot parse aor uri [%.*s]\n",
+ uac_param.to_uri.len, uac_param.to_uri.s);
+ return init_mi_param_error();
+ }
+ uac_param.hash_code = core_hash(&uac_param.to_uri, NULL, reg_hsize);
+
+ uac_param.from_uri = third_party_registrant;
+ if (uac_param.from_uri.len) {
+ if (parse_uri(uac_param.from_uri.s,
+ uac_param.from_uri.len, &uri)<0) {
+ LM_ERR("cannot parse third party registrant"
+ " uri [%.*s]\n",
+ uac_param.from_uri.len,
+ uac_param.from_uri.s);
+ return init_mi_param_error();
+ }
+ } else {
+ uac_param.from_uri.s = NULL;
+ }
+
+ uac_param.contact_uri = contact;
+ if (!uac_param.contact_uri.s) {
+ LM_ERR("empty CONTACT provided\n");
+ return init_mi_param_error();
+ }
+ if (parse_uri(uac_param.contact_uri.s,
+ uac_param.contact_uri.len, &uri)<0) {
+ LM_ERR("cannot parse contact uri [%.*s]\n",
+ uac_param.contact_uri.len,
+ uac_param.contact_uri.s);
+ return init_mi_param_error();
+ }
+
+ uac_param.auth_user = username;
+ uac_param.auth_password = password;
+
+ uac_param.contact_params = binding_params;
+ if (uac_param.contact_params.len != 0) {
+ memset(&hooks, 0, sizeof(param_hooks_t));
+ _param_str = uac_param.contact_params;
+ if (parse_params(&_param_str, CLASS_CONTACT, &hooks, &c_params) <0) {
+ LM_ERR("Bogus params [%.*s]\n", _param_str.len, _param_str.s);
+ free_params(c_params);
+ return init_mi_param_error();
+ }
+ _param = c_params;
+ while (_param) {
+ LM_DBG("[%.*s][%.*s]\n", _param->name.len, _param->name.s, _param->body.len, _param->body.s);
+ if (reg_id_found == 0 && strncmp(_param->name.s, "reg-id", 6) == 0)
+ reg_id_found = 1;
+ if (sip_instance_found == 0 && strncmp(_param->name.s, "+sip.instance", 13) == 0)
+ sip_instance_found = 1;
+ _param = _param->next;
+ }
+ free_params(c_params);
+ if (reg_id_found && sip_instance_found) {
+ LM_DBG("special record\n");
+ uac_param.flags |= FORCE_SINGLE_REGISTRATION;
+ }
+ }
+
+ uac_param.expires = expiry;
+ if (uac_param.expires <= timer_interval) {
+ LM_ERR("Please decrease timer_interval=[%u]"
+ " - requested expires=[%u] to small for AOR=[%.*s]\n",
+ timer_interval, uac_param.expires,
+ uac_param.to_uri.len, uac_param.to_uri.s);
+ return init_mi_param_error();
+ }
+
+ /* Get the socket */
+ if (forced_socket.len && forced_socket.s) {
+ uac_param.send_sock = parse_sock_info(&forced_socket);
+ if (uac_param.send_sock==NULL) {
+ LM_ERR("invalid forced socket [%.*s]\n",
+ forced_socket.len, forced_socket.s);
+ return init_mi_param_error();
+ }
+ }
+
+ if (sharing_tag.s && sharing_tag.len) {
+ uac_param.cluster_shtag = sharing_tag;
+ p = memchr(uac_param.cluster_shtag.s, '/',
+ uac_param.cluster_shtag.len);
+ if (!p) {
+ LM_ERR("Bad naming for sharing tag <%.*s>, "
+ " expected\n",
+ uac_param.cluster_shtag.len,
+ uac_param.cluster_shtag.s);
+ return init_mi_param_error();
+ }
+ s.s = p + 1;
+ s.len = uac_param.cluster_shtag.s + uac_param.cluster_shtag.len - s.s;
+ trim_spaces_lr( s );
+ uac_param.cluster_shtag.len = p - uac_param.cluster_shtag.s;
+ trim_spaces_lr( uac_param.cluster_shtag );
+ /* get the cluster ID */
+ if (str2int( &s, (unsigned int*)&uac_param.cluster_id)<0) {
+ LM_ERR("Invalid cluster id <%.*s> for sharing tag "
+ " <%.*s> \n",s.len, s.s,
+ uac_param.cluster_shtag.len,
+ uac_param.cluster_shtag.s);
+ return init_mi_param_error();
+ }
+ }
+
+ if (state == REG_DB_STATE_ENABLED)
+ uac_param.flags |= REG_ENABLED;
+
+ LM_DBG("registrar=[%.*s] AOR=[%.*s] auth_user=[%.*s] "
+ "password=[%.*s] expire=[%d] proxy=[%.*s] "
+ "contact=[%.*s] third_party=[%.*s] "
+ "cluster_shtag=[%.*s/%d] state=[%d]\n",
+ uac_param.registrar_uri.len, uac_param.registrar_uri.s,
+ uac_param.to_uri.len, uac_param.to_uri.s,
+ uac_param.auth_user.len, uac_param.auth_user.s,
+ uac_param.auth_password.len, uac_param.auth_password.s,
+ uac_param.expires,
+ uac_param.proxy_uri.len, uac_param.proxy_uri.s,
+ uac_param.contact_uri.len, uac_param.contact_uri.s,
+ uac_param.from_uri.len, uac_param.from_uri.s,
+ uac_param.cluster_shtag.len, uac_param.cluster_shtag.s,
+ uac_param.cluster_id,
+ state);
+
+ coords.aor = aor;
+ coords.contact = contact;
+ coords.registrar = registrar;
+
+ lock_get(®_htable[uac_param.hash_code].lock);
+ ret = add_record(&uac_param, &now, mode, &coords);
+ lock_release(®_htable[uac_param.hash_code].lock);
+ if(ret<0) {
+ if (now.s) pkg_free(now.s);
+ return init_mi_error(503, MI_SSTR("Failed Upsert"));
+ }
+
+ if (now.s) pkg_free(now.s);
+ return init_mi_result_ok();
+}
+
+static mi_response_t *mi_reg_delete(const mi_params_t *params,
+ struct mi_handler *async_hdl)
+{
+ mi_response_t *resp;
+ record_coords_t coords;
+ int rc;
+ unsigned int hash_code;
+
+ if ((resp = mi_get_coords(params, &coords)))
+ return resp;
+
+ hash_code = core_hash(&coords.aor, NULL, reg_hsize);
+ coords.extra = (void*)(unsigned long)hash_code;
+
+ lock_get(®_htable[hash_code].lock);
+ rc = slinkedl_traverse(reg_htable[hash_code].p_list,
+ &run_mi_reg_disable, &coords, NULL);
+
+ if (rc > 0) {
+ /* we found it, let's also delete it now */
+ rc = slinkedl_delete(reg_htable[hash_code].p_list,
+ &run_mi_reg_matching, &coords);
+ }
+ lock_release(®_htable[hash_code].lock);
+
+ if (rc < 0)
+ return NULL;
+ else if (rc == 0)
+ return init_mi_error(404, MI_SSTR("No such registrant"));
+
+ return init_mi_result_ok();
+}