diff --git a/include/asterisk/pbx.h b/include/asterisk/pbx.h
index 55a5b9f4c8ba0f3407d0d68bb4f71d4e4aa27c13..f5f0691869ad7a39dbb2940bdd8b5446d38f9b66 100644
--- a/include/asterisk/pbx.h
+++ b/include/asterisk/pbx.h
@@ -1272,6 +1272,22 @@ const char *ast_get_extension_cidmatch(struct ast_exten *e);
 const char *ast_get_extension_app(struct ast_exten *e);
 const char *ast_get_extension_label(struct ast_exten *e);
 void *ast_get_extension_app_data(struct ast_exten *e);
+
+/*!
+ * \brief Fill a string buffer with the data at a dialplan extension
+ *
+ * \param buf String buffer
+ * \param bufsize Size of buf
+ * \param c Channel
+ * \param context Dialplan context
+ * \param exten Dialplan extension
+ * \param priority Dialplan priority
+ *
+ * \retval -1 Failed to obtain extension data
+ * \retval 0 Successfully obtained extension data
+ */
+int ast_get_extension_data(char *buf, int bufsize, struct ast_channel *c,
+	const char *context, const char *exten, int priority);
 /*! @} */
 
 /*! @name Registrar info functions ... */
@@ -1395,6 +1411,11 @@ int pbx_builtin_raise_exception(struct ast_channel *chan, const char *data);
 void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count);
 void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count);
 void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used);
+
+/*!
+ * \brief Substitutes variables, similar to pbx_substitute_variables_helper_full, but allows passing the context, extension, and priority in.
+ */
+void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int cp2_size, size_t *used, char *context, char *exten, int pri);
 /*! @} */
 
 /*! @name Substitution routines, using dynamic string buffers
diff --git a/main/pbx.c b/main/pbx.c
index 2ed1dd15cae7d1ed4377e463b3147d2b0cf2618b..07cf8e7567d914f70550810898faeb09f91b6220 100644
--- a/main/pbx.c
+++ b/main/pbx.c
@@ -8602,6 +8602,27 @@ void *ast_get_extension_app_data(struct ast_exten *e)
 	return e ? e->data : NULL;
 }
 
+int ast_get_extension_data(char *buf, int bufsize, struct ast_channel *c,
+	const char *context, const char *exten, int priority)
+{
+	struct ast_exten *e;
+	struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
+	ast_rdlock_contexts();
+	e = pbx_find_extension(c, NULL, &q, context, exten, priority, NULL, "", E_MATCH);
+	if (e) {
+		if (buf) {
+			const char *tmp = ast_get_extension_app_data(e);
+			if (tmp) {
+				ast_copy_string(buf, tmp, bufsize);
+			}
+		}
+		ast_unlock_contexts();
+		return 0;
+	}
+	ast_unlock_contexts();
+	return -1;
+}
+
 /*
  * Walking functions ...
  */
diff --git a/main/pbx_variables.c b/main/pbx_variables.c
index 91b5bbb2b451a0cf38cb73e97e01d72f24968b51..b6c0dd8d53b66aaf099785411a7566c71244578d 100644
--- a/main/pbx_variables.c
+++ b/main/pbx_variables.c
@@ -624,6 +624,11 @@ void ast_str_substitute_variables_varshead(struct ast_str **buf, ssize_t maxlen,
 }
 
 void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used)
+{
+	pbx_substitute_variables_helper_full_location(c, headp, cp1, cp2, count, used, NULL, NULL, 0);
+}
+
+void pbx_substitute_variables_helper_full_location(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count, size_t *used, char *context, char *exten, int pri)
 {
 	/* Substitutes variables into cp2, based on string cp1, cp2 NO LONGER NEEDS TO BE ZEROED OUT!!!!  */
 	const char *whereweare;
@@ -759,7 +764,16 @@ void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead
 				ast_debug(2, "Function %s result is '%s'\n", vars, cp4 ? cp4 : "(null)");
 			} else {
 				/* Retrieve variable value */
-				pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+				/* For dialplan location, if we were told what to substitute explicitly, use that instead */
+				if (exten && !strcmp(vars, "EXTEN")) {
+					ast_copy_string(workspace, exten, VAR_BUF_SIZE);
+				} else if (context && !strcmp(vars, "CONTEXT")) {
+					ast_copy_string(workspace, context, VAR_BUF_SIZE);
+				} else if (pri && !strcmp(vars, "PRIORITY")) {
+					snprintf(workspace, VAR_BUF_SIZE, "%d", pri);
+				} else {
+					pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
+				}
 			}
 			if (cp4) {
 				cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);