From f668db9ba0973a5307d9476e000328c3958fa2ff Mon Sep 17 00:00:00 2001
From: Kevin Harwell <kharwell@digium.com>
Date: Tue, 15 Jan 2019 17:20:30 -0600
Subject: [PATCH] pjsip/config_global: regcontext context not created

The context specified by 'regcontext' was not being created, so when Asterisk
attempted to later dynamically add an extension it would fail. This patch now
creates the context if a 'regcontext' is specified.

ASTERISK-28238

Change-Id: I0f36cf4ab0a93ff4b1cc5548d617ecfd45e09265
---
 res/res_pjsip/config_global.c             | 51 +++++++++++++++++++++++
 res/res_pjsip/include/res_pjsip_private.h | 10 +++++
 res/res_pjsip/pjsip_configuration.c       | 35 +++++++++++++++-
 3 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c
index 61a21c253d..38383c5c2b 100644
--- a/res/res_pjsip/config_global.c
+++ b/res/res_pjsip/config_global.c
@@ -23,6 +23,7 @@
 
 #include "asterisk/res_pjsip.h"
 #include "include/res_pjsip_private.h"
+#include "asterisk/pbx.h"
 #include "asterisk/sorcery.h"
 #include "asterisk/taskprocessor.h"
 #include "asterisk/ast_version.h"
@@ -131,6 +132,46 @@ static void *global_alloc(const char *name)
 	return cfg;
 }
 
+/*
+ * There is ever only one global section, so we can use a single global
+ * value here to track the regcontext through reloads.
+ */
+static char *previous_regcontext = NULL;
+
+static int check_regcontext(const struct global_config *cfg)
+{
+	char *current = NULL;
+
+	if (previous_regcontext && !strcmp(previous_regcontext, cfg->regcontext)) {
+		/* Nothing changed so nothing to do */
+		return 0;
+	}
+
+	if (!ast_strlen_zero(cfg->regcontext)) {
+		current = ast_strdup(cfg->regcontext);
+		if (!current) {
+			return -1;
+		}
+
+		if (ast_sip_persistent_endpoint_add_to_regcontext(cfg->regcontext)) {
+			ast_free(current);
+			return -1;
+		}
+	}
+
+	if (!ast_strlen_zero(previous_regcontext)) {
+		ast_context_destroy_by_name(previous_regcontext, "PJSIP");
+		ast_free(previous_regcontext);
+		previous_regcontext = NULL;
+	}
+
+	if (current) {
+		previous_regcontext = current;
+	}
+
+	return 0;
+}
+
 static int global_apply(const struct ast_sorcery *sorcery, void *obj)
 {
 	struct global_config *cfg = obj;
@@ -154,6 +195,10 @@ static int global_apply(const struct ast_sorcery *sorcery, void *obj)
 	ast_sip_add_global_request_header("User-Agent", cfg->useragent, 1);
 	ast_sip_add_global_response_header("Server", cfg->useragent, 1);
 
+	if (check_regcontext(cfg)) {
+		return -1;
+	}
+
 	ao2_t_global_obj_replace_unref(global_cfg, cfg, "Applying global settings");
 	return 0;
 }
@@ -515,11 +560,17 @@ int ast_sip_destroy_sorcery_global(void)
 
 	ast_sorcery_instance_observer_remove(sorcery, &observer_callbacks_global);
 
+	if (previous_regcontext) {
+		ast_context_destroy_by_name(previous_regcontext, "PJSIP");
+		ast_free(previous_regcontext);
+	}
+
 	ao2_t_global_obj_release(global_cfg, "Module is unloading");
 
 	return 0;
 }
 
+
 int ast_sip_initialize_sorcery_global(void)
 {
 	struct ast_sorcery *sorcery = ast_sip_get_sorcery();
diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h
index 2fd6d314c7..7af5b27bb8 100644
--- a/res/res_pjsip/include/res_pjsip_private.h
+++ b/res/res_pjsip/include/res_pjsip_private.h
@@ -398,4 +398,14 @@ int ast_sip_initialize_transport_management(void);
  */
 void ast_sip_destroy_transport_management(void);
 
+/*!
+ * \internal
+ * \brief Add online persistent endpoints to the given regcontext
+ *
+ * \param regcontext The context to add endpoints to
+ *
+ * \retval -1 on error, 0 on success
+ */
+int ast_sip_persistent_endpoint_add_to_regcontext(const char *regcontext);
+
 #endif /* RES_PJSIP_PRIVATE_H_ */
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index d88024a80b..e6ac8ecfad 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1128,6 +1128,39 @@ static void persistent_endpoint_destroy(void *obj)
 	ast_endpoint_shutdown(persistent->endpoint);
 }
 
+static int add_to_regcontext(void *obj, void *arg, int flags)
+{
+	struct sip_persistent_endpoint *persistent = obj;
+	const char *regcontext = arg;
+
+	if (ast_endpoint_get_state(persistent->endpoint) == AST_ENDPOINT_ONLINE) {
+		if (!ast_exists_extension(NULL, regcontext, ast_endpoint_get_resource(
+				persistent->endpoint), 1, NULL)) {
+			ast_add_extension(regcontext, 1, ast_endpoint_get_resource(persistent->endpoint), 1, NULL, NULL,
+				"Noop", ast_strdup(ast_endpoint_get_resource(persistent->endpoint)), ast_free_ptr, "PJSIP");
+		}
+	}
+
+	return 0;
+}
+
+int ast_sip_persistent_endpoint_add_to_regcontext(const char *regcontext)
+{
+	if (ast_strlen_zero(regcontext)) {
+		return 0;
+	}
+
+	/* Make sure the regcontext exists */
+	if (!ast_context_find_or_create(NULL, NULL, regcontext, "PJSIP")) {
+		ast_log(LOG_ERROR, "Failed to create regcontext '%s'\n", regcontext);
+		return -1;
+	}
+
+	/* Add any online endpoints */
+	ao2_callback(persistent_endpoints, OBJ_NODATA, add_to_regcontext, (void *)regcontext);
+	return 0;
+}
+
 int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast_endpoint_state state)
 {
 	struct sip_persistent_endpoint *persistent;
@@ -1154,7 +1187,7 @@ int ast_sip_persistent_endpoint_update_state(const char *endpoint_name, enum ast
 		if (!ast_strlen_zero(regcontext)) {
 			if (!ast_exists_extension(NULL, regcontext, ast_endpoint_get_resource(persistent->endpoint), 1, NULL)) {
 				ast_add_extension(regcontext, 1, ast_endpoint_get_resource(persistent->endpoint), 1, NULL, NULL,
-					"Noop", ast_strdup(ast_endpoint_get_resource(persistent->endpoint)), ast_free_ptr, "SIP");
+					"Noop", ast_strdup(ast_endpoint_get_resource(persistent->endpoint)), ast_free_ptr, "PJSIP");
 			}
 		}
 
-- 
GitLab