[Freeswitch-svn] [commit] r3266 - freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference

Freeswitch SVN knhor at freeswitch.org
Sun Nov 5 13:45:43 EST 2006


Author: knhor
Date: Sun Nov  5 13:45:43 2006
New Revision: 3266

Modified:
   freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c

Log:
This is a large refactoring of existing code.
It turns the api_command interface into a table driven lookup instead of a linear if {} else {} statment.
It allows for single point of maintenance for all the command syntax help strings.
Broke the user conference control switch statements into functions in prep for configurable digits.
Add the ability to configure/disable the individual user conference control command digits per conference profile. (will become configurable digit strings later as per anthm's recomendation/request)
Add event logging to user control Mute/Unmute and Mute_Deaf/Unmute_Undeaf functions. (more to come later)


Modified: freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c
==============================================================================
--- freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c	(original)
+++ freeswitch/branches/knhor/trunk/src/mod/applications/mod_conference/mod_conference.c	Sun Nov  5 13:45:43 2006
@@ -47,6 +47,10 @@
 #define CONF_DBUFFER_MAX 0
 #define CONF_CHAT_PROTO "conf"
 
+#ifndef MIN
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#endif
+
 typedef enum {
 	FILE_STOP_CURRENT,
 	FILE_STOP_ALL
@@ -64,9 +68,8 @@
 	uint32_t threads;
 } globals;
 
+// forward declaration for conference_obj structure
 struct conference_member;
-struct conference_obj;
-typedef struct conference_obj conference_obj_t;
 typedef struct conference_member conference_member_t;
 
 typedef enum {
@@ -127,6 +130,7 @@
 	char *kicked_sound;
 	char *caller_id_name;
 	char *caller_id_number;
+	char *caller_dtmf_control;
 	char *pin;
 	char *pin_sound;
 	char *bad_pin_sound;
@@ -147,6 +151,7 @@
 	int32_t energy_level;
 	uint8_t min;
 };
+typedef struct conference_obj conference_obj_t;
 
 /* Relationship with another member */
 struct conference_relationship {
@@ -197,6 +202,15 @@
 };
 typedef struct conference_record conference_record_t;
 
+// API command parser
+typedef struct api_command {
+	char *pname;
+	void *pfnapicmd;
+//	int (*pfnapicmd)();
+	int  fntype;
+	char *psyntax;
+} api_command_t;
+
 /* Function Prototypes */
 static uint32_t next_member_id(void);
 static conference_relationship_t *member_get_relationship(conference_member_t *member, conference_member_t *other_member);
@@ -232,10 +246,26 @@
 static switch_status_t chat_send(char *proto, char *from, char *to, char *subject, char *body, char *hint);
 static void launch_conference_record_thread(conference_obj_t *conference, char *path);
 
+typedef int (*conf_api_args_cmd_t)(conference_obj_t*, switch_stream_handle_t*, int, char**);
+typedef int (*conf_api_member_cmd_t)(conference_member_t*, switch_stream_handle_t*, void*);
+typedef int (*conf_api_text_cmd_t)(conference_obj_t*, switch_stream_handle_t*, char*);
+
 static void conference_member_itterator(conference_obj_t *conference,
 										switch_stream_handle_t *stream,
-										int (*pfncallback)(conference_obj_t*, conference_member_t*, int, switch_stream_handle_t*, void*),
+										conf_api_member_cmd_t pfncallback,
 										void *data);
+static int conference_function_api_mute(conference_member_t *member,
+										switch_stream_handle_t *stream,
+										void *data);
+static int conference_function_api_unmute(conference_member_t *member,
+										switch_stream_handle_t *stream,
+										void *data);
+static int conference_function_api_deaf(conference_member_t *member,
+										switch_stream_handle_t *stream,
+										void *data);
+static int conference_function_api_undeaf(conference_member_t *member,
+										switch_stream_handle_t *stream,
+										void *data);
 
 /* Return a Distinct ID # */
 static uint32_t next_member_id(void)
@@ -745,6 +775,173 @@
 	return NULL;
 }
 
+static void conference_loop_fn_mute_toggle(conference_member_t *member)
+{
+	char msg[512];
+
+	if (switch_test_flag(member, MFLAG_CAN_SPEAK)) {
+//		switch_clear_flag_locked(member, MFLAG_CAN_SPEAK | MFLAG_CAN_HEAR);
+		conference_function_api_mute(member, NULL, NULL);
+		conference_function_api_deaf(member, NULL, NULL);
+		if (member->conference->muted_sound) {
+			conference_member_play_file(member, member->conference->muted_sound, 0);
+		} else {
+			snprintf(msg, sizeof(msg), "Muted");
+			conference_member_say(member->conference, member, msg, 0);
+		}
+	} else {
+//		switch_set_flag_locked(member, MFLAG_CAN_SPEAK);
+		conference_function_api_unmute(member, NULL, NULL);
+		if (member->conference->unmuted_sound) {
+			conference_member_play_file(member, member->conference->unmuted_sound, 0);
+		} else {
+			snprintf(msg, sizeof(msg), "Un-Muted");
+			conference_member_say(member->conference, member, msg, 0);
+		}
+	}
+}
+
+static void conference_loop_fn_deafmute_toggle(conference_member_t *member)
+{
+	char msg[512];
+
+	if (switch_test_flag(member, MFLAG_CAN_SPEAK)) {
+//		switch_clear_flag_locked(member, MFLAG_CAN_SPEAK|MFLAG_CAN_HEAR);
+		conference_function_api_mute(member, NULL, NULL);
+		conference_function_api_deaf(member, NULL, NULL);
+		if (member->conference->muted_sound) {
+			conference_member_play_file(member, member->conference->muted_sound, 0);
+		} else {
+			snprintf(msg, sizeof(msg), "Muted");
+			conference_member_say(member->conference, member, msg, 0);
+		}
+	} else {
+//		switch_set_flag_locked(member, MFLAG_CAN_SPEAK|MFLAG_CAN_HEAR);
+		conference_function_api_unmute(member, NULL, NULL);
+		conference_function_api_undeaf(member, NULL, NULL);
+		if (member->conference->unmuted_sound) {
+			conference_member_play_file(member, member->conference->unmuted_sound, 0);
+		} else {
+			snprintf(msg, sizeof(msg), "UN-Muted");
+			conference_member_say(member->conference, member, msg, 0);
+		}
+	}
+}
+
+static void conference_loop_fn_energy_up(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->energy_level += 100;
+	if (member->energy_level > 1200) {
+		member->energy_level = 1200;
+	}
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_energy_equ_conference(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->energy_level = member->conference->energy_level;
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+					
+static void conference_loop_fn_energy_dn(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->energy_level -= 100;
+	if (member->energy_level < 0) {
+		member->energy_level = 0;
+	}
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_volume_talk_up(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->volume_out_level++;
+	switch_normalize_volume(member->volume_out_level);
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_volume_talk_zero(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->volume_out_level = 0;
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_volume_talk_dn(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->volume_out_level--;
+	switch_normalize_volume(member->volume_out_level);
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_volume_listen_up(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->volume_in_level++;
+	switch_normalize_volume(member->volume_in_level);
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_volume_listen_zero(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->volume_in_level = 0;
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_volume_listen_dn(conference_member_t *member)
+{
+	char msg[512];
+
+	switch_mutex_lock(member->flag_mutex);
+	member->volume_in_level--;
+	switch_normalize_volume(member->volume_in_level);
+	switch_mutex_unlock(member->flag_mutex);
+	snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
+	conference_member_say(member->conference, member, msg, 0);
+}
+
+static void conference_loop_fn_hangup(conference_member_t *member)
+{
+	switch_clear_flag_locked(member, MFLAG_RUNNING);
+}
+
 /* Sub-Routine called by a channel inside a conference */
 static void conference_loop(conference_member_t *member)
 {
@@ -790,7 +987,6 @@
 		switch_size_t file_data_len = samples * 2;
 		switch_size_t file_sample_len = samples;
 		char *digit;
-		char msg[512];
 		switch_event_t *event;
 
 		if (switch_core_session_dequeue_event(member->session, &event) == SWITCH_STATUS_SUCCESS) {
@@ -827,126 +1023,57 @@
 		}
 
 		if (switch_channel_has_dtmf(channel)) {
+			char *pcontroldigit;
+
 			switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf));
 
 			for (digit = dtmf; *digit; digit++) {
-				switch(*digit) {
-				case '0':
-					if (switch_test_flag(member, MFLAG_CAN_SPEAK)) {
-						switch_clear_flag_locked(member, MFLAG_CAN_SPEAK | MFLAG_CAN_HEAR);
-						if (member->conference->muted_sound) {
-							conference_member_play_file(member, member->conference->muted_sound, 0);
-						} else {
-							snprintf(msg, sizeof(msg), "Muted");
-							conference_member_say(member->conference, member, msg, 0);
-						}
-					} else {
-						switch_set_flag_locked(member, MFLAG_CAN_SPEAK);
-						if (member->conference->unmuted_sound) {
-							conference_member_play_file(member, member->conference->unmuted_sound, 0);
-						} else {
-							snprintf(msg, sizeof(msg), "Un-Muted");
-							conference_member_say(member->conference, member, msg, 0);
-						}
+				// The position of the dialed digit in the control string
+				// determines the operation that is being requested
+				// To disable the function for the user, make the control
+				// string digit something that can't be dialed... ie. a space
+				// The control string should have at least 12 characters in it
+				pcontroldigit = strchr(member->conference->caller_dtmf_control,*digit);
+
+				if(pcontroldigit != NULL) {
+					switch(pcontroldigit-member->conference->caller_dtmf_control) {
+					case 0:
+						conference_loop_fn_mute_toggle(member);
+						break;
+					case 1:
+						conference_loop_fn_deafmute_toggle(member);
+						break;
+					case 2:
+						conference_loop_fn_energy_up(member);
+						break;
+					case 3:
+						conference_loop_fn_energy_equ_conference(member);
+						break;
+					case 4:
+						conference_loop_fn_energy_dn(member);
+						break;
+					case 5:
+						conference_loop_fn_volume_talk_up(member);
+						break;
+					case 6:
+						conference_loop_fn_volume_talk_zero(member);
+						break;
+					case 7:
+						conference_loop_fn_volume_talk_dn(member);
+						break;
+					case 8:
+						conference_loop_fn_volume_listen_up(member);
+						break;
+					case 9:
+						conference_loop_fn_volume_listen_zero(member);
+						break;
+					case 10:
+						conference_loop_fn_volume_listen_dn(member);
+						break;
+					case 11:
+						conference_loop_fn_hangup(member);
+						break;
 					}
-					break;
-				case '*':
-					if (switch_test_flag(member, MFLAG_CAN_SPEAK)) {
-						switch_clear_flag_locked(member, MFLAG_CAN_SPEAK|MFLAG_CAN_HEAR);
-						if (member->conference->muted_sound) {
-							conference_member_play_file(member, member->conference->muted_sound, 0);
-						} else {
-							snprintf(msg, sizeof(msg), "Muted");
-							conference_member_say(member->conference, member, msg, 0);
-						}
-					} else {
-						switch_set_flag_locked(member, MFLAG_CAN_SPEAK|MFLAG_CAN_HEAR);
-						if (member->conference->unmuted_sound) {
-							conference_member_play_file(member, member->conference->unmuted_sound, 0);
-						} else {
-							snprintf(msg, sizeof(msg), "UN-Muted");
-							conference_member_say(member->conference, member, msg, 0);
-						}
-					}
-					break;
-				case '9':
-					switch_mutex_lock(member->flag_mutex);
-					member->energy_level += 100;
-					if (member->energy_level > 1200) {
-						member->energy_level = 1200;
-					}
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '8':
-					switch_mutex_lock(member->flag_mutex);
-					member->energy_level = member->conference->energy_level;
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '7':
-					switch_mutex_lock(member->flag_mutex);
-					member->energy_level -= 100;
-					if (member->energy_level < 0) {
-						member->energy_level = 0;
-					}
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Energy level %d", member->energy_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '3':
-					switch_mutex_lock(member->flag_mutex);
-					member->volume_out_level++;
-					switch_normalize_volume(member->volume_out_level);
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '2':
-					switch_mutex_lock(member->flag_mutex);
-					member->volume_out_level = 0;
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '1':
-					switch_mutex_lock(member->flag_mutex);
-					member->volume_out_level--;
-					switch_normalize_volume(member->volume_out_level);
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Volume level %d", member->volume_out_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '6':
-					switch_mutex_lock(member->flag_mutex);
-					member->volume_in_level++;
-					switch_normalize_volume(member->volume_in_level);
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '5':
-					switch_mutex_lock(member->flag_mutex);
-					member->volume_in_level = 0;
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '4':
-					switch_mutex_lock(member->flag_mutex);
-					member->volume_in_level--;
-					switch_normalize_volume(member->volume_in_level);
-					switch_mutex_unlock(member->flag_mutex);
-					snprintf(msg, sizeof(msg), "Gain level %d", member->volume_in_level);
-					conference_member_say(member->conference, member, msg, 0);
-					break;
-				case '#':
-					switch_clear_flag_locked(member, MFLAG_RUNNING);
-					break;
-				default:
-					break;
 				}
 			}
 		}
@@ -1462,7 +1589,7 @@
 	return SWITCH_STATUS_SUCCESS;
 }
 
-static void conference_member_itterator(conference_obj_t *conference, switch_stream_handle_t *stream, int (*pfncallback)(conference_obj_t*, conference_member_t*, int, switch_stream_handle_t*, void*), void *data)
+static void conference_member_itterator(conference_obj_t *conference, switch_stream_handle_t *stream, conf_api_member_cmd_t pfncallback, void *data)
 {
 	conference_member_t *member = NULL;
 
@@ -1470,7 +1597,7 @@
 		switch_mutex_lock(conference->member_mutex);
 
 		for (member = conference->members; member; member = member->next) {
-			pfncallback(conference,member,member->id,stream,data);
+			pfncallback(member,stream,data);
 		}
 		switch_mutex_unlock(conference->member_mutex);
 	}
@@ -1548,44 +1675,47 @@
 	switch_mutex_unlock(conference->member_mutex);
 }
 
-static int conference_function_mute_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_mute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		switch_clear_flag_locked(member, MFLAG_CAN_SPEAK);
 		if (member->conference->muted_sound) {
 			conference_member_play_file(member, member->conference->muted_sound, 0);
 		}
-		stream->write_function(stream, "OK mute %u\n", id);
+		if(stream != NULL) {
+			stream->write_function(stream, "OK mute %u\n", member->id);
+		}
 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 			switch_channel_t *channel = switch_core_session_get_channel(member->session);
 			switch_channel_event_set_data(channel, event);
 
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "mute-member");
 			switch_event_fire(&event);
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-static int conference_function_unmute_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_unmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		switch_set_flag_locked(member, MFLAG_CAN_SPEAK);
-		stream->write_function(stream, "OK unmute %u\n", id);
+		if(stream != NULL) {
+			stream->write_function(stream, "OK unmute %u\n", member->id);
+		}
 		if (member->conference->unmuted_sound) {
 			conference_member_play_file(member, member->conference->unmuted_sound, 0);
 		}
@@ -1593,76 +1723,77 @@
 			switch_channel_t *channel = switch_core_session_get_channel(member->session);
 			switch_channel_event_set_data(channel, event);
 
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "unmute-member");
 			switch_event_fire(&event);
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-static int conference_function_deaf_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_deaf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		switch_clear_flag_locked(member, MFLAG_CAN_HEAR);
-		stream->write_function(stream, "OK deaf %u\n", id);
+		if(stream != NULL) {
+			stream->write_function(stream, "OK deaf %u\n", member->id);
+		}
 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 			switch_channel_t *channel = switch_core_session_get_channel(member->session);
 			switch_channel_event_set_data(channel, event);
 
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "deaf-member");
 			switch_event_fire(&event);
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-static int conference_function_undeaf_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_undeaf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		switch_set_flag_locked(member, MFLAG_CAN_HEAR);
-		stream->write_function(stream, "OK undeaf %u\n", id);
+		if(stream != NULL) {
+			stream->write_function(stream, "OK undeaf %u\n", member->id);
+		}
 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 			switch_channel_t *channel = switch_core_session_get_channel(member->session);
 			switch_channel_event_set_data(channel, event);
 
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "undeaf-member");
 			switch_event_fire(&event);
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-static int conference_function_kick_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_kick(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		switch_mutex_lock(member->flag_mutex);
@@ -1670,30 +1801,31 @@
 		switch_set_flag(member, MFLAG_KICKED);
 		switch_mutex_unlock(member->flag_mutex);
 
-		stream->write_function(stream, "OK kicked %u\n", id);
+		if(stream != NULL) {
+			stream->write_function(stream, "OK kicked %u\n", member->id);
+		}
 
 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 			switch_channel_t *channel = switch_core_session_get_channel(member->session);
 			switch_channel_event_set_data(channel, event);
 
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "kick-member");
 			switch_event_fire(&event);
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-static int conference_function_energy_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_energy(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		if (data) {
@@ -1702,15 +1834,17 @@
 			switch_mutex_unlock(member->flag_mutex);
 		}
 
-		stream->write_function(stream, "Energy %u=%d\n", id, member->energy_level);
+		if(stream != NULL) {
+			stream->write_function(stream, "Energy %u=%d\n", member->id, member->energy_level);
+		}
 
 		if (data) {
 			if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 				switch_channel_t *channel = switch_core_session_get_channel(member->session);
 				switch_channel_event_set_data(channel, event);
 
-				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "energy-level-member");
 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->energy_level);
 
@@ -1718,18 +1852,17 @@
 			}
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-static int conference_function_volume_in_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_volume_in(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		if (data) {
@@ -1739,14 +1872,16 @@
 			switch_mutex_unlock(member->flag_mutex);
 		}
 
-		stream->write_function(stream, "Volume IN %u=%d\n", id, member->volume_in_level);
+		if(stream != NULL) {
+			stream->write_function(stream, "Volume IN %u=%d\n", member->id, member->volume_in_level);
+		}
 		if (data) {
 			if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 				switch_channel_t *channel = switch_core_session_get_channel(member->session);
 				switch_channel_event_set_data(channel, event);
 
-				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "volume-in-member");
 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Volume-Level", "%u", member->volume_in_level);
 
@@ -1754,18 +1889,17 @@
 			}
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-static int conference_function_volume_out_member(conference_obj_t *conference, conference_member_t *member, int id, switch_stream_handle_t *stream, void *data)
+static int conference_function_api_volume_out(conference_member_t *member, switch_stream_handle_t *stream, void *data)
 {
 	int err = 0;
 
-	if (member != NULL || (member = conference_member_get(conference, id))) {
+	if (member != NULL) {
 		switch_event_t *event;
 
 		if (data) {
@@ -1775,15 +1909,17 @@
 			switch_mutex_unlock(member->flag_mutex);
 		}
 
-		stream->write_function(stream, "Volume OUT %u=%d\n", id, member->volume_out_level);
+		if(stream != NULL) {
+			stream->write_function(stream, "Volume OUT %u=%d\n", member->id, member->volume_out_level);
+		}
 
 		if (data) {
 			if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
 				switch_channel_t *channel = switch_core_session_get_channel(member->session);
 				switch_channel_event_set_data(channel, event);
 
-				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", member->conference->name);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "volume-out-member");
 				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Volume-Level", "%u", member->volume_out_level);
 
@@ -1791,605 +1927,561 @@
 			}
 		}
 	} else {
-		stream->write_function(stream, "Non-Existant ID %u\n", id);
 		err = 1;
 	}
 
 	return err;
 }
 
-/* API Interface Function */
-static switch_status_t conf_function(char *buf, switch_core_session_t *session, switch_stream_handle_t *stream)
+static int conference_function_api_list(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
 {
-	char *lbuf = NULL;
-	switch_status_t status = SWITCH_STATUS_SUCCESS;
-	char *http = NULL;
+//static void conference_list(conference_obj_t *conference, switch_stream_handle_t *stream, char *delim)
+	switch_hash_index_t *hi;
+	void *val;
+	char *d = ";";
 
-	if (session) {
-		return SWITCH_STATUS_FALSE;
-	}
+	if (argv[1]) {
+		if (argv[2] && !strcasecmp(argv[1], "delim")) {
+			d = argv[2];
 
-	if (stream->event) {
-		http = switch_event_get_header(stream->event, "http-host");
+			if (*d == '"') {
+				if (++d) {
+					char *p;
+					if ((p = strchr(d, '"'))) {
+						*p = '\0';
+					}
+				} else {
+					d = ";";
+				}
+			}
+		}
 	}
 
-	if (http) {
-		/* Output must be to a web browser */
-		stream->write_function(stream, "<pre>\n");
+	for (hi = switch_hash_first(globals.conference_pool, globals.conference_hash); hi; hi = switch_hash_next(hi)) {
+		switch_hash_this(hi, NULL, NULL, &val);
+		conference = (conference_obj_t *) val;
+
+		stream->write_function(stream, "Conference %s (%u members)\n", conference->name, conference->count);
+		conference_list(conference, stream, d);
+//		stream->write_function(stream, "\n");
 	}
 
-	if (!buf) {
-		stream->write_function(stream, "%s", conf_api_interface.syntax);
-		return status;
+	return 0;
+}
+
+static int conference_function_api_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	switch_event_t *event;
+
+	if (argc == 3) {
+		if (conference_play_file(conference, argv[2], 0) == SWITCH_STATUS_SUCCESS) {
+			stream->write_function(stream, "(play) Playing file %s\n", argv[2]);
+			if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "play-file");
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
+				switch_event_fire(&event);
+			}
+		} else {
+			stream->write_function(stream, "(play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
+		}
+	} else if (argc == 4) {
+		uint32_t id = atoi(argv[3]);
+		conference_member_t *member;
+
+		if ((member = conference_member_get(conference, id))) {
+			if (conference_member_play_file(member, argv[2], 0) == SWITCH_STATUS_SUCCESS) {
+				stream->write_function(stream, "(play) Playing file %s to member %u\n", argv[2], id);
+				if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "play-file-member");
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
+					switch_event_fire(&event);
+				}
+			} else {
+				stream->write_function(stream, "(play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
+			}
+		} else {
+			stream->write_function(stream, "Member: %u not found.\n", id);
+		}
 	}
 
-	if ((lbuf = strdup(buf))) {
-		conference_obj_t *conference = NULL;
-		int argc;
-		char *argv[25];
-		switch_event_t *event;
+	return 0;
+}
 
-		argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
+static int conference_function_api_say(conference_obj_t *conference, switch_stream_handle_t *stream, char *text)
+{
+	int err = 0;
 
-		/* Figure out what conference */
-		if (argc) {
-			if (!strcasecmp(argv[0], "commands")) {
-				stream->write_function(stream, "%s", conf_api_interface.syntax);
-				goto done;
-			} else if (!strcasecmp(argv[0], "list")) {
-				switch_hash_index_t *hi;
-				void *val;
-				char *d = ";";
+	if (text != NULL && !switch_strlen_zero(text)) {
+		if (conference_say(conference, text, 0) == SWITCH_STATUS_SUCCESS) {
+			switch_event_t *event;
 
-				if (argv[1]) {
-					if (argv[2] && !strcasecmp(argv[1], "delim")) {
-						d = argv[2];
+			stream->write_function(stream, "(say) OK\n");
+			if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "speak-text");
+				switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Text", text);
+				switch_event_fire(&event);
+			}
+		} else {
+			stream->write_function(stream, "(say) Error!");
+		}
+	} else {
+		stream->write_function(stream, "(say) Error! No text.");
+		err = 1;
+	}
 
-						if (*d == '"') {
-							if (++d) {
-								char *p;
-								if ((p = strchr(d, '"'))) {
-									*p = '\0';
-								}
-							} else {
-								d = ";";
-							}
-						}
-					}
-				} 
+	return err;
+}
 
-				for (hi = switch_hash_first(globals.conference_pool, globals.conference_hash); hi; hi = switch_hash_next(hi)) {
-					switch_hash_this(hi, NULL, NULL, &val);
-					conference = (conference_obj_t *) val;
+static int conference_function_api_saymember(conference_obj_t *conference, switch_stream_handle_t *stream, char *text)
+{
+	int err = 0;
 
-					stream->write_function(stream, "Conference %s (%u members)\n", conference->name, conference->count);
-					conference_list(conference, stream, d);
-					stream->write_function(stream, "\n");
+	if (text != NULL && !switch_strlen_zero(text)) {
+		char *name = strdup(text);
+		uint32_t id = 0;
+		conference_member_t *member;
+
+		if (name != NULL && *name) {
+			text = strchr(name, ' ');
+			*name = '\0';
+			id = atoi(name);
+			free(name);
+		} else {
+			err = 1;
+		}
+
+		if (id != 0 && (member = conference_member_get(conference, id))) {
+			if (text && conference_member_say(conference, member, text, 0) == SWITCH_STATUS_SUCCESS) {
+				switch_event_t *event;
+
+				stream->write_function(stream, "(saymember) OK\n");
+				if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "speak-text-member");
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
+					switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Text", text);
+					switch_event_fire(&event);
 				}
-				goto done;
-			} else if (!(conference = (conference_obj_t *) switch_core_hash_find(globals.conference_hash, argv[0]))) {
-				stream->write_function(stream, "No Conference called %s found.\n", argv[0]);
-				goto done;
+			} else {
+				stream->write_function(stream, "(saymember) Error!");
 			}
+		} else {
+			stream->write_function(stream, "(saymember) Unknown Member %u!", id);
+			err = 1;
+		}
+	} else {
+		err = 1;
+	}
 
-			if (argc > 1) { 
-				if (!strcasecmp(argv[1], "lock")) {
-					switch_set_flag_locked(conference, CFLAG_LOCKED);
-					stream->write_function(stream, "OK %s locked\n", argv[0]);
-					if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-						switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-						switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "lock");
-						switch_event_fire(&event);
+	return err;
+}
+
+static int conference_function_api_stop(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	uint8_t current = 0, all = 0;
+	int err = 0;
+
+	if (argc > 2) {
+		current = strcasecmp(argv[2], "current") ? 0 : 1;
+		all = strcasecmp(argv[2], "all") ? 0 : 1;
+	}
+
+	if (current || all) {
+		if (argc == 4) {
+			uint32_t id = atoi(argv[3]);
+			conference_member_t *member;
+
+			if ((member = conference_member_get(conference, id))) {
+				uint32_t stopped = conference_member_stop_file(member, current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
+				stream->write_function(stream, "Stopped %u files.\n", stopped);
+			} else {
+				stream->write_function(stream, "Member: %u not found.\n", id);
+			}
+		} else {
+			uint32_t stopped = conference_stop_file(conference, current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
+			stream->write_function(stream, "Stopped %u files.\n", stopped);
+		}
+	} else {
+		err = 1;
+	}
+
+	return err;
+}
+
+static int conference_function_api_relate(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	int err = 0;
+
+	char *relate_usage = "Usage relate <id> <id> [nospeak|nohear|clear]\n";
+	if (argc > 4) {
+		uint8_t nospeak = 0, nohear = 0, clear = 0;
+		nospeak = strstr(argv[4], "nospeak") ? 1 : 0;
+		nohear = strstr(argv[4], "nohear") ? 1 : 0;
+
+		if (!strcasecmp(argv[4], "clear")) {
+			clear = 1;
+		}
+
+		if (!(clear || nospeak || nohear)) { 
+			stream->write_function(stream, relate_usage);
+			goto done;
+		}
+
+		if (clear) {
+			conference_member_t *member = NULL;
+			uint32_t id = atoi(argv[2]);
+			uint32_t oid = atoi(argv[3]);
+
+			switch_mutex_lock(conference->mutex);
+			switch_mutex_lock(conference->member_mutex);
+			if ((member = conference_member_get(conference, id))) {
+				member_del_relationship(member, oid);
+				stream->write_function(stream, "relationship %u->%u cleared.", id, oid);
+			} else {
+				stream->write_function(stream, "relationship %u->%u not found", id, oid);
+			}
+			switch_mutex_unlock(conference->member_mutex);
+			switch_mutex_unlock(conference->mutex);
+		} else if (nospeak || nohear) {
+			conference_member_t *member = NULL, *other_member = NULL;
+			uint32_t id = atoi(argv[2]);
+			uint32_t oid = atoi(argv[3]);
+
+			switch_mutex_lock(conference->mutex);
+			switch_mutex_lock(conference->member_mutex);
+			if ((member = conference_member_get(conference, id)) && (other_member = conference_member_get(conference, oid))) {
+				conference_relationship_t *rel = NULL;
+				if ((rel = member_get_relationship(member, other_member))) {
+					rel->flags = 0;
+				} else {
+					rel = member_add_relationship(member, oid);
+				}
+
+				if (rel) {
+					switch_set_flag(rel, RFLAG_CAN_SPEAK | RFLAG_CAN_HEAR);
+					if (nospeak) {
+						switch_clear_flag(rel, RFLAG_CAN_SPEAK);
 					}
-					goto done;
-				} else if (!strcasecmp(argv[1], "unlock")) {
-					switch_clear_flag_locked(conference, CFLAG_LOCKED);
-					stream->write_function(stream, "OK %s unlocked\n", argv[0]);
-					if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-						switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-						switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "unlock");
-						switch_event_fire(&event);
+					if (nohear) {
+						switch_clear_flag(rel, RFLAG_CAN_HEAR);
 					}
-					goto done;
-				} else if (!strcasecmp(argv[1], "dial")) {
-					if (argc > 2) {
-						conference_outcall(conference, NULL, argv[2], 60, argv[3], argv[4], argv[5]);
-						stream->write_function(stream, "OK\n");
-						goto done;
-					} else {
-						stream->write_function(stream, "Error!\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "play")) {
-					if (argc == 3) {
-						if (conference_play_file(conference, argv[2], 0) == SWITCH_STATUS_SUCCESS) {
-							stream->write_function(stream, "(play) Playing file %s\n", argv[2]);
-							if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-								switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-								switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "play-file");
-								switch_event_add_header(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
-								switch_event_fire(&event);
-							}
-							goto done;
-						} else {
-							stream->write_function(stream, "(play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
-							goto done;
-						}
-					} else if (argc == 4) {
-						uint32_t id = atoi(argv[3]);
-						conference_member_t *member;
+					stream->write_function(stream, "ok %u->%u set\n", id, oid);
+				} else {
+					stream->write_function(stream, "error!\n");
+				}
 
-						if ((member = conference_member_get(conference, id))) {
-							if (conference_member_play_file(member, argv[2], 0) == SWITCH_STATUS_SUCCESS) {
-								stream->write_function(stream, "(play) Playing file %s to member %u\n", argv[2], id);
-								if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-									switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-									switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
-									switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "play-file-member");
-									switch_event_add_header(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
-									switch_event_fire(&event);
-								}
-								goto done;
-							} else {
-								stream->write_function(stream, "(play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
-								goto done;
-							}
-						} else {
-							stream->write_function(stream, "Member: %u not found.\n", id);
-							goto done;
-						}
-					}
-				} else if (!strcasecmp(argv[1], "say")) {
-					char *tbuf = NULL;
-					char *text;
+			} else {
+				stream->write_function(stream, "relationship %u->%u not found", id, oid);
+			}
+			switch_mutex_unlock(conference->member_mutex);
+			switch_mutex_unlock(conference->mutex);
+		}
 
-					if (argc > 2 && (tbuf = strdup(buf))) {
-						if ((text = strstr(tbuf, "say"))) {
-							text += 4;
-							while(*text == ' ') {
-								text++;
-							}
-							if (!switch_strlen_zero(text) && conference_say(conference, text, 0) == SWITCH_STATUS_SUCCESS) {
-								stream->write_function(stream, "(say) OK\n");
-								if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-									switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-									switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "speak-text");
-									switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Text", text);
-									switch_event_fire(&event);
-								}
-								goto done;
-							} else {
-								stream->write_function(stream, "(say) Error!");
-								goto done;
-							}
-						}
 
-						free(tbuf);
-					} else {
-						stream->write_function(stream, "(say) Error! No text.");
-					}
-				} else if (!strcasecmp(argv[1], "saymember")) {
-					char *tbuf = NULL, *text, *name;
-					uint32_t id;
-					conference_member_t *member;
+	} else {
+		stream->write_function(stream, relate_usage);
+	}
 
-					if (argc > 3) {
-						id = atoi(argv[3]);
-					} else {
-						stream->write_function(stream, "(saymember) Syntax Error!");
-						goto done;
-					}
+done:
+	return err;
+}
 
-					if ((tbuf = strdup(buf))) {
-						if ((name = strstr(tbuf, "saymember "))) {
-							name += 10;
+static int conference_function_api_lock(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	switch_event_t *event;
 
-							if (*name) {
-								text = strchr(name, ' ');
-								id = atoi(name);
-							} else {
-								stream->write_function(stream, "(saymember) Syntax Error!");
-								goto done;
-							}
+	switch_set_flag_locked(conference, CFLAG_LOCKED);
+	stream->write_function(stream, "OK %s locked\n", argv[0]);
+	if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
+		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "lock");
+		switch_event_fire(&event);
+	}
 
+	return 0;
+}
 
+static int conference_function_api_unlock(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	switch_event_t *event;
 
-							if ((member = conference_member_get(conference, id))) {
-								if (text && conference_member_say(conference, member, text, 0) == SWITCH_STATUS_SUCCESS) {
-									stream->write_function(stream, "(saymember) OK\n");
-									if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-										switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
-										switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "speak-text-member");
-										switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", id);
-										switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Text", text);
-										switch_event_fire(&event);
-									}
-								} else {
-									stream->write_function(stream, "(saymember) Error!");
-								}
-							} else {
-								stream->write_function(stream, "(saymember) Unknown Member %u!", id);
-							}
-						} else {
-							stream->write_function(stream, "(saymember) Syntax Error!");
-						}
+	switch_clear_flag_locked(conference, CFLAG_LOCKED);
+	stream->write_function(stream, "OK %s unlocked\n", argv[0]);
+	if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Conference-Name", conference->name);
+		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "unlock");
+		switch_event_fire(&event);
+	}
 
-						free(tbuf);
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "stop")) {
-					uint8_t current = 0, all = 0;
+	return 0;
+}
 
-					if (argc > 2) {
-						current = strcasecmp(argv[2], "current") ? 0 : 1;
-						all = strcasecmp(argv[2], "all") ? 0 : 1;
-					}
+static int conference_function_api_dial(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	int err = 0;
 
-					if (current || all) {
-						if (argc == 4) {
-							uint32_t id = atoi(argv[3]);
-							conference_member_t *member;
-							if ((member = conference_member_get(conference, id))) {
-								uint32_t stopped = conference_member_stop_file(member, current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
-								stream->write_function(stream, "Stopped %u files.\n", stopped);
-							} else {
-								stream->write_function(stream, "Member: %u not found.\n", id);
-								goto done;
-							}
-						} else {
-							uint32_t stopped = conference_stop_file(conference, current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
-							stream->write_function(stream, "Stopped %u files.\n", stopped);
-						}
-					} else {
-						stream->write_function(stream, "Usage stop [current/all]\n");
-						goto done;
-					}
+	if (argc > 2) {
+		conference_outcall(conference, NULL, argv[2], 60, argv[3], argv[4], argv[5]);
+		stream->write_function(stream, "OK\n");
+	} else {
+		err = 1;
+	}
 
-				} else if (!strcasecmp(argv[1], "energy")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+	return err;
+}
 
-						if (!all) {
-							conference_function_energy_member(conference, NULL, id, stream, argv[3]);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_energy_member, argv[3]); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage energy <id|all> [<newval>]\n");
-						goto done;
-					}
+static int conference_function_api_transfer(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	int err = 0;
 
-				} else if (!strcasecmp(argv[1], "volume_in")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+	if (argc > 3) {
+		conference_member_t *member = NULL;
+		uint32_t id = atoi(argv[2]);
+		conference_obj_t *new_conference = NULL;
+		switch_channel_t *channel;
+		switch_event_t *event;
+		char *profile_name;
+		switch_xml_t cxml = NULL, cfg = NULL, profile = NULL, profiles = NULL;
 
-						if (!all) {
-							conference_function_volume_in_member(conference, NULL, id, stream, argv[3]);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_volume_in_member, argv[3]); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage volume_in <[id|all]> [<newval>]\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "volume_out")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+		if (!(member = conference_member_get(conference, id))) {								
+			stream->write_function(stream, "No Member %u in conference %s.\n", id, conference->name);
+			goto done;
+		}
 
-						if (!all) {
-							conference_function_volume_out_member(conference, NULL, id, stream, argv[3]);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_volume_out_member, argv[3]); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage volume_out <[id|all> [<newval>]\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "mute")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+		channel = switch_core_session_get_channel(member->session);
 
-						if (!all) {
-							conference_function_mute_member(conference, NULL, id, stream, NULL);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_mute_member, NULL); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage mute <[id|all]>\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "unmute")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+		if (!(new_conference = (conference_obj_t *) switch_core_hash_find(globals.conference_hash, argv[3]))) {
+			switch_memory_pool_t *pool;
+			char *conf_name;
 
-						if (!all) {
-							conference_function_unmute_member(conference, NULL, id, stream, NULL);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_unmute_member, NULL); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage unmute <[id|all]>\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "deaf")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+			// Setup a memory pool to use.
+			if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
+				goto done;
+			}
 
-						if (!all) {
-							conference_function_deaf_member(conference, NULL, id, stream, NULL);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_deaf_member, NULL); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage deaf <[id|all]>\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "undeaf")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+			conf_name = switch_core_strdup(pool, argv[3]);
 
-						if (!all) {
-							conference_function_undeaf_member(conference, NULL, id, stream, NULL);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_undeaf_member, NULL); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage undeaf <[id|all]>\n");
-						goto done;
-					}
+			if ((profile_name = strchr(conf_name, '@'))) {
+				*profile_name++ = '\0';
 
-				} else if (!strcasecmp(argv[1], "record")) {
-					if (argc > 2) {
-						launch_conference_record_thread(conference, argv[2]);
-						goto done;
-					} else {
-						stream->write_function(stream, "usage record <filename>\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "norecord")) {
-					if (argc > 2) {
-						int all = (strcasecmp(argv[2], "all") == 0 );
+				// Open the config from the xml registry 
+				if (!(cxml = switch_xml_open_cfg(global_cf_name, &cfg, NULL))) {
+					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", global_cf_name);
+					goto done;
+				}
 
-						if(!conference_record_stop(conference, all ? NULL : argv[2]) && !all) {
-							stream->write_function(stream, "non-existant recording '%s'\n", argv[2]);
-						}
-						goto done;
-					} else {
-						stream->write_function(stream, "usage norecord <[filename | all]>\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "kick")) {
-					if (argc > 2) {
-						uint32_t id = atoi(argv[2]);
-						int all = ( id == 0 && strcasecmp(argv[2], "all") == 0 );
+				if ((profiles = switch_xml_child(cfg, "profiles"))) {
+					profile = switch_xml_find_child(profiles, "profile", "name", profile_name);
+				}
+			} 
 
-						if (!all) {
-							conference_function_kick_member(conference, NULL, id, stream, NULL);
-							goto done;
-						} else {
-							conference_member_itterator(conference, stream, &conference_function_kick_member, NULL); 
-							goto done;
-						}
-					} else {
-						stream->write_function(stream, "usage kick <[id|all]>\n");
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "transfer")) {
-					char *transfer_usage = "Usage transfer <id> <confname>\n";
-					if (argc > 3) {
-						conference_member_t *member = NULL;
-						uint32_t id = atoi(argv[2]);
-						conference_obj_t *new_conference = NULL;
-						switch_channel_t *channel;
-						switch_event_t *event;
-						char *profile_name;
-						switch_xml_t cxml = NULL, cfg = NULL, profile = NULL, profiles = NULL;
 
-						if (!(member = conference_member_get(conference, id))) {								
-							stream->write_function(stream, "No Member %u in conference %s.\n", id, conference->name);
-							goto done;
-						}
+			// Release the config registry handle 
+			if (cxml) {
+				switch_xml_free(cxml);
+				cxml = NULL;
+			}
 
-						channel = switch_core_session_get_channel(member->session);
+			// Create the conference object.
+			new_conference = conference_new(conf_name, profile, pool);
 
-						if (!(new_conference = (conference_obj_t *) switch_core_hash_find(globals.conference_hash, argv[3]))) {
-							switch_memory_pool_t *pool;
-							char *conf_name;
 
-							/* Setup a memory pool to use. */
-							if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
-								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Pool Failure\n");
-								goto done;
-							}
+			if (!new_conference) {
+				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
+				goto done;
+			}
 
-							conf_name = switch_core_strdup(pool, argv[3]);
+			// Set the minimum number of members (once you go above it you cannot go below it)
+			new_conference->min = 1;
 
-							if ((profile_name = strchr(conf_name, '@'))) {
-								*profile_name++ = '\0';
+			// Indicate the conference is dynamic
+			switch_set_flag_locked(new_conference, CFLAG_DYNAMIC);
 
-								/* Open the config from the xml registry */
-								if (!(cxml = switch_xml_open_cfg(global_cf_name, &cfg, NULL))) {
-									switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", global_cf_name);
-									goto done;
-								}
+			// Start the conference thread for this conference
+			launch_conference_thread(new_conference);
+		}
 
-								if ((profiles = switch_xml_child(cfg, "profiles"))) {
-									profile = switch_xml_find_child(profiles, "profile", "name", profile_name);
-								}
-							} 
+		conference_del_member(member->last_conference, member);
+		conference_add_member(new_conference, member);
+		stream->write_function(stream, "OK Member %u sent to conference %s.\n", id, argv[3]);
 
+		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
+			switch_channel_event_set_data(channel, event);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-Conference-Name", conference->name);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-Conference-Name", argv[3]);
+			switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "transfer");
+			switch_event_fire(&event);
+		}
 
-							/* Release the config registry handle */
-							if (cxml) {
-								switch_xml_free(cxml);
-								cxml = NULL;
-							}
+	} else {
+		err = 1;
+	}
+done:
+	return err;
+}
 
-							/* Create the conference object. */
-							new_conference = conference_new(conf_name, profile, pool);
+static int conference_function_api_record(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	if (argc > 2) {
+		launch_conference_record_thread(conference, argv[2]);
+	} else {
+		stream->write_function(stream, "usage record <filename>\n");
+	}
 
+	return 0;
+}
 
-							if (!new_conference) {
-								switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
-								goto done;
-							}
+static int conference_function_api_norecord(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char**argv)
+{
+	if (argc > 2) {
+		int all = (strcasecmp(argv[2], "all") == 0 );
 
-							/* Set the minimum number of members (once you go above it you cannot go below it) */
-							new_conference->min = 1;
+		if(!conference_record_stop(conference, all ? NULL : argv[2]) && !all) {
+			stream->write_function(stream, "non-existant recording '%s'\n", argv[2]);
+		}
+	} else {
+		stream->write_function(stream, "usage norecord <[filename | all]>\n");
+	}
 
-							/* Indicate the conference is dynamic */
-							switch_set_flag_locked(new_conference, CFLAG_DYNAMIC);
+	return 0;
+}
 
-							/* Start the conference thread for this conference */
-							launch_conference_thread(new_conference);
-						}
+/* API Interface Function sub-commands */
+static api_command_t conf_function_api_commands[] = {
+	{"list",	&conference_function_api_list,		0, "<confname> list [delim <string>]"},
+	{"energy",	&conference_function_api_energy,	1, "<confname> energy <member_id|all> [<newval>]"},
+	{"volume in",	&conference_function_api_volume_in,	1, "<confname> volume_in <member_id|all> [<newval>]"},
+	{"volume out",	&conference_function_api_volume_out,	1, "<confname> volume_out <member_id|all> [<newval>]"},
+	{"play",	&conference_function_api_play,		0, "<confname> play <file_path> [<member_id>]"},
+	{"say",		&conference_function_api_say,		2, "<confname> say <text>"},
+	{"saymember",	 &conference_function_api_saymember,	2, "<confname> saymember <member_id><text>"},
+	{"stop",	 &conference_function_api_stop,		1, "<confname> stop <[current|all]> [<member_id>]"},
+	{"kick",	 &conference_function_api_kick,		1, "<confname> kick <[member_id|all]>"},
+	{"mute",	 &conference_function_api_mute,		1, "<confname> mute <[member_id|all]>"},
+	{"unmute",	 &conference_function_api_unmute,	1, "<confname> unmute <[member_id|all]>"},
+	{"deaf",	 &conference_function_api_deaf,		1, "<confname> deaf <[member_id|all]>"},
+	{"undeaf",	 &conference_function_api_undeaf,	1, "<confname> undef <[member_id|all]>"},
+	{"relate",	 &conference_function_api_relate,	0, "<confname> relate <member_id> <other_member_id> [nospeak|nohear|clear]"},
+	{"lock",	 &conference_function_api_lock,		0, "<confname> lock"},
+	{"unlock",	 &conference_function_api_unlock,	0, "<confname> unlock"},
+	{"dial",	 &conference_function_api_dial,		0, "<confname> dial <endpoint_module_name>/<destination>"},
+	{"transfer",	 &conference_function_api_transfer,	0, "<confname> transfer <member_id> <conference_name>"},
+	{"record",	 &conference_function_api_record,	0, "<confname> record <filename>"},
+	{"norecord",	 &conference_function_api_norecord,	0, "<confname> norecord <[filename|all]>"},
+};
 
-						conference_del_member(member->last_conference, member);
-						conference_add_member(new_conference, member);
-						stream->write_function(stream, "OK Member %u sent to conference %s.\n", id, argv[3]);
+#define CONFFUNCAPISIZE (sizeof(conf_function_api_commands)/sizeof(conf_function_api_commands[0]))
 
-						if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
-							switch_channel_event_set_data(channel, event);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Member-ID", "%u", member->id);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Old-Conference-Name", conference->name);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "New-Conference-Name", argv[3]);
-							switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Action", "transfer");
-							switch_event_fire(&event);
-						}
+/* API Interface Function */
+static switch_status_t conf_function(char *buf, switch_core_session_t *session, switch_stream_handle_t *stream)
+{
+	char *lbuf = NULL;
+	switch_status_t status = SWITCH_STATUS_SUCCESS;
+	char *http = NULL;
 
-					} else {
-						stream->write_function(stream, transfer_usage);
-						goto done;
-					}
-				} else if (!strcasecmp(argv[1], "relate")) {
-					char *relate_usage = "Usage relate <id> <id> [nospeak|nohear|clear]\n";
-					if (argc > 4) {
-						uint8_t nospeak = 0, nohear = 0, clear = 0;
-						nospeak = strstr(argv[4], "nospeak") ? 1 : 0;
-						nohear = strstr(argv[4], "nohear") ? 1 : 0;
+	if (session) {
+		return SWITCH_STATUS_FALSE;
+	}
 
-						if (!strcasecmp(argv[4], "clear")) {
-							clear = 1;
-						}
+	if (stream->event) {
+		http = switch_event_get_header(stream->event, "http-host");
+	}
 
-						if (!(clear || nospeak || nohear)) { 
-							stream->write_function(stream, relate_usage);
-							goto done;
-						}
+	if (http) {
+		/* Output must be to a web browser */
+		stream->write_function(stream, "<pre>\n");
+	}
 
-						if (clear) {
-							conference_member_t *member = NULL;
-							uint32_t id = atoi(argv[2]);
-							uint32_t oid = atoi(argv[3]);
+	if (buf != NULL && (lbuf = strdup(buf))) {
+		int argc;
+		char *argv[25];
 
-							switch_mutex_lock(conference->mutex);
-							switch_mutex_lock(conference->member_mutex);
-							if ((member = conference_member_get(conference, id))) {
-								member_del_relationship(member, oid);
-								stream->write_function(stream, "relationship %u->%u cleared.", id, oid);
-							} else {
-								stream->write_function(stream, "relationship %u->%u not found", id, oid);
-							}
-							switch_mutex_unlock(conference->member_mutex);
-							switch_mutex_unlock(conference->mutex);
-						} else if (nospeak || nohear) {
-							conference_member_t *member = NULL, *other_member = NULL;
-							uint32_t id = atoi(argv[2]);
-							uint32_t oid = atoi(argv[3]);
+		memset(argv,0,sizeof(argv));
+		argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
 
-							switch_mutex_lock(conference->mutex);
-							switch_mutex_lock(conference->member_mutex);
-							if ((member = conference_member_get(conference, id)) && (other_member = conference_member_get(conference, oid))) {
-								conference_relationship_t *rel = NULL;
-								if ((rel = member_get_relationship(member, other_member))) {
-									rel->flags = 0;
-								} else {
-									rel = member_add_relationship(member, oid);
-								}
+		// find a command to execute
+		if (argc) {
+			int i,found=0;
+			uint32_t cnum = atoi(argv[0]);
+			conference_obj_t *conference = (cnum != 0 ? (conference_obj_t *) switch_core_hash_find(globals.conference_hash, argv[0]) : NULL);
 
-								if (rel) {
-									switch_set_flag(rel, RFLAG_CAN_SPEAK | RFLAG_CAN_HEAR);
-									if (nospeak) {
-										switch_clear_flag(rel, RFLAG_CAN_SPEAK);
-									}
-									if (nohear) {
-										switch_clear_flag(rel, RFLAG_CAN_HEAR);
-									}
-									stream->write_function(stream, "ok %u->%u set\n", id, oid);
-								} else {
-									stream->write_function(stream, "error!\n");
-								}
+			if(cnum == 0 || (cnum != 0 && conference != NULL)) {
+				int argn = (cnum != 0 ? 1 : 0);
 
-							} else {
-								stream->write_function(stream, "relationship %u->%u not found", id, oid);
-							}
-							switch_mutex_unlock(conference->member_mutex);
-							switch_mutex_unlock(conference->mutex);
-						}
+				for (i=0; i<CONFFUNCAPISIZE && !found; i++) {
+					if (strcasecmp(argv[argn],conf_function_api_commands[i].pname) == 0) {
+						found = 1;
+						switch(conf_function_api_commands[i].fntype) {
+							case 0:	// commands that we've broken the command line into arguments for
+								{	conf_api_args_cmd_t pfn = (conf_api_args_cmd_t)conf_function_api_commands[i].pfnapicmd;
 
+									if (pfn(conference, stream, argc, &argv[0]) != 0) {
+										// command returned error, so show syntax usage
+										stream->write_function(stream,conf_function_api_commands[i].psyntax);
+										}
+								}
+								break;
+							case 1: // member specific command that can be itteratted
+								{
+									uint32_t id = atoi(argv[argn+1]);
+									int all = ( id == 0 && strcasecmp(argv[argn+1], "all") == 0 );
 
-					} else {
-						stream->write_function(stream, relate_usage);
-					}
-				} else if (!strcasecmp(argv[1], "list")) {
-					char *d = ";";
+									if(all) {
+										 conference_member_itterator(conference, stream, conf_function_api_commands[i].pfnapicmd, argv[argn+2]);
+									} else {
+										conf_api_member_cmd_t pfn = (conf_api_member_cmd_t)conf_function_api_commands[i].pfnapicmd;
+										conference_member_t *member = conference_member_get(conference, id);
 
-					if (argv[2]) {
-						if (argv[3] && !strcasecmp(argv[2], "delim")) {
-							d = argv[3];
+										if (member != NULL) {
+											pfn(conference_member_get(conference, id), stream, argv[argn+2]);
+										} else {
+											if (id == 0) {
+												stream->write_function(stream,conf_function_api_commands[i].psyntax);
+											} else {
+												stream->write_function(stream, "Non-Existant ID %u\n", id);
+											}
+										}
+									}
+								}
+								break;
+							case 2:	// commands that deals with all text after command
+								{	conf_api_text_cmd_t pfn = (conf_api_text_cmd_t)conf_function_api_commands[i].pfnapicmd;
+									char *pstr = lbuf+strlen(conf_function_api_commands[i].pname)+1;
 
-							if (*d == '"') {
-								if (++d) {
-									char *p;
-									if ((p = strchr(d, '"'))) {
-										*p = '\0';
+									// advance past all leading white space after command
+									while(*pstr == ' ' || *pstr == '\t') {
+										pstr++;
 									}
-								} else {
-									d = ";";
+
+									if (pfn(conference, stream, pstr) != 0) {
+										// command returned error, so show syntax usage
+										stream->write_function(stream,conf_function_api_commands[i].psyntax);
+										}
 								}
-							}
+								break;
 						}
 					}
-					conference_list(conference, stream, d);
-				} else {
-					stream->write_function(stream, "Command: %s not found.\n", argv[1]);
-					goto done;
 				}
-
+				if(!found) {
+					stream->write_function(stream, "Confernece command '%s' not found.\nTry 'help conference'\n", argv[argn]);
+//					stream->write_function(stream,(char *)conf_api_interface.syntax);
+				}
 			} else {
-				stream->write_function(stream, "Command not specified.\n");
-				goto done;
+				stream->write_function(stream,"No Conference called %s found.\n",argv[0]);
 			}
+
 		} else {
-			stream->write_function(stream, "USAGE: %s\n", conf_api_interface.syntax);
+			stream->write_function(stream, "Command not specified.\nTry 'help conference'\n");
+//			stream->write_function(stream,(char *)conf_api_interface.syntax);
 		}
-	} else {
-		stream->write_function(stream, "Memory Error!\n");
 	}
 
-done:
-
 	if (lbuf) {
 		free(lbuf);
 	}
@@ -3072,31 +3164,10 @@
 };
 
 static switch_api_interface_t conf_api_interface = {
-	/*.interface_name */ "conference",
-	/*.desc */ "Conference",
-	/*.function */ conf_function,
-	/*.syntax */ 
-		"list [delim <string>]\n"
-		"\t<confname> list [delim <string>]\n"
-		"\t<confname> energy <member_id|all> [<newval>]\n"
-		"\t<confname> volume_in <member_id|all> [<newval>]\n"
-		"\t<confname> volume_out <member_id|all> [<newval>]\n"
-		"\t<confname> play <file_path> [<member_id>]\n"
-		"\t<confname> say <text>\n"
-		"\t<confname> saymember <member_id><text>\n"
-		"\t<confname> stop <[current|all]> [<member_id>]\n"
-		"\t<confname> kick <[member_id|all]>\n"
-		"\t<confname> mute <[member_id|all]>\n"
-		"\t<confname> unmute <[member_id|all]>\n"
-		"\t<confname> deaf <[member_id|all]>\n"
-		"\t<confname> undef <[member_id|all]>\n"
-		"\t<confname> relate <member_id> <other_member_id> [nospeak|nohear]\n"
-		"\t<confname> lock\n"
-		"\t<confname> unlock\n"
-		"\t<confname> dial <endpoint_module_name>/<destination>\n"
-		"\t<confname> transfer <member_id> <conference_name>\n"
-		"\t<confname> record <filename>\n"
-		"\t<confname> norecord <[filename|all]>\n",
+	/*.interface_name */	"conference",
+	/*.desc */		"Conference module commands",
+	/*.function */		conf_function,
+	/*.syntax */		// see switch_module_load
 	/*.next */ 
 };
 
@@ -3193,6 +3264,7 @@
 	char *energy_level = NULL;
 	char *caller_id_name = NULL;
 	char *caller_id_number = NULL;
+	char *caller_dtmf_control = "0*987321654#";
 	uint32_t rate = 8000, interval = 20;
 	switch_status_t status;
 
@@ -3260,6 +3332,8 @@
 			caller_id_name = val;
 		} else if (!strcasecmp(var, "caller-id-number")) {
 			caller_id_number = val;
+		} else if (!strcasecmp(var, "caller-dtmf-control")) {
+			caller_dtmf_control = val;
 		}
 	}
 
@@ -3321,6 +3395,23 @@
 	conference->caller_id_name = switch_core_strdup(conference->pool, caller_id_name);
 	conference->caller_id_number = switch_core_strdup(conference->pool, caller_id_number);
 
+	// The position of the dialed digit in the control string
+	// determines the operation that is being requested
+	// To disable the function for the user, make the control
+	// string digit something that can't be dialed... ie. a space
+	// The control string should have at least 12 characters in it
+	if(strlen(caller_dtmf_control) < 12) {
+		char dbuf[13];
+
+		memset(dbuf,' ',sizeof(dbuf));
+		dbuf[12] = '\0';
+		memcpy(dbuf,caller_dtmf_control,MIN(sizeof(dbuf)-1,strlen(caller_dtmf_control)));
+
+		conference->caller_dtmf_control = switch_core_strdup(conference->pool, dbuf);
+	} else {
+		conference->caller_dtmf_control = switch_core_strdup(conference->pool, caller_dtmf_control);
+	}
+
 	if (!switch_strlen_zero(enter_sound)) {
 		conference->enter_sound = switch_core_strdup(conference->pool, enter_sound);
 	}
@@ -3395,10 +3486,33 @@
 /* Called by FreeSWITCH when the module loads */
 SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename)
 {
+	int i;
+	size_t nl,ol=0;
+	char *p = NULL;
+
 	switch_status_t status = SWITCH_STATUS_SUCCESS;
 
 	memset(&globals, 0, sizeof(globals));
 
+	// build api interface help ".syntax" field string
+	p=strdup("list\n");
+	for (i=0; i<CONFFUNCAPISIZE; i++) {
+		nl=strlen(conf_function_api_commands[i].psyntax)+4;
+		if(p != NULL)
+			ol = strlen(p);
+		p = realloc(p,ol+nl);
+		if(p != NULL) {
+			strcat(p,"\t\t");
+			strcat(p,conf_function_api_commands[i].psyntax);
+			if(i<CONFFUNCAPISIZE-1)
+				strcat(p,"\n");
+		}
+		
+	}
+	// install api interface help ".syntax" field string
+	if(p != NULL)
+		conf_api_interface.syntax = p;
+
 	/* Connect my internal structure to the blank pointer passed to me */
 	*module_interface = &conference_module_interface;
 
@@ -3434,6 +3548,10 @@
 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Waiting for %d threads\n", globals.threads);
 			switch_yield(100000);
 		}
+
+		// free api interface help ".syntax" field string
+		if (conf_api_interface.syntax != NULL)
+			free((char *)conf_api_interface.syntax);
 	}
 
 	return SWITCH_STATUS_SUCCESS;



More information about the Freeswitch-svn mailing list