From 3d91c0a0c01474336aba0342a3d10c301f19d93f Mon Sep 17 00:00:00 2001
From: Tilghman Lesher <tilghman@meg.abyt.es>
Date: Tue, 16 Jan 2007 08:38:59 +0000
Subject: [PATCH] IAX2 remote variables - Bug 7619

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@51123 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 channels/chan_iax2.c   | 62 ++++++++++++++++++++++++++++++++++++++++++
 channels/iax2-parser.c | 34 ++++++++++++++++++++++-
 channels/iax2-parser.h |  1 +
 channels/iax2.h        |  1 +
 4 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index 2026f92e87..aa5c791b11 100644
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -2884,6 +2884,7 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
 	unsigned short callno = PTR_TO_CALLNO(c->tech_pvt);
 	struct parsed_dial_string pds;
 	struct create_addr_info cai;
+	struct ast_var_t *var;
 
 	if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
 		ast_log(LOG_WARNING, "Channel is already in use (%s)?\n", c->name);
@@ -3007,6 +3008,19 @@ static int iax2_call(struct ast_channel *c, char *dest, int timeout)
 	/* send the command using the appropriate socket for this peer */
 	iaxs[callno]->sockfd = cai.sockfd;
 
+	/* Add remote vars */
+	AST_LIST_TRAVERSE(&c->varshead, var, entries) {
+		if (!strncmp(ast_var_name(var), "~IAX2~", strlen("~IAX2~"))) {
+			char tmp[256];
+			int i;
+			/* Automatically divide the value up into sized chunks */
+			for (i = 0; i < strlen(ast_var_value(var)); i += 255 - (strlen(ast_var_name(var)) - strlen("~IAX2~") + 1)) {
+				snprintf(tmp, sizeof(tmp), "%s=%s", ast_var_name(var) + strlen("~IAX2~"), ast_var_value(var) + i);
+				iax_ie_append_str(&ied, IAX_IE_VARIABLE, tmp);
+			}
+		}
+	}
+
 	/* Transmit the string in a "NEW" request */
 	send_command(iaxs[callno], AST_FRAME_IAX, IAX_COMMAND_NEW, 0, ied.buf, ied.pos, -1);
 
@@ -6447,6 +6461,33 @@ static int socket_process_meta(int packet_len, struct ast_iax2_meta_hdr *meta, s
 	return 1;
 }
 
+static int acf_iaxvar_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
+{
+	const char *value;
+	char tmp[256];
+	snprintf(tmp, sizeof(tmp), "~IAX2~%s", data);
+	value = pbx_builtin_getvar_helper(chan, tmp);
+	ast_copy_string(buf, value ? value : "", len);
+	return 0;
+}
+
+static int acf_iaxvar_write(struct ast_channel *chan, const char *cmd, char *varname, const char *value)
+{
+	char tmp[256];
+	/* Inherit forever */
+	snprintf(tmp, sizeof(tmp), "__~IAX2~%s", varname);
+	pbx_builtin_setvar_helper(chan, tmp, value);
+	return 0;
+}
+
+static struct ast_custom_function iaxvar_function = {
+	.name = "IAXVAR",
+	.synopsis = "Sets or retrieves a remote variable",
+	.syntax = "IAXVAR(<varname>)",
+	.read = acf_iaxvar_read,
+	.write = acf_iaxvar_write,
+};
+
 static int socket_process(struct iax2_thread *thread)
 {
 	struct sockaddr_in sin;
@@ -6753,6 +6794,9 @@ retryowner:
 						} else {
 							if (option_debug)
 								ast_log(LOG_DEBUG, "Neat, somebody took away the channel at a magical time but i found it!\n");
+							/* Free remote variables (if any) */
+							if (ies.vars)
+								ast_variables_destroy(ies.vars);
 							ast_mutex_unlock(&iaxsl[fr->callno]);
 							return 1;
 						}
@@ -6996,6 +7040,18 @@ retryowner:
 								
 								if(!(c = ast_iax2_new(fr->callno, AST_STATE_RING, format)))
 									iax2_destroy(fr->callno);
+								else if (ies.vars) {
+									struct ast_variable *var, *prev = NULL;
+									char tmp[256];
+									for (var = ies.vars; var; var = var->next) {
+										if (prev)
+											free(prev);
+										prev = var;
+										snprintf(tmp, sizeof(tmp), "__~IAX2~%s", var->name);
+										pbx_builtin_setvar_helper(c, tmp, var->value);
+									}
+									ies.vars = NULL;
+								}
 							} else {
 								ast_set_flag(&iaxs[fr->callno]->state, IAX_STATE_TBD);
 								/* If this is a TBD call, we're ready but now what...  */
@@ -7586,6 +7642,10 @@ retryowner2:
 				iax_ie_append_byte(&ied0, IAX_IE_IAX_UNKNOWN, f.subclass);
 				send_command(iaxs[fr->callno], AST_FRAME_IAX, IAX_COMMAND_UNSUPPORT, 0, ied0.buf, ied0.pos, -1);
 			}
+			/* Free remote variables (if any) */
+			if (ies.vars)
+				ast_variables_destroy(ies.vars);
+
 			/* Don't actually pass these frames along */
 			if ((f.subclass != IAX_COMMAND_ACK) && 
 			  (f.subclass != IAX_COMMAND_TXCNT) && 
@@ -10025,6 +10085,7 @@ static int __unload_module(void)
 static int unload_module(void)
 {
 	ast_custom_function_unregister(&iaxpeer_function);
+	ast_custom_function_unregister(&iaxvar_function);
 	return __unload_module();
 }
 
@@ -10038,6 +10099,7 @@ static int load_module(void)
 	struct iax2_peer *peer = NULL;
 	
 	ast_custom_function_register(&iaxpeer_function);
+	ast_custom_function_register(&iaxvar_function);
 
 	iax_set_output(iax_debug_output);
 	iax_set_error(iax_error_output);
diff --git a/channels/iax2-parser.c b/channels/iax2-parser.c
index 1a5f4d9f86..5e76dd850f 100644
--- a/channels/iax2-parser.c
+++ b/channels/iax2-parser.c
@@ -39,6 +39,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/frame.h"
 #include "asterisk/utils.h"
 #include "asterisk/unaligned.h"
+#include "asterisk/config.h"
 #include "asterisk/lock.h"
 #include "asterisk/threadstorage.h"
 
@@ -262,6 +263,7 @@ static struct iax2_ie {
 	{ IAX_IE_RR_DELAY, "RR_DELAY", dump_short },
 	{ IAX_IE_RR_DROPPED, "RR_DROPPED", dump_int },
 	{ IAX_IE_RR_OOO, "RR_OUTOFORDER", dump_int },
+	{ IAX_IE_VARIABLE, "VARIABLE", dump_string },
 };
 
 static struct iax2_ie prov_ies[] = {
@@ -613,7 +615,8 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
 	/* Parse data into information elements */
 	int len;
 	int ie;
-	char tmp[256];
+	char tmp[256], *tmp2;
+	struct ast_variable *var, *var2, *prev;
 	memset(ies, 0, (int)sizeof(struct iax_ies));
 	ies->msgcount = -1;
 	ies->firmwarever = -1;
@@ -898,6 +901,35 @@ int iax_parse_ies(struct iax_ies *ies, unsigned char *data, int datalen)
 				ies->rr_ooo = ntohl(get_unaligned_uint32(data + 2));
 			}
 			break;
+		case IAX_IE_VARIABLE:
+			ast_copy_string(tmp, (char *)data + 2, len + 1);
+			tmp2 = strchr(tmp, '=');
+			if (tmp2)
+				*tmp2++ = '\0';
+			else
+				tmp2 = "";
+			/* Existing variable or new variable? */
+			for (var2 = ies->vars, prev = NULL; var2; prev = var2, var2 = var2->next) {
+				if (strcmp(tmp, var2->name) == 0) {
+					int len = strlen(var2->value) + strlen(tmp2) + 1;
+					char *tmp3 = alloca(len);
+					snprintf(tmp3, len, "%s%s", var2->value, tmp2);
+					var = ast_variable_new(tmp, tmp3);
+					var->next = var2->next;
+					if (prev)
+						prev->next = var;
+					else
+						ies->vars = var;
+					free(var2);
+					break;
+				}
+			}
+			if (!var2) {
+				var = ast_variable_new(tmp, tmp2);
+				var->next = ies->vars;
+				ies->vars = var;
+			}
+			break;
 		default:
 			snprintf(tmp, (int)sizeof(tmp), "Ignoring unknown information element '%s' (%d) of length %d\n", iax_ie2str(ie), ie, len);
 			outputf(tmp);
diff --git a/channels/iax2-parser.h b/channels/iax2-parser.h
index 1b95d099fe..35f8c69bdc 100644
--- a/channels/iax2-parser.h
+++ b/channels/iax2-parser.h
@@ -73,6 +73,7 @@ struct iax_ies {
 	unsigned short rr_delay;
 	unsigned int rr_dropped;
 	unsigned int rr_ooo;
+	struct ast_variable *vars;
 };
 
 #define DIRECTION_INGRESS 1
diff --git a/channels/iax2.h b/channels/iax2.h
index 2b0c8bb99c..084c4f2019 100644
--- a/channels/iax2.h
+++ b/channels/iax2.h
@@ -128,6 +128,7 @@
 #define IAX_IE_RR_DELAY				49		/* Max playout delay for received frames (in ms) u16 */
 #define IAX_IE_RR_DROPPED			50		/* Dropped frames (presumably by jitterbuf) u32 */
 #define IAX_IE_RR_OOO				51		/* Frames received Out of Order u32 */
+#define IAX_IE_VARIABLE				52		/* Remote variables */
 
 
 #define IAX_AUTH_PLAINTEXT			(1 << 0)
-- 
GitLab