From 3768d802279291a851291c22ed5722761aab5782 Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Fri, 6 Jan 2012 15:10:37 -0600
Subject: [PATCH] add app lists to white or blacklist apps in the execute tag

---
 conf/autoload_configs/httapi.conf.xml         |  9 +-
 src/mod/applications/mod_httapi/apps.cgi      | 34 +++++++
 .../applications/mod_httapi/httapi.conf.xml   | 13 ++-
 src/mod/applications/mod_httapi/mod_httapi.c  | 90 +++++++++++++++++--
 .../mod_httapi/mod_httapi_doc.txt             |  2 +-
 5 files changed, 137 insertions(+), 11 deletions(-)
 create mode 100755 src/mod/applications/mod_httapi/apps.cgi

diff --git a/conf/autoload_configs/httapi.conf.xml b/conf/autoload_configs/httapi.conf.xml
index d82e6b2eef..dfd7d692fb 100644
--- a/conf/autoload_configs/httapi.conf.xml
+++ b/conf/autoload_configs/httapi.conf.xml
@@ -28,7 +28,14 @@
 	<permission name="set-params" value="true"/>
 	<permission name="set-vars" value="false"/>
 	<permission name="extended-data" value="false"/>
-	<permission name="execute-apps" value="false"/>
+	<permission name="execute-apps" value="true">
+	  <!-- default to "deny" or "allow" -->
+	  <application-list default="deny">
+	    <!-- type attr can be "deny" or "allow" nothing defaults to opposite of the list default so allow in this case -->
+	    <application name="info"/>
+	    <application name="hangup"/>
+	  </application-list>
+	</permission>
 	<permission name="expand-vars-in-tag-body" value="false"/>
 	<permission name="dial" value="true"/>
 	<permission name="dial-set-context" value="false"/>
diff --git a/src/mod/applications/mod_httapi/apps.cgi b/src/mod/applications/mod_httapi/apps.cgi
new file mode 100755
index 0000000000..90414b810e
--- /dev/null
+++ b/src/mod/applications/mod_httapi/apps.cgi
@@ -0,0 +1,34 @@
+#!/usr/bin/perl
+
+# Object initialization:
+use XML::Simple;
+use CGI;
+use Data::Dumper;
+use XML::Writer;
+
+my $q = CGI->new;
+my $exiting = $q->param("exiting");
+
+if ($exiting) {
+    print $q->header(-type => "text/plain");
+    print "OK";
+    exit();
+}
+
+print $q->header(-type => "text/xml");
+
+
+my $writer = new XML::Writer(OUTPUT => STDOUT, DATA_MODE => 1);
+
+$writer->startTag('document', type => 'xml/freeswitch-httapi');
+
+$writer->startTag('work');
+$writer->emptyTag('pause', milliseconds => "500");
+$writer->emptyTag('execute', application => "info");
+$writer->dataElement('execute', "user_busy", application => "hangup");
+$writer->endTag('work');
+
+
+$writer->endTag('document');
+$writer->end();
+
diff --git a/src/mod/applications/mod_httapi/httapi.conf.xml b/src/mod/applications/mod_httapi/httapi.conf.xml
index 05b321e11b..dfd7d692fb 100644
--- a/src/mod/applications/mod_httapi/httapi.conf.xml
+++ b/src/mod/applications/mod_httapi/httapi.conf.xml
@@ -1,8 +1,8 @@
 <configuration name="httapi.conf" description="HT-TAPI Hypertext Telephony API">
   <settings>
-    <!-- print xml on the console -->
+    <!-- print xml on the consol -->
     <param name="debug" value="true"/>
-    <!-- time to keep audio files when discovered they were deleted from the http server -->
+    <!-- time to keep audio files when discoverd they were deleted from the http server -->
     <param name="file-not-found-expires" value="300"/>
     <!-- how often to re-check the server to make sure the remote file has not changed -->
     <param name="file-cache-ttl" value="300"/>
@@ -28,7 +28,14 @@
 	<permission name="set-params" value="true"/>
 	<permission name="set-vars" value="false"/>
 	<permission name="extended-data" value="false"/>
-	<permission name="execute-apps" value="false"/>
+	<permission name="execute-apps" value="true">
+	  <!-- default to "deny" or "allow" -->
+	  <application-list default="deny">
+	    <!-- type attr can be "deny" or "allow" nothing defaults to opposite of the list default so allow in this case -->
+	    <application name="info"/>
+	    <application name="hangup"/>
+	  </application-list>
+	</permission>
 	<permission name="expand-vars-in-tag-body" value="false"/>
 	<permission name="dial" value="true"/>
 	<permission name="dial-set-context" value="false"/>
diff --git a/src/mod/applications/mod_httapi/mod_httapi.c b/src/mod/applications/mod_httapi/mod_httapi.c
index ae87ad5c40..17454b32f0 100644
--- a/src/mod/applications/mod_httapi/mod_httapi.c
+++ b/src/mod/applications/mod_httapi/mod_httapi.c
@@ -40,7 +40,7 @@ typedef struct profile_perms_s {
 	switch_byte_t set_vars;
 	switch_byte_t extended_data;
 	switch_byte_t execute_apps;
-	switch_byte_t expand_vars_in_tag_body;
+	switch_byte_t expand_vars;
 	struct {
 		switch_byte_t enabled;
 		switch_byte_t set_context;
@@ -95,6 +95,8 @@ typedef struct client_profile_s {
 	struct {
 		char *context;
 		char *dp;
+		switch_event_t *app_list;
+		int default_allow;
 	} dial_params;
 
 } client_profile_t;
@@ -627,11 +629,41 @@ static switch_status_t parse_sms(const char *tag_name, client_t *client, switch_
 	return SWITCH_STATUS_SUCCESS;
 }
 
+static int check_app_perm(client_t *client, const char *app_name)
+{
+	const char *v;
+	int r = 0;
+
+	if (!client->profile->perms.execute_apps) {
+		return 0;
+	}
+
+	if (!client->profile->dial_params.app_list) {
+		return 1;
+	}
+
+	if ((v = switch_event_get_header(client->profile->dial_params.app_list, app_name))) {
+		if (*v == 'd') {
+			r = 0;
+		} else {
+			r = 1;
+		}
+	} else {
+		r = client->profile->dial_params.default_allow;
+	}
+	
+
+	return r;
+}
+
 static switch_status_t parse_execute(const char *tag_name, client_t *client, switch_xml_t tag, const char *body)
 {
 	const char *app_name = switch_xml_attr(tag, "application");
+	const char *data = switch_xml_attr(tag, "data");
 
-	if (!client->profile->perms.execute_apps) {
+	if (zstr(data)) data = body;
+
+	if (!check_app_perm(client, app_name)) {
 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Permission Denied!\n");
 		switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
 		return SWITCH_STATUS_FALSE;
@@ -642,7 +674,19 @@ static switch_status_t parse_execute(const char *tag_name, client_t *client, swi
 		switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
 		return SWITCH_STATUS_FALSE;
 	} else {
-		switch_core_session_execute_application(client->session, app_name, body);
+		if (!client->profile->perms.expand_vars) {
+			const char *p;
+
+			for(p = data; p && *p; p++) {
+				if (*p == '$') {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Expand Variables: Permission Denied!\n");
+					switch_channel_hangup(client->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+					return SWITCH_STATUS_FALSE;
+				}
+			}
+		}
+
+		switch_core_session_execute_application(client->session, app_name, data);
 	}
 
 	return SWITCH_STATUS_SUCCESS;
@@ -928,7 +972,7 @@ static switch_status_t parse_xml(client_t *client)
 							char *expanded = tag->txt;
 							switch_event_t *templ_data;
 
-							if (tag->txt && client->profile->perms.expand_vars_in_tag_body) {
+							if (tag->txt && client->profile->perms.expand_vars) {
 								switch_channel_get_variables(client->channel, &templ_data);
 								switch_event_merge(templ_data, client->params);
 								expanded = switch_event_expand_headers(templ_data, tag->txt);
@@ -1525,8 +1569,31 @@ static switch_status_t do_config(void)
 					profile->perms.extended_data = switch_true(val);
 				} else if (!strcasecmp(var, "execute-apps")) {
 					profile->perms.execute_apps = switch_true(val);
-				} else if (!strcasecmp(var, "expand-vars-in-tag-body")) {
-					profile->perms.expand_vars_in_tag_body = switch_true(val);
+					
+					if (profile->perms.execute_apps) {
+						switch_xml_t x_list, x_app;
+						if ((x_list = switch_xml_child(param, "application-list"))) {
+							char *var = (char *) switch_xml_attr_soft(param, "default");
+							
+							profile->dial_params.default_allow = (var && !strcasecmp(var, "allow"));
+							switch_event_create(&profile->dial_params.app_list, SWITCH_EVENT_CLONE);
+							profile->dial_params.app_list->flags |= EF_UNIQ_HEADERS;
+
+							for (x_app = switch_xml_child(x_list, "application"); x_app; x_app = x_app->next) {
+								const char *name = switch_xml_attr(x_app, "name");
+								const char *type = switch_xml_attr(x_app, "type");
+
+								if (zstr(type)) type = profile->dial_params.default_allow ? "deny" : "allow";
+
+								if (name) {
+									switch_event_add_header_string(profile->dial_params.app_list, SWITCH_STACK_BOTTOM, name, type);
+								}
+							}
+						}
+					}
+					
+				} else if (!strcasecmp(var, "expand-vars")) {
+					profile->perms.expand_vars = switch_true(val);
 				} else if (!strcasecmp(var, "dial")) {
 					profile->perms.dial.enabled = switch_true(val);
 				} else if (!strcasecmp(var, "dial-set-context")) {
@@ -2240,6 +2307,17 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_httapi_load)
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_httapi_shutdown)
 {
 	hash_node_t *ptr = NULL;
+	client_profile_t *profile;
+	switch_hash_index_t *hi;
+	void *val;
+	const void *vvar;
+
+	for (hi = switch_hash_first(NULL, globals.profile_hash); hi; hi = switch_hash_next(hi)) {
+		switch_hash_this(hi, &vvar, NULL, &val);
+		profile = (client_profile_t *) val;
+		switch_event_destroy(&profile->dial_params.app_list);
+	}
+
 
 	switch_core_hash_destroy(&globals.profile_hash);	
 	switch_core_hash_destroy(&globals.parse_hash);	
diff --git a/src/mod/applications/mod_httapi/mod_httapi_doc.txt b/src/mod/applications/mod_httapi/mod_httapi_doc.txt
index 8359d1278c..f6c94e6fd4 100644
--- a/src/mod/applications/mod_httapi/mod_httapi_doc.txt
+++ b/src/mod/applications/mod_httapi/mod_httapi_doc.txt
@@ -266,7 +266,7 @@ default-profile                 : <string>              default         Profile
 set-vars                        : <variables> tag can be parsed to set channel vars.
 extended-data                   : Extended data is sent like full channel event data.
 execute-apps                    : <execute> tag is enabled to execute apps.
-expand-vars-in-tag-body         : body content of tags are run trough variable expansion.
+expand-vars			: Allow expansion of FS ${variables}. (this opens up all FSAPI calls)
 *dial                           : <dial> tag is enabled allowing outbound dialing.
 dial-set-context                : <dial context=""> context attribute is permitted.
 dial-set-dialplan               : <dial dialplan=""> dialplan attribute is permitted.