[Asterisk-code-review] res_pjsip_geolocation: Initial commit (asterisk[development/16/geolocation])

George Joseph asteriskteam at digium.com
Mon Mar 7 10:30:15 CST 2022


George Joseph has uploaded this change for review. ( https://gerrit.asterisk.org/c/asterisk/+/18191 )


Change subject: res_pjsip_geolocation:  Initial commit
......................................................................

res_pjsip_geolocation:  Initial commit

* Added two parameters to endpoint: geoloc_incoming_call_profile
  and geoloc_outgoing_call_profile.  Alembic included.  res_pjsip
  now has a use/optional dependency on res_geolocation for
  validation.

* Added the res_pjsip_geolocation module with support for incoming
  calls.

* Fixed ast_datastore_free so it's tolerant of being passed a
  NULL pointer.  This makes it RAII_VAR compatible.

* Added PJSTR_PRINTF_SPEC and PJSTR_PRINTF_VAR(_v) convenience
  macros to res_pjsip.h.
  ast_log(LOG_ERROR, "some pjstr value: " PJSTR_PRINTF_SPEC "\n",
      PJSTR_PRINTF_VAR(&some_pjstr));

* Did more renames of functions and files.

Change-Id: I6ccb5108a5eec060efb8116502fbff3cc63d7554
---
M configs/samples/pjsip.conf.sample
A contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py
M include/asterisk/res_geolocation.h
M include/asterisk/res_pjsip.h
M main/datastore.c
M res/res_geolocation.c
D res/res_geolocation/geoloc_channel.c
M res/res_geolocation/geoloc_config.c
A res/res_geolocation/geoloc_datastore.c
M res/res_geolocation/geoloc_eprofile.c
M res/res_pjsip.c
M res/res_pjsip/pjsip_config.xml
M res/res_pjsip/pjsip_configuration.c
A res/res_pjsip_geolocation.c
14 files changed, 775 insertions(+), 119 deletions(-)



  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/91/18191/1

diff --git a/configs/samples/pjsip.conf.sample b/configs/samples/pjsip.conf.sample
index 8118086..97d1e73 100644
--- a/configs/samples/pjsip.conf.sample
+++ b/configs/samples/pjsip.conf.sample
@@ -898,6 +898,17 @@
                            ; responses.
                            ; (default: no)
 
+;geoloc_incoming_call_profile =
+                ; This geolocation profile will be applied to all calls received
+                ; by the channel driver from the remote endpoint before they're
+                ; forwarded to the dialplan.
+;geoloc_outgoing_call_profile =
+                ; This geolocation profile will be applied to all calls received
+                ; by the channel driver from the dialplan before they're forwarded
+                ; the remote endpoint.
+;
+
+
 ;==========================AUTH SECTION OPTIONS=========================
 ;[auth]
 ;  SYNOPSIS: Authentication type
diff --git a/contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py b/contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py
new file mode 100644
index 0000000..bf9de50
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/7197536bb68d_geoloc_endpoint_params.py
@@ -0,0 +1,22 @@
+"""Geoloc Endpoint Params
+
+Revision ID: 7197536bb68d
+Revises: 8f72185e437f
+Create Date: 2022-03-07 05:32:54.909429
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '7197536bb68d'
+down_revision = '8f72185e437f'
+
+from alembic import op
+import sqlalchemy as sa
+
+def upgrade():
+    op.add_column('ps_endpoints', sa.Column('geoloc_incoming_call_profile', sa.String(80)))
+    op.add_column('ps_endpoints', sa.Column('geoloc_outgoing_call_profile', sa.String(80)))
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'geoloc_outgoing_call_profile')
+    op.drop_column('ps_endpoints', 'geoloc_incoming_call_profile')
diff --git a/include/asterisk/res_geolocation.h b/include/asterisk/res_geolocation.h
index 7fae4bf..80f1caf 100644
--- a/include/asterisk/res_geolocation.h
+++ b/include/asterisk/res_geolocation.h
@@ -22,6 +22,7 @@
 #include "asterisk/sorcery.h"
 #include "asterisk/config.h"
 #include "asterisk/xml.h"
+#include "asterisk/optional_api.h"
 
 enum ast_geoloc_pidf_element {
 	AST_PIDF_ELEMENT_NONE = 0,
@@ -67,7 +68,7 @@
 	struct ast_variable *usage_rules_vars;
 };
 
-struct ast_geoloc_effective_profile {
+struct ast_geoloc_eprofile {
 	AST_DECLARE_STRING_FIELDS(
 		AST_STRING_FIELD(id);
 		AST_STRING_FIELD(location_reference);
@@ -85,6 +86,34 @@
 	struct ast_variable *usage_rules_vars;
 };
 
+/*!
+ * \brief Check if res_geolocation is available
+ *
+ * \return 1 if available, 0 otherwise.
+ */
+AST_OPTIONAL_API(int, ast_geoloc_is_loaded,	(void), { return 0; });
+
+/*!
+ * \brief Retrieve a geolocation location object by id.
+ *
+ * \param id Location object id.
+ *
+ * \return Location object or NULL if not found.
+ */
+AST_OPTIONAL_API(struct ast_geoloc_location *, ast_geoloc_get_location,
+		 (const char *id),
+		 { return NULL; });
+
+/*!
+ * \brief Retrieve a geolocation profile by id.
+ *
+ * \param id profile id.
+ *
+ * \return Profile or NULL if not found.
+ */
+AST_OPTIONAL_API(struct ast_geoloc_profile *, ast_geoloc_get_profile,
+		 (const char *id),
+		 { return NULL; });
 
 /*!
  * \brief Given an official civicAddress code, return its friendly name.
@@ -164,7 +193,13 @@
  * \return The datastore.
  */
 struct ast_datastore *ast_geoloc_datastore_create_from_eprofile(
-	struct ast_geoloc_effective_profile *eprofile);
+	struct ast_geoloc_eprofile *eprofile);
+
+struct ast_datastore *ast_geoloc_datastore_create(const char *id);
+int ast_geoloc_datastore_add_eprofile(struct ast_datastore *ds,
+	struct ast_geoloc_eprofile *eprofile);
+int ast_geoloc_datastore_size(struct ast_datastore *ds);
+struct ast_geoloc_eprofile *ast_geoloc_datastore_get_eprofile(struct ast_datastore *ds, int ix);
 
 /*!
  * \brief Allocate a new, empty effective profile.
@@ -173,7 +208,7 @@
  *
  * \return The effective profile ao2 object.
  */
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_alloc(const char *name);
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_alloc(const char *name);
 
 /*!
  * \brief Allocate a new effective profile from an existing profile.
@@ -182,7 +217,7 @@
  *
  * \return The effective profile ao2 object.
  */
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile);
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile);
 
 /*!
  * \brief Allocate a new effective profile from an XML PIDF-LO document
@@ -192,7 +227,7 @@
  *
  * \return The effective profile ao2 object.
  */
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_pidf(
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_pidf(
 	struct ast_xml_doc *pidf_xmldoc, const char *reference_string);
 
 /*!
@@ -203,7 +238,7 @@
  *
  * \return The effective profile ao2 object.
  */
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_uri(const char *uri,
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_uri(const char *uri,
 	const char *reference_string);
 
 /*!
@@ -213,6 +248,6 @@
  *
  * \return 0 on success, any other value on error.
  */
-int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_effective_profile *eprofile);
+int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_eprofile *eprofile);
 
 #endif /* INCLUDE_ASTERISK_RES_GEOLOCATION_H_ */
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index a4d0607..40e13e2 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -62,6 +62,9 @@
 #define PJSIP_EXPIRES_NOT_SPECIFIED	((pj_uint32_t)-1)
 #endif
 
+#define PJSTR_PRINTF_SPEC "%.*s"
+#define PJSTR_PRINTF_VAR(_v) ((int)(_v).slen), ((_v).ptr)
+
 /* Response codes from RFC8224 */
 #define AST_STIR_SHAKEN_RESPONSE_CODE_STALE_DATE 403
 #define AST_STIR_SHAKEN_RESPONSE_CODE_USE_IDENTITY_HEADER 428
@@ -871,6 +874,10 @@
 	unsigned int stir_shaken;
 	/*! Should we authenticate OPTIONS requests per RFC 3261? */
 	unsigned int allow_unauthenticated_options;
+	/*! The name of the geoloc profile to apply when Asterisk receives a call from this endpoint */
+	AST_STRING_FIELD_EXTENDED(geoloc_incoming_call_profile);
+	/*! The name of the geoloc profile to apply when Asterisk sends a call to this endpoint */
+	AST_STRING_FIELD_EXTENDED(geoloc_outgoing_call_profile);
 };
 
 /*! URI parameter for symmetric transport */
diff --git a/main/datastore.c b/main/datastore.c
index f37ee6c..d5adfae 100644
--- a/main/datastore.c
+++ b/main/datastore.c
@@ -69,6 +69,10 @@
 {
 	int res = 0;
 
+	if (!datastore) {
+		return 0;
+	}
+
 	/* Using the destroy function (if present) destroy the data */
 	if (datastore->info->destroy != NULL && datastore->data != NULL) {
 		datastore->info->destroy(datastore->data);
diff --git a/res/res_geolocation.c b/res/res_geolocation.c
index 9108102..19dd84b 100644
--- a/res/res_geolocation.c
+++ b/res/res_geolocation.c
@@ -24,6 +24,8 @@
 
 
 #include "asterisk.h"
+#define AST_API_MODULE
+#include "asterisk/res_geolocation.h"
 #include "res_geolocation/geoloc_private.h"
 
 static int reload_module(void)
diff --git a/res/res_geolocation/geoloc_channel.c b/res/res_geolocation/geoloc_channel.c
deleted file mode 100644
index aa8ad87..0000000
--- a/res/res_geolocation/geoloc_channel.c
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2022, Sangoma Technologies Corporation
- *
- * George Joseph <gjoseph at sangoma.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
-
-#include "asterisk.h"
-#include "asterisk/datastore.h"
-#include "asterisk/res_geolocation.h"
-#include "geoloc_private.h"
-
-
-struct ast_sorcery *geoloc_sorcery;
-
-static void geoloc_datastore_destructor(void *obj)
-{
-	struct ast_geoloc_effective_profile *eprofile = obj;
-	ao2_ref(eprofile, -1);
-}
-
-
-static const struct ast_datastore_info geoloc_datastore_info = {
-	.type = "geolocation",
-	.destroy = geoloc_datastore_destructor
-};
-
-
-struct ast_datastore *ast_geoloc_datastore_create_from_profile_name(const char *profile_name)
-{
-	struct ast_datastore *ds = NULL;
-	struct ast_geoloc_effective_profile *eprofile = NULL;
-	struct ast_geoloc_profile *profile = NULL;
-
-	if (ast_strlen_zero(profile_name)) {
-		return NULL;
-	}
-
-	ds = ast_datastore_alloc(&geoloc_datastore_info, NULL);
-	if (!ds) {
-		ast_log(LOG_ERROR, "A datasatore couldn't be allocated for profile '%s'", profile_name);
-		return NULL;
-	}
-
-	profile = ast_sorcery_retrieve_by_id(geoloc_sorcery, "profile", profile_name);
-	if (!profile) {
-		ast_datastore_free(ds);
-		ast_log(LOG_ERROR, "A profile with the name '%s' was not found", profile_name);
-		return NULL;
-	}
-
-	eprofile = ast_geoloc_eprofile_create_from_profile(profile);
-	ao2_ref(profile, -1);
-	if (!eprofile) {
-		ast_datastore_free(ds);
-		ast_log(LOG_ERROR, "An effective profile with the name '%s' couldn't be allocated", profile_name);
-		return NULL;
-	}
-
-	ds->data = eprofile;
-
-	return ds;
-}
-
-int geoloc_channel_unload(void)
-{
-	if (geoloc_sorcery) {
-		ast_sorcery_unref(geoloc_sorcery);
-	}
-	return AST_MODULE_LOAD_SUCCESS;
-}
-
-int geoloc_channel_load(void)
-{
-	geoloc_sorcery = geoloc_get_sorcery();
-	return AST_MODULE_LOAD_SUCCESS;
-}
-
-int geoloc_channel_reload(void)
-{
-	return AST_MODULE_LOAD_SUCCESS;
-}
diff --git a/res/res_geolocation/geoloc_config.c b/res/res_geolocation/geoloc_config.c
index b707c89..309df22 100644
--- a/res/res_geolocation/geoloc_config.c
+++ b/res/res_geolocation/geoloc_config.c
@@ -19,8 +19,8 @@
 #include "asterisk.h"
 #include "asterisk/module.h"
 #include "asterisk/cli.h"
+#define AST_API_MODULE
 #include "geoloc_private.h"
-#include "asterisk/res_geolocation.h"
 
 static struct ast_sorcery *geoloc_sorcery;
 
@@ -433,7 +433,7 @@
 		struct ast_str *refinement_str = NULL;
 		struct ast_str *variables_str = NULL;
 		struct ast_str *resolved_str = NULL;
-		struct ast_geoloc_effective_profile *eprofile = ast_geoloc_eprofile_create_from_profile(profile);
+		struct ast_geoloc_eprofile *eprofile = ast_geoloc_eprofile_create_from_profile(profile);
 		ao2_ref(profile, -1);
 
 		if (!ast_strlen_zero(eprofile->location_reference)) {
@@ -514,6 +514,24 @@
 	AST_CLI_DEFINE(geoloc_config_cli_reload, "Reload Geolocation Configuration"),
 };
 
+struct ast_geoloc_location * AST_OPTIONAL_API_NAME(ast_geoloc_get_location)(const char *id)
+{
+	if (ast_strlen_zero(id)) {
+		return NULL;
+	}
+
+	return ast_sorcery_retrieve_by_id(geoloc_sorcery, "location", id);
+}
+
+struct ast_geoloc_profile * AST_OPTIONAL_API_NAME(ast_geoloc_get_profile)(const char *id)
+{
+	if (ast_strlen_zero(id)) {
+		return NULL;
+	}
+
+	return ast_sorcery_retrieve_by_id(geoloc_sorcery, "profile", id);
+}
+
 int geoloc_config_reload(void)
 {
 	if (geoloc_sorcery) {
@@ -589,3 +607,8 @@
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
+int AST_OPTIONAL_API_NAME(ast_geoloc_is_loaded)(void)
+{
+	return 1;
+}
+
diff --git a/res/res_geolocation/geoloc_datastore.c b/res/res_geolocation/geoloc_datastore.c
new file mode 100644
index 0000000..17e2847
--- /dev/null
+++ b/res/res_geolocation/geoloc_datastore.c
@@ -0,0 +1,219 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2022, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph at sangoma.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+#include "asterisk.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/datastore.h"
+#include "asterisk/res_geolocation.h"
+#include "asterisk/vector.h"
+#include "geoloc_private.h"
+
+#define GEOLOC_DS_TYPE "geoloc_eprofiles"
+
+struct ast_sorcery *geoloc_sorcery;
+
+struct eprofiles_datastore {
+	const char *id;
+	AST_VECTOR(geoloc_eprofiles, struct ast_geoloc_eprofile *) eprofiles;
+};
+
+static void geoloc_datastore_free(void *obj)
+{
+	struct eprofiles_datastore *eds = obj;
+
+	AST_VECTOR_RESET(&eds->eprofiles, ao2_cleanup);
+	AST_VECTOR_FREE(&eds->eprofiles);
+	ast_free(eds);
+}
+
+static const struct ast_datastore_info geoloc_datastore_info = {
+	.type = GEOLOC_DS_TYPE,
+	.destroy = geoloc_datastore_free
+};
+
+struct ast_datastore *ast_geoloc_datastore_create(const char *id)
+{
+	struct ast_datastore *ds = NULL;
+	struct eprofiles_datastore *eds = NULL;
+	int rc = 0;
+
+	if (ast_strlen_zero(id)) {
+		ast_log(LOG_ERROR, "A geoloc datastore can't be allocated with a NULL or empty id\n");
+		return NULL;
+	}
+
+	ds = ast_datastore_alloc(&geoloc_datastore_info, NULL);
+	if (!ds) {
+		ast_log(LOG_ERROR, "Geoloc datastore '%s' couldn't be allocated\n", id);
+		return NULL;
+	}
+
+	eds = ast_calloc(1, sizeof(*eds));
+	if (!eds) {
+		ast_datastore_free(ds);
+		ast_log(LOG_ERROR, "Private structure for geoloc datastore '%s' couldn't be allocated\n", id);
+		return NULL;
+	}
+	ds->data = eds;
+
+
+	rc = AST_VECTOR_INIT(&eds->eprofiles, 2);
+	if (rc != 0) {
+		ast_datastore_free(ds);
+		ast_log(LOG_ERROR, "Vector for geoloc datastore '%s' couldn't be initialized\n", id);
+		return NULL;
+	}
+
+	return ds;
+}
+
+int ast_geoloc_datastore_add_eprofile(struct ast_datastore *ds,
+	struct ast_geoloc_eprofile *eprofile)
+{
+	struct eprofiles_datastore *eds = NULL;
+	int rc = 0;
+
+	if (!ds || !ast_strings_equal(ds->info->type, GEOLOC_DS_TYPE) || !ds->data || !eprofile) {
+		return -1;
+	}
+
+	eds = ds->data;
+	rc = AST_VECTOR_APPEND(&eds->eprofiles, eprofile);
+	if (rc != 0) {
+		ast_log(LOG_ERROR, "Couldn't add eprofile '%s' to geoloc datastore '%s'\n", eprofile->id, eds->id);
+	}
+
+	return rc;
+}
+
+int ast_geoloc_datastore_size(struct ast_datastore *ds)
+{
+	struct eprofiles_datastore *eds = NULL;
+
+	if (!ds || !ast_strings_equal(ds->info->type, GEOLOC_DS_TYPE) || !ds->data) {
+		return -1;
+	}
+
+	eds = ds->data;
+
+	return AST_VECTOR_SIZE(&eds->eprofiles);
+}
+
+struct ast_geoloc_eprofile *ast_geoloc_datastore_get_eprofile(struct ast_datastore *ds, int ix)
+{
+	struct eprofiles_datastore *eds = NULL;
+	struct ast_geoloc_eprofile *eprofile;
+
+	if (!ds || !ast_strings_equal(ds->info->type, GEOLOC_DS_TYPE) || !ds->data) {
+		return NULL;
+	}
+
+	eds = ds->data;
+
+	if (ix >= AST_VECTOR_SIZE(&eds->eprofiles)) {
+		return NULL;
+	}
+
+	eprofile  = AST_VECTOR_GET(&eds->eprofiles, ix);
+	return ao2_bump(eprofile);
+}
+
+struct ast_datastore *ast_geoloc_datastore_create_from_eprofile(
+	struct ast_geoloc_eprofile *eprofile)
+{
+	struct ast_datastore *ds;
+	int rc = 0;
+
+	if (!eprofile) {
+		return NULL;
+	}
+
+	ds = ast_geoloc_datastore_create(eprofile->id);
+	if (!ds) {
+		return NULL;
+	}
+
+	rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);
+	if (rc != 0) {
+		ast_datastore_free(ds);
+		ds = NULL;
+	}
+
+	return ds;
+}
+
+struct ast_datastore *ast_geoloc_datastore_create_from_profile_name(const char *profile_name)
+{
+	struct ast_datastore *ds = NULL;
+	struct ast_geoloc_eprofile *eprofile = NULL;
+	struct ast_geoloc_profile *profile = NULL;
+	int rc = 0;
+
+	if (ast_strlen_zero(profile_name)) {
+		return NULL;
+	}
+
+	profile = ast_sorcery_retrieve_by_id(geoloc_sorcery, "profile", profile_name);
+	if (!profile) {
+		ast_log(LOG_ERROR, "A profile with the name '%s' was not found\n", profile_name);
+		return NULL;
+	}
+
+	ds = ast_geoloc_datastore_create(profile_name);
+	if (!ds) {
+		ast_log(LOG_ERROR, "A datasatore couldn't be allocated for profile '%s'\n", profile_name);
+		ao2_ref(profile, -1);
+		return NULL;
+	}
+
+	eprofile = ast_geoloc_eprofile_create_from_profile(profile);
+	ao2_ref(profile, -1);
+	if (!eprofile) {
+		ast_datastore_free(ds);
+		ast_log(LOG_ERROR, "An effective profile with the name '%s' couldn't be allocated\n", profile_name);
+		return NULL;
+	}
+
+	rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);
+	ao2_ref(eprofile, -1);
+	if (rc != 0) {
+		ast_datastore_free(ds);
+		ds = NULL;
+	}
+
+	return ds;
+}
+
+int geoloc_channel_unload(void)
+{
+	if (geoloc_sorcery) {
+		ast_sorcery_unref(geoloc_sorcery);
+	}
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_channel_load(void)
+{
+	geoloc_sorcery = geoloc_get_sorcery();
+	return AST_MODULE_LOAD_SUCCESS;
+}
+
+int geoloc_channel_reload(void)
+{
+	return AST_MODULE_LOAD_SUCCESS;
+}
diff --git a/res/res_geolocation/geoloc_eprofile.c b/res/res_geolocation/geoloc_eprofile.c
index 493265e..bc334e7 100644
--- a/res/res_geolocation/geoloc_eprofile.c
+++ b/res/res_geolocation/geoloc_eprofile.c
@@ -33,7 +33,7 @@
 static struct ast_sorcery *geoloc_sorcery;
 
 static void geoloc_effective_profile_destructor(void *obj) {
-	struct ast_geoloc_effective_profile *eprofile = obj;
+	struct ast_geoloc_eprofile *eprofile = obj;
 
 	ast_string_field_free_memory(eprofile);
 	ast_variables_destroy(eprofile->location_vars);
@@ -43,9 +43,9 @@
 	ast_variables_destroy(eprofile->usage_rules_vars);
 }
 
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_alloc(const char *name)
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_alloc(const char *name)
 {
-	struct ast_geoloc_effective_profile *eprofile = ao2_alloc_options(sizeof(*eprofile),
+	struct ast_geoloc_eprofile *eprofile = ao2_alloc_options(sizeof(*eprofile),
 		geoloc_effective_profile_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
 
 	ast_string_field_init(eprofile, 256);
@@ -54,7 +54,7 @@
 	return eprofile;
 }
 
-int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_effective_profile *eprofile)
+int ast_geoloc_eprofile_refresh_location(struct ast_geoloc_eprofile *eprofile)
 {
 	struct ast_geoloc_location *loc = NULL;
 	struct ast_variable *var;
@@ -68,13 +68,16 @@
 		}
 
 		eprofile->format = loc->format;
+
 		ast_variables_destroy(eprofile->location_vars);
-		eprofile->location_vars = ast_variables_dup(loc->location_vars);
+		eprofile->location_vars = loc->location_vars ? ast_variables_dup(loc->location_vars) : NULL;
 		ao2_ref(loc, -1);
+		loc = NULL;
 	}
 
 	ast_variables_destroy(eprofile->effective_location);
-	eprofile->effective_location = ast_variables_dup(eprofile->location_vars);
+	eprofile->effective_location = eprofile->location_vars ? ast_variables_dup(eprofile->location_vars) : NULL;
+
 	if (eprofile->location_refinement) {
 		for (var = eprofile->location_refinement; var; var = var->next) {
 			struct ast_variable *newvar = ast_variable_new(var->name, var->value, "");
@@ -87,9 +90,9 @@
 	return 0;
 }
 
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile)
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_profile(struct ast_geoloc_profile *profile)
 {
-	struct ast_geoloc_effective_profile *eprofile;
+	struct ast_geoloc_eprofile *eprofile;
 	const char *profile_id = ast_sorcery_object_get_id(profile);
 
 	eprofile = ast_geoloc_eprofile_alloc(profile_id);
@@ -113,10 +116,10 @@
 	return eprofile;
 }
 
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_uri(const char *uri,
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_uri(const char *uri,
 	const char *reference_string)
 {
-	struct ast_geoloc_effective_profile *eprofile = NULL;
+	struct ast_geoloc_eprofile *eprofile = NULL;
 	char *local_uri;
 
 	if (ast_strlen_zero(uri)) {
@@ -143,11 +146,11 @@
 	return eprofile;
 }
 
-static struct ast_geoloc_effective_profile *geoloc_eprofile_create_from_xslt_result(
+static struct ast_geoloc_eprofile *geoloc_eprofile_create_from_xslt_result(
 	struct ast_xml_doc *result_doc,
 	const char *reference_string)
 {
-	struct ast_geoloc_effective_profile *eprofile;
+	struct ast_geoloc_eprofile *eprofile;
 	struct ast_xml_node *presence = NULL;
 	struct ast_xml_node *pidf_element = NULL;
 	struct ast_xml_node *location_info = NULL;
@@ -206,11 +209,11 @@
 	return eprofile;
 }
 
-struct ast_geoloc_effective_profile *ast_geoloc_eprofile_create_from_pidf(
+struct ast_geoloc_eprofile *ast_geoloc_eprofile_create_from_pidf(
 	struct ast_xml_doc *pidf_xmldoc, const char *reference_string)
 {
 	RAII_VAR(struct ast_xml_doc *, result_doc, NULL, ast_xml_close);
-	struct ast_geoloc_effective_profile *eprofile;
+	struct ast_geoloc_eprofile *eprofile;
 
 	/*
 	 * The namespace prefixes used here (dm, def, gp, etc) don't have to match
@@ -326,7 +329,7 @@
 AST_TEST_DEFINE(test_create_from_uri)
 {
 
-	RAII_VAR(struct ast_geoloc_effective_profile *, eprofile,  NULL, ao2_cleanup);
+	RAII_VAR(struct ast_geoloc_eprofile *, eprofile,  NULL, ao2_cleanup);
 	const char *uri = NULL;
 	int rc = AST_TEST_PASS;
 
@@ -364,7 +367,7 @@
 	)
 {
 	RAII_VAR(struct ast_str *, str, NULL, ast_free);
-	RAII_VAR(struct ast_geoloc_effective_profile *, eprofile,  NULL, ao2_cleanup);
+	RAII_VAR(struct ast_geoloc_eprofile *, eprofile,  NULL, ao2_cleanup);
 	RAII_VAR(struct ast_xml_doc *, result_doc, NULL, ast_xml_close);
 	const char *search[] = { "path", path, NULL };
 
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 1e41360..8900964 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -53,6 +53,7 @@
 	<depend>res_sorcery_memory</depend>
 	<depend>res_sorcery_astdb</depend>
 	<use type="module">res_statsd</use>
+	<use type="module">res_geolocation</use>
 	<support_level>core</support_level>
  ***/
 
@@ -2714,5 +2715,5 @@
 	.reload = reload_module,
 	.load_pri = AST_MODPRI_CHANNEL_DEPEND - 5,
 	.requires = "dnsmgr,res_pjproject,res_sorcery_config,res_sorcery_memory,res_sorcery_astdb",
-	.optional_modules = "res_statsd",
+	.optional_modules = "res_geolocation,res_statsd",
 );
diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml
index 6470ab6..0f7b5c5 100644
--- a/res/res_pjsip/pjsip_config.xml
+++ b/res/res_pjsip/pjsip_config.xml
@@ -1135,6 +1135,24 @@
 						responses.</para>
 					</description>
 				</configOption>
+				<configOption name="geoloc_incoming_call_profile" default="">
+					<synopsis>Geolocation profile to apply to incoming calls</synopsis>
+					<description><para>
+						This geolocation profile will be applied to all calls received
+						by the channel driver from the remote endpoint before they're
+						forwarded to the dialplan.
+						</para>
+					</description>
+				</configOption>
+				<configOption name="geoloc_outgoing_call_profile" default="">
+					<synopsis>Geolocation profile to apply to outgoing calls</synopsis>
+					<description><para>
+						This geolocation profile will be applied to all calls received
+						by the channel driver from the dialplan before they're forwarded
+						the remote endpoint.
+						</para>
+					</description>
+				</configOption>
 			</configObject>
 			<configObject name="auth">
 				<synopsis>Authentication type</synopsis>
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index eccc639..70d2a19 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -21,6 +21,7 @@
 #include <pjsip.h>
 #include <pjsip_ua.h>
 
+#include "asterisk/res_geolocation.h"
 #include "asterisk/res_pjsip.h"
 #include "include/res_pjsip_private.h"
 #include "asterisk/res_pjsip_cli.h"
@@ -1370,6 +1371,36 @@
 		}
 	}
 
+	if (!ast_strlen_zero(endpoint->geoloc_incoming_call_profile) ||
+		!ast_strlen_zero(endpoint->geoloc_outgoing_call_profile)) {
+
+		if (!ast_geoloc_is_loaded()) {
+			ast_log(LOG_ERROR, "A geoloc incoming and/or outgoing call_profile was specified on endpoint '%s'"
+				" but res_geolocation is not loaded.\n", ast_sorcery_object_get_id(endpoint));
+			return -1;
+		}
+
+		if (!ast_strlen_zero(endpoint->geoloc_incoming_call_profile)) {
+			struct ast_geoloc_profile *profile = ast_geoloc_get_profile(endpoint->geoloc_incoming_call_profile);
+			if (!profile) {
+				ast_log(LOG_ERROR, "geoloc_incoming_call_profile '%s' on endpoint '%s' doesn't exist\n",
+					endpoint->geoloc_incoming_call_profile, ast_sorcery_object_get_id(endpoint));
+				return -1;
+			}
+			ao2_cleanup(profile);
+		}
+
+		if (!ast_strlen_zero(endpoint->geoloc_outgoing_call_profile)) {
+			struct ast_geoloc_profile *profile = ast_geoloc_get_profile(endpoint->geoloc_outgoing_call_profile);
+			if (!profile) {
+				ast_log(LOG_ERROR, "geoloc_outgoing_call_profile '%s' on endpoint '%s' doesn't exist\n",
+					endpoint->geoloc_outgoing_call_profile, ast_sorcery_object_get_id(endpoint));
+				return -1;
+			}
+			ao2_cleanup(profile);
+		}
+	}
+
 	return 0;
 }
 
@@ -2008,6 +2039,8 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ignore_183_without_sdp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, ignore_183_without_sdp));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "stir_shaken", "off", stir_shaken_handler, stir_shaken_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_unauthenticated_options", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_unauthenticated_options));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "geoloc_incoming_call_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, geoloc_incoming_call_profile));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "geoloc_outgoing_call_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, geoloc_outgoing_call_profile));
 
 	if (ast_sip_initialize_sorcery_transport()) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");
@@ -2168,6 +2201,12 @@
 		return NULL;
 	}
 
+	if (ast_string_field_init_extended(endpoint, geoloc_incoming_call_profile) ||
+		ast_string_field_init_extended(endpoint, geoloc_outgoing_call_profile)) {
+		ao2_cleanup(endpoint);
+		return NULL;
+	}
+
 	if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
 		ao2_cleanup(endpoint);
 		return NULL;
diff --git a/res/res_pjsip_geolocation.c b/res/res_pjsip_geolocation.c
new file mode 100644
index 0000000..cd8ba79
--- /dev/null
+++ b/res/res_pjsip_geolocation.c
@@ -0,0 +1,365 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2022, Sangoma Technologies Corporation
+ *
+ * George Joseph <gjoseph at sangoma.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+	<depend>res_geolocation</depend>
+	<depend>pjproject</depend>
+	<depend>res_pjsip</depend>
+	<depend>res_pjsip_session</depend>
+	<depend>chan_pjsip</depend>
+	<depend>libxml2</depend>
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+#include "asterisk/module.h"
+#include "asterisk/xml.h"
+#include "asterisk/res_geolocation.h"
+
+#include <pjsip_ua.h>
+#include "asterisk/res_pjsip.h"
+#include "asterisk/res_pjsip_session.h"
+
+static pj_str_t GEOLOCATION_HDR;
+
+static int find_pidf(const char *session_name, struct pjsip_rx_data *rdata, char *geoloc_uri,
+	char **pidf_body, unsigned int *pidf_len)
+{
+	/*
+	 * If the URI is "cid" then we're going to search for a pidf document
+	 * in the body of the message.  If there's no body, there's no point.
+	 */
+	if (!rdata->msg_info.msg->body) {
+		ast_log(LOG_WARNING, "%s: There's no message body in which to search for '%s'.  Skipping\n",
+			session_name, geoloc_uri);
+		return -1;
+	}
+
+	/*
+	 * If the message content type is 'application/pidf+xml', then the pidf is
+	 * the only document in the message and we'll just parse the entire body
+	 * as xml.  If it's 'multipart/mixed' then we have to find the part that
+	 * has a Content-ID heder value matching the URI.
+	 */
+	if (ast_sip_are_media_types_equal(&rdata->msg_info.ctype->media,
+		&pjsip_media_type_application_pidf_xml)) {
+		*pidf_body = rdata->msg_info.msg->body->data;
+		*pidf_len = rdata->msg_info.msg->body->len;
+	} else if (ast_sip_are_media_types_equal(&rdata->msg_info.ctype->media,
+		&pjsip_media_type_multipart_mixed)) {
+		pj_str_t cid = pj_str(geoloc_uri);
+		pjsip_multipart_part *mp = pjsip_multipart_find_part_by_cid_str(
+			rdata->tp_info.pool, rdata->msg_info.msg->body, &cid);
+
+		if (!mp) {
+			ast_log(LOG_WARNING, "%s: A Geolocation header was found with URI '%s'"
+				" but the associated multipart part was not found in the message body.  Skipping URI",
+				session_name, geoloc_uri);
+			return -1;
+		}
+		*pidf_body = mp->body->data;
+		*pidf_len = mp->body->len;
+	} else {
+		ast_log(LOG_WARNING, "%s: A Geolocation header was found with URI '%s'"
+			" but no pidf document with that content id was found.  Skipping URI",
+			session_name, geoloc_uri);
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int handle_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
+{
+	const char *session_name = ast_sip_session_get_name(session);
+	struct ast_sip_endpoint *endpoint = session->endpoint;
+	struct ast_channel *channel = session->channel;
+	RAII_VAR(struct ast_geoloc_profile *, config_profile, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_geoloc_eprofile *, config_eprofile, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_datastore *, ds, NULL, ast_datastore_free);
+	size_t eprofile_count = 0;
+	char *geoloc_hdr_value = NULL;
+	char *duped_geoloc_hdr_value = NULL;
+	char *geoloc_uri = NULL;
+	int rc = 0;
+	pjsip_generic_string_hdr *geoloc_hdr =
+		pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &GEOLOCATION_HDR, NULL);
+	SCOPE_ENTER(3, "%s\n", session_name);
+
+	if (!endpoint) {
+		SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no endpoint.  Skipping.\n",
+			session_name);
+	}
+
+	if (!channel) {
+		SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Session has no channel.  Skipping.\n",
+			session_name);
+	}
+
+	if (!geoloc_hdr) {
+		ast_trace(4, "%s: Message has no Geolocation header\n", session_name);
+	} else {
+		ast_trace(4, "%s: Geolocation: " PJSTR_PRINTF_SPEC, session_name,
+			PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
+	}
+
+	if (ast_strlen_zero(endpoint->geoloc_incoming_call_profile)) {
+		if (geoloc_hdr) {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has Geolocation header '"
+				PJSTR_PRINTF_SPEC "' but endpoint has no geoloc_incoming_call_profile. "
+				"Geolocation info discarded.\n", session_name,
+				PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
+		} else {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Endpoint has no geoloc_incoming_call_profile. "
+				"Skipping.\n", session_name);
+		}
+	}
+
+	config_profile = ast_geoloc_get_profile(endpoint->geoloc_incoming_call_profile);
+	if (!config_profile) {
+		SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_NOTICE, "%s: Message has Geolocation header '"
+			PJSTR_PRINTF_SPEC "' but endpoint's geoloc_incoming_call_profile doesn't exist. "
+			"Geolocation info discarded.\n", session_name,
+			PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
+	}
+
+	ds = ast_geoloc_datastore_create(session_name);
+	if (!ds) {
+		SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
+			"%s: Couldn't allocate a geoloc datastore\n", session_name);
+	}
+
+	if (config_profile->location_disposition == AST_GEOLOC_LOC_DISP_DISCARD) {
+		ast_trace(4, "%s: Profile '%s' location_disposition is 'discard' so "
+			"discarding Geolocation: " PJSTR_PRINTF_SPEC, session_name,
+			ast_sorcery_object_get_id(config_profile),
+			PJSTR_PRINTF_VAR(geoloc_hdr->hvalue));
+
+		config_eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);
+		if (!config_eprofile) {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Unable to create eprofile from "
+				"profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));
+		}
+
+		rc = ast_geoloc_datastore_add_eprofile(ds, config_eprofile);
+		if (rc != 0) {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
+				"%s: Couldn't add eprofile '%s' to datastore\n", session_name,
+				config_eprofile->id);
+		}
+
+		ast_channel_datastore_add(channel, ds);
+		/*
+		 * We gave our eprofile reference to the datastore and the
+		 * datastore to the channel so don't let RAII_VAR clean them up.
+		 */
+		config_eprofile = NULL;
+		ds = NULL;
+
+		SCOPE_EXIT_RTN_VALUE(0, "%s: Added geoloc datastore with 1 eprofile\n",
+			session_name);
+	} else if (config_profile->location_disposition == AST_GEOLOC_LOC_DISP_PREPEND) {
+		ast_trace(4, "%s: Profile '%s' location_disposition is 'prepend' so "
+			"adding to datastore first", session_name, ast_sorcery_object_get_id(config_profile));
+
+		config_eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);
+		if (!config_eprofile) {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Unable to create eprofile from"
+				" profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));
+		}
+
+		rc = ast_geoloc_datastore_add_eprofile(ds, config_eprofile);
+		if (rc != 0) {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
+				"%s: Couldn't add eprofile '%s' to datastore\n", session_name,
+				config_eprofile->id);
+		}
+		config_eprofile = NULL;
+
+		if (!geoloc_hdr) {
+			ast_channel_datastore_add(channel, ds);
+			ds = NULL;
+
+			SCOPE_EXIT_RTN_VALUE(0, "%s: No Geolocation header so just adding config profile "
+				"'%s' to datastore\n", session_name, ast_sorcery_object_get_id(config_profile));
+		}
+	} else if (config_profile->location_disposition == AST_GEOLOC_LOC_DISP_REPLACE) {
+		if (geoloc_hdr) {
+			ast_trace(4, "%s: Profile '%s' location_disposition is 'replace' so "
+				"we don't need to do anything with the configured profile", session_name,
+				ast_sorcery_object_get_id(config_profile));
+		} else {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
+				"%s: Profile '%s' location_disposition is 'replace' but there's"
+				"no Geolocation header and therefore no location info to replace"
+				"it with\n", session_name, ast_sorcery_object_get_id(config_profile));
+		}
+	}
+
+	geoloc_hdr_value = ast_alloca(geoloc_hdr->hvalue.slen + 1);
+	ast_copy_pj_str(geoloc_hdr_value, &geoloc_hdr->hvalue, geoloc_hdr->hvalue.slen + 1);
+	duped_geoloc_hdr_value = ast_strdupa(geoloc_hdr_value);
+
+	/*
+	 * From RFC-6442:
+	 * Geolocation-header = "Geolocation" HCOLON locationValue
+	 *                      *( COMMA locationValue )
+	 * locationValue      = LAQUOT locationURI RAQUOT
+	 *                      *(SEMI geoloc-param)
+	 * locationURI        = sip-URI / sips-URI / pres-URI
+	 *                        / http-URI / https-URI
+	 *	                      / cid-url ; (from RFC 2392)
+	 *                        / absoluteURI ; (from RFC 3261)
+	 */
+	while((geoloc_uri = ast_strsep(&duped_geoloc_hdr_value, ',', AST_STRSEP_TRIM))) {
+		/* geoloc_uri should now be <scheme:location> */
+		int uri_len = strlen(geoloc_uri);
+		char *pidf_body = NULL;
+		unsigned int pidf_len = 0;
+		struct ast_xml_doc *incoming_doc = NULL;
+
+		struct ast_geoloc_eprofile *eprofile = NULL;
+		int rc = 0;
+
+		ast_trace(4, "Processing URI '%s'\n", geoloc_uri);
+
+		if (geoloc_uri[0] != '<' || geoloc_uri[uri_len - 1] != '>') {
+			ast_log(LOG_WARNING, "%s: Geolocation header has bad URI '%s'.  Skipping\n", session_name,
+				geoloc_uri);
+			continue;
+		}
+		/* Trim the trailing '>' and skip the leading '<' */
+		geoloc_uri[uri_len - 1] = '\0';
+		geoloc_uri++;
+		/*
+		 * If the URI isn't "cid" then we're just going to pass it through.
+		 */
+		if (!ast_begins_with(geoloc_uri, "cid:")) {
+			ast_trace(4, "Processing URI '%s'.  Reference\n", geoloc_uri);
+
+			eprofile = ast_geoloc_eprofile_create_from_uri(geoloc_uri, session_name);
+			if (!eprofile) {
+				ast_log(LOG_WARNING, "%s: Unable to create effective profile for URI '%s'.  Skipping\n",
+					session_name, geoloc_uri);
+				continue;
+			}
+		} else {
+			ast_trace(4, "Processing URI '%s'.  PIDF\n", geoloc_uri);
+			rc = find_pidf(session_name, rdata, geoloc_uri, &pidf_body, &pidf_len);
+			if (rc != 0 || !pidf_body || pidf_len == 0) {
+				continue;
+			}
+
+			incoming_doc = ast_xml_read_memory(pidf_body, pidf_len);
+			if (!incoming_doc) {
+				ast_log(LOG_WARNING, "%s: Unable to parse pidf document for URI '%s'\n",
+					session_name, geoloc_uri);
+				continue;
+			}
+
+			eprofile = ast_geoloc_eprofile_create_from_pidf(incoming_doc, session_name);
+		}
+		eprofile->location_disposition = config_profile->location_disposition;
+		eprofile->send_location = config_profile->send_location;
+
+		ast_trace(4, "Processing URI '%s'.  Adding to datastore\n", geoloc_uri);
+		rc = ast_geoloc_datastore_add_eprofile(ds, eprofile);
+		if (rc != 0) {
+			ao2_ref(eprofile, -1);
+			ast_log(LOG_WARNING, "%s: Unable to add effective profile for URI '%s' to datastore.  Skipping\n",
+				session_name, geoloc_uri);
+		}
+	}
+
+	if (config_profile->location_disposition == AST_GEOLOC_LOC_DISP_APPEND) {
+		ast_trace(4, "%s: Profile '%s' location_disposition is 'prepend' so "
+			"adding to datastore first", session_name, ast_sorcery_object_get_id(config_profile));
+
+		config_eprofile = ast_geoloc_eprofile_create_from_profile(config_profile);
+		if (!config_eprofile) {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING, "%s: Unable to create eprofile from"
+				" profile '%s'\n", session_name, ast_sorcery_object_get_id(config_profile));
+		}
+
+		rc = ast_geoloc_datastore_add_eprofile(ds, config_eprofile);
+		if (rc != 0) {
+			SCOPE_EXIT_LOG_RTN_VALUE(0, LOG_WARNING,
+				"%s: Couldn't add eprofile '%s' to datastore\n", session_name,
+				config_eprofile->id);
+		}
+		config_eprofile = NULL;
+	}
+
+	eprofile_count = ast_geoloc_datastore_size(ds);
+	if (eprofile_count == 0) {
+		SCOPE_EXIT_RTN_VALUE(0,
+			"%s: Unable to add any effective profiles.  Not adding datastore to channel.\n",
+			session_name);
+	}
+
+	ast_channel_datastore_add(channel, ds);
+	ds = NULL;
+
+	SCOPE_EXIT_RTN_VALUE(0, "%s: Added geoloc datastore with %" PRIu64 " eprofiles\n",
+		session_name, eprofile_count);
+}
+
+static void handle_outgoing_request(struct ast_sip_session *session, struct pjsip_tx_data *tdata)
+{
+
+}
+
+static struct ast_sip_session_supplement geolocation_supplement = {
+	.method = "INVITE",
+	.priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 10,
+	.incoming_request = handle_incoming_request,
+	.outgoing_request = handle_outgoing_request,
+};
+
+static int reload_module(void)
+{
+	return 0;
+}
+
+static int unload_module(void)
+{
+	int res = 0;
+	ast_sip_session_unregister_supplement(&geolocation_supplement);
+
+	return res;
+}
+
+static int load_module(void)
+{
+	int res = 0;
+	GEOLOCATION_HDR = pj_str("Geolocation");
+
+	ast_sip_session_register_supplement(&geolocation_supplement);
+
+	return res;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "res_pjsip_geolocation Module for Asterisk",
+	.support_level = AST_MODULE_SUPPORT_CORE,
+	.load = load_module,
+	.unload = unload_module,
+	.reload = reload_module,
+	.load_pri = AST_MODPRI_CHANNEL_DEPEND - 1,
+	.requires = "res_geolocation,res_pjsip,res_pjsip_session,chan_pjsip",
+);

-- 
To view, visit https://gerrit.asterisk.org/c/asterisk/+/18191
To unsubscribe, or for help writing mail filters, visit https://gerrit.asterisk.org/settings

Gerrit-Project: asterisk
Gerrit-Branch: development/16/geolocation
Gerrit-Change-Id: I6ccb5108a5eec060efb8116502fbff3cc63d7554
Gerrit-Change-Number: 18191
Gerrit-PatchSet: 1
Gerrit-Owner: George Joseph <gjoseph at digium.com>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.digium.com/pipermail/asterisk-code-review/attachments/20220307/9f5a2228/attachment-0001.html>


More information about the asterisk-code-review mailing list