1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Anthony Minessale II <anthm@freeswitch.org>
27  * Neal Horman <neal at wanlink dot com>
28  * Bret McDanel <trixter at 0xdecafbad dot com>
29  * Dale Thatcher <freeswitch at dalethatcher dot com>
30  * Chris Danielson <chris at maxpowersoft dot com>
31  * Rupa Schomaker <rupa@rupa.com>
32  * David Weekly <david@weekly.org>
33  * Joao Mesquita <jmesquita@gmail.com>
34  * Raymond Chandler <intralanman@freeswitch.org>
35  * Seven Du <dujinfang@gmail.com>
36  * Emmanuel Schmidbauer <e.schmidbauer@gmail.com>
37  * William King <william.king@quentustech.com>
38  *
39  * mod_conference.c -- Software Conference Bridge
40  *
41  */
42 #include <mod_conference.h>
43 
44 
45 api_command_t conference_api_sub_commands[] = {
46 	{"canvas-auto-clear", (void_fn_t) & conference_api_sub_canvas_auto_clear, CONF_API_SUB_ARGS_SPLIT, "canvas-auto-clear", "<canvas_id> <true|false>"},
47 	{"count", (void_fn_t) & conference_api_sub_count, CONF_API_SUB_ARGS_SPLIT, "count", ""},
48 	{"list", (void_fn_t) & conference_api_sub_list, CONF_API_SUB_ARGS_SPLIT, "list", "[delim <string>]|[count]"},
49 	{"xml_list", (void_fn_t) & conference_api_sub_xml_list, CONF_API_SUB_ARGS_SPLIT, "xml_list", ""},
50 	{"json_list", (void_fn_t) & conference_api_sub_json_list, CONF_API_SUB_ARGS_SPLIT, "json_list", "[compact]"},
51 	{"energy", (void_fn_t) & conference_api_sub_energy, CONF_API_SUB_MEMBER_TARGET, "energy", "<member_id|all|last|non_moderator> [<newval>]"},
52 	{"auto-energy", (void_fn_t) & conference_api_sub_auto_energy, CONF_API_SUB_MEMBER_TARGET, "auto-energy", "<member_id|all|last|non_moderator> [<newval>]"},
53 	{"max-energy", (void_fn_t) & conference_api_sub_max_energy, CONF_API_SUB_MEMBER_TARGET, "max-energy", "<member_id|all|last|non_moderator> [<newval>]"},
54 	{"agc", (void_fn_t) & conference_api_sub_agc, CONF_API_SUB_MEMBER_TARGET, "agc", "<member_id|all|last|non_moderator> [<newval>]"},
55 	{"vid-canvas", (void_fn_t) & conference_api_sub_canvas, CONF_API_SUB_MEMBER_TARGET, "vid-canvas", "<member_id|all|last|non_moderator> [<newval>]"},
56 	{"vid-watching-canvas", (void_fn_t) & conference_api_sub_watching_canvas, CONF_API_SUB_MEMBER_TARGET, "vid-watching-canvas", "<member_id|all|last|non_moderator> [<newval>]"},
57 	{"vid-layer", (void_fn_t) & conference_api_sub_layer, CONF_API_SUB_MEMBER_TARGET, "vid-layer", "<member_id|all|last|non_moderator> [<newval>]"},
58 	{"volume_in", (void_fn_t) & conference_api_sub_volume_in, CONF_API_SUB_MEMBER_TARGET, "volume_in", "<member_id|all|last|non_moderator> [<newval>]"},
59 	{"volume_out", (void_fn_t) & conference_api_sub_volume_out, CONF_API_SUB_MEMBER_TARGET, "volume_out", "<member_id|all|last|non_moderator> [<newval>]"},
60 	{"position", (void_fn_t) & conference_api_sub_position, CONF_API_SUB_MEMBER_TARGET, "position", "<member_id> <x>:<y>:<z>"},
61 	{"auto-3d-position", (void_fn_t) & conference_api_sub_auto_position, CONF_API_SUB_ARGS_SPLIT, "auto-3d-position", "[on|off]"},
62 	{"play", (void_fn_t) & conference_api_sub_play, CONF_API_SUB_ARGS_SPLIT, "play", "<file_path> [async|<member_id> [nomux]]"},
63 	{"moh", (void_fn_t) & conference_api_sub_moh, CONF_API_SUB_ARGS_SPLIT, "moh", "<file_path>|toggle|[on|off]"},
64 	{"pause_play", (void_fn_t) & conference_api_sub_pause_play, CONF_API_SUB_ARGS_SPLIT, "pause", "[<member_id>]"},
65 	{"play_status", (void_fn_t) & conference_api_sub_play_status, CONF_API_SUB_ARGS_SPLIT, "play_status", "[<member_id>]"},
66 	{"file_seek", (void_fn_t) & conference_api_sub_file_seek, CONF_API_SUB_ARGS_SPLIT, "file_seek", "[+-]<val> [<member_id>]"},
67 	{"say", (void_fn_t) & conference_api_sub_say, CONF_API_SUB_ARGS_AS_ONE, "say", "<text>"},
68 	{"saymember", (void_fn_t) & conference_api_sub_saymember, CONF_API_SUB_ARGS_AS_ONE, "saymember", "<member_id> <text>"},
69 	{"cam", (void_fn_t) & conference_api_sub_cam, CONF_API_SUB_ARGS_SPLIT, "cam", ""},
70 	{"stop", (void_fn_t) & conference_api_sub_stop, CONF_API_SUB_ARGS_SPLIT, "stop", "<[current|all|async|last]> [<member_id>]"},
71 	{"dtmf", (void_fn_t) & conference_api_sub_dtmf, CONF_API_SUB_MEMBER_TARGET, "dtmf", "<[member_id|all|last|non_moderator]> <digits>"},
72 	{"kick", (void_fn_t) & conference_api_sub_kick, CONF_API_SUB_MEMBER_TARGET, "kick", "<[member_id|all|last|non_moderator]> [<optional sound file>]"},
73 	{"vid-flip", (void_fn_t) & conference_api_sub_vid_flip, CONF_API_SUB_MEMBER_TARGET, "vid-flip", "<[member_id|all|last|non_moderator]>"},
74 	{"vid-border", (void_fn_t) & conference_api_sub_vid_border, CONF_API_SUB_MEMBER_TARGET, "vid-border", "<[member_id|all|last|non_moderator]>"},
75 	{"hup", (void_fn_t) & conference_api_sub_hup, CONF_API_SUB_MEMBER_TARGET, "hup", "<[member_id|all|last|non_moderator]>"},
76 	{"hold", (void_fn_t) & conference_api_sub_hold, CONF_API_SUB_MEMBER_TARGET, "hold", "<[member_id|all]|last|non_moderator> [file]"},
77 	{"unhold", (void_fn_t) & conference_api_sub_unhold, CONF_API_SUB_MEMBER_TARGET, "unhold", "<[member_id|all]|last|non_moderator>"},
78 	{"mute", (void_fn_t) & conference_api_sub_mute, CONF_API_SUB_MEMBER_TARGET, "mute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
79 	{"tmute", (void_fn_t) & conference_api_sub_tmute, CONF_API_SUB_MEMBER_TARGET, "tmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
80 	{"unmute", (void_fn_t) & conference_api_sub_unmute, CONF_API_SUB_MEMBER_TARGET, "unmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
81 	{"vmute", (void_fn_t) & conference_api_sub_vmute, CONF_API_SUB_MEMBER_TARGET, "vmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
82 	{"tvmute", (void_fn_t) & conference_api_sub_tvmute, CONF_API_SUB_MEMBER_TARGET, "tvmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
83 	{"vmute-snap", (void_fn_t) & conference_api_sub_conference_video_vmute_snap, CONF_API_SUB_MEMBER_TARGET, "vmute-snap", "<[member_id|all]|last|non_moderator>"},
84 	{"unvmute", (void_fn_t) & conference_api_sub_unvmute, CONF_API_SUB_MEMBER_TARGET, "unvmute", "<[member_id|all]|last|non_moderator> [<quiet>]"},
85 	{"vblind", (void_fn_t) & conference_api_sub_vblind, CONF_API_SUB_MEMBER_TARGET, "vblind", "<[member_id|all]|last|non_moderator> [<quiet>]"},
86 	{"tvblind", (void_fn_t) & conference_api_sub_tvblind, CONF_API_SUB_MEMBER_TARGET, "tvblind", "<[member_id|all]|last|non_moderator> [<quiet>]"},
87 	{"unvblind", (void_fn_t) & conference_api_sub_unvblind, CONF_API_SUB_MEMBER_TARGET, "unvblind", "<[member_id|all]|last|non_moderator> [<quiet>]"},
88 	{"deaf", (void_fn_t) & conference_api_sub_deaf, CONF_API_SUB_MEMBER_TARGET, "deaf", "<[member_id|all]|last|non_moderator>"},
89 	{"undeaf", (void_fn_t) & conference_api_sub_undeaf, CONF_API_SUB_MEMBER_TARGET, "undeaf", "<[member_id|all]|last|non_moderator>"},
90 	{"vid-filter", (void_fn_t) & conference_api_sub_video_filter, CONF_API_SUB_MEMBER_TARGET, "vid-filter", "<[member_id|all]|last|non_moderator> <string>"},
91 	{"relate", (void_fn_t) & conference_api_sub_relate, CONF_API_SUB_ARGS_SPLIT, "relate", "<member_id>[,<member_id>] <other_member_id>[,<other_member_id>] [nospeak|nohear|clear]"},
92 	{"lock", (void_fn_t) & conference_api_sub_lock, CONF_API_SUB_ARGS_SPLIT, "lock", ""},
93 	{"unlock", (void_fn_t) & conference_api_sub_unlock, CONF_API_SUB_ARGS_SPLIT, "unlock", ""},
94 	{"dial", (void_fn_t) & conference_api_sub_dial, CONF_API_SUB_ARGS_SPLIT, "dial", "<endpoint_module_name>/<destination> <callerid number> <callerid name>"},
95 	{"bgdial", (void_fn_t) & conference_api_sub_bgdial, CONF_API_SUB_ARGS_SPLIT, "bgdial", "<endpoint_module_name>/<destination> <callerid number> <callerid name>"},
96 	{"transfer", (void_fn_t) & conference_api_sub_transfer, CONF_API_SUB_ARGS_SPLIT, "transfer", "<conference_name> <member id> [...<member id>]"},
97 	{"record", (void_fn_t) & conference_api_sub_record, CONF_API_SUB_ARGS_SPLIT, "record", "<filename>"},
98 	{"chkrecord", (void_fn_t) & conference_api_sub_check_record, CONF_API_SUB_ARGS_SPLIT, "chkrecord", "<confname>"},
99 	{"norecord", (void_fn_t) & conference_api_sub_norecord, CONF_API_SUB_ARGS_SPLIT, "norecord", "<[filename|all]>"},
100 	{"pause", (void_fn_t) & conference_api_sub_pauserec, CONF_API_SUB_ARGS_SPLIT, "pause", "<filename>"},
101 	{"resume", (void_fn_t) & conference_api_sub_pauserec, CONF_API_SUB_ARGS_SPLIT, "resume", "<filename>"},
102 	{"recording", (void_fn_t) & conference_api_sub_recording, CONF_API_SUB_ARGS_SPLIT, "recording", "[start|stop|check|pause|resume] [<filename>|all]"},
103 	{"exit_sound", (void_fn_t) & conference_api_sub_exit_sound, CONF_API_SUB_ARGS_SPLIT, "exit_sound", "on|off|none|file <filename>"},
104 	{"enter_sound", (void_fn_t) & conference_api_sub_enter_sound, CONF_API_SUB_ARGS_SPLIT, "enter_sound", "on|off|none|file <filename>"},
105 	{"pin", (void_fn_t) & conference_api_sub_pin, CONF_API_SUB_ARGS_SPLIT, "pin", "<pin#>"},
106 	{"nopin", (void_fn_t) & conference_api_sub_pin, CONF_API_SUB_ARGS_SPLIT, "nopin", ""},
107 	{"get", (void_fn_t) & conference_api_sub_get, CONF_API_SUB_ARGS_SPLIT, "get", "<parameter-name>"},
108 	{"set", (void_fn_t) & conference_api_sub_set, CONF_API_SUB_ARGS_SPLIT, "set", "<max_members|sound_prefix|caller_id_name|caller_id_number|endconference_grace_time> <value>"},
109 	{"file-vol", (void_fn_t) & conference_api_sub_file_vol, CONF_API_SUB_ARGS_SPLIT, "file-vol", "<vol#>"},
110 	{"floor", (void_fn_t) & conference_api_sub_floor, CONF_API_SUB_MEMBER_TARGET, "floor", "<member_id|last>"},
111 	{"vid-floor", (void_fn_t) & conference_api_sub_vid_floor, CONF_API_SUB_MEMBER_TARGET, "vid-floor", "<member_id|last> [force]"},
112 	{"vid-banner", (void_fn_t) & conference_api_sub_vid_banner, CONF_API_SUB_MEMBER_TARGET, "vid-banner", "<member_id|last> <text>"},
113 	{"vid-mute-img", (void_fn_t) & conference_api_sub_vid_mute_img, CONF_API_SUB_MEMBER_TARGET, "vid-mute-img", "<member_id|last> [<path>|clear]"},
114 	{"vid-logo-img", (void_fn_t) & conference_api_sub_vid_logo_img, CONF_API_SUB_MEMBER_TARGET, "vid-logo-img", "<member_id|last> [<path>|clear]"},
115 	{"vid-codec-group", (void_fn_t) & conference_api_sub_vid_codec_group, CONF_API_SUB_MEMBER_TARGET, "vid-codec-group", "<member_id|last> [<group>|clear]"},
116 	{"vid-res-id", (void_fn_t) & conference_api_sub_vid_res_id, CONF_API_SUB_ARGS_SPLIT, "vid-res-id", "<member_id>|all <val>|clear [force]"},
117 	{"vid-role-id", (void_fn_t) & conference_api_sub_vid_role_id, CONF_API_SUB_MEMBER_TARGET, "vid-role-id", "<member_id|last> <val>|clear"},
118 	{"get-uuid", (void_fn_t) & conference_api_sub_get_uuid, CONF_API_SUB_MEMBER_TARGET, "get-uuid", "<member_id|last>"},
119 	{"clear-vid-floor", (void_fn_t) & conference_api_sub_clear_vid_floor, CONF_API_SUB_ARGS_AS_ONE, "clear-vid-floor", ""},
120 	{"vid-layout", (void_fn_t) & conference_api_sub_vid_layout, CONF_API_SUB_ARGS_SPLIT, "vid-layout", "<layout name>|group <group name> [<canvas id>]"},
121 	{"vid-write-png", (void_fn_t) & conference_api_sub_write_png, CONF_API_SUB_ARGS_SPLIT, "vid-write-png", "<path>"},
122 	{"vid-fps", (void_fn_t) & conference_api_sub_vid_fps, CONF_API_SUB_ARGS_SPLIT, "vid-fps", "<fps>"},
123 	{"vid-res", (void_fn_t) & conference_api_sub_vid_res, CONF_API_SUB_ARGS_SPLIT, "vid-res", "<WxH>"},
124 	{"vid-fgimg", (void_fn_t) & conference_api_sub_canvas_fgimg, CONF_API_SUB_ARGS_SPLIT, "vid-fgimg", "<file> | clear [<canvas-id>]"},
125 	{"vid-bgimg", (void_fn_t) & conference_api_sub_canvas_bgimg, CONF_API_SUB_ARGS_SPLIT, "vid-bgimg", "<file> | clear [<canvas-id>]"},
126 	{"vid-bandwidth", (void_fn_t) & conference_api_sub_vid_bandwidth, CONF_API_SUB_ARGS_SPLIT, "vid-bandwidth", "<BW>"},
127 	{"vid-personal", (void_fn_t) & conference_api_sub_vid_personal, CONF_API_SUB_ARGS_SPLIT, "vid-personal", "[on|off]"}
128 };
129 
conference_api_sub_pause_play(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)130 switch_status_t conference_api_sub_pause_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
131 {
132 	if (argc == 2) {
133 		switch_mutex_lock(conference->mutex);
134 		conference_fnode_toggle_pause(conference->fnode, stream);
135 		switch_mutex_unlock(conference->mutex);
136 
137 		return SWITCH_STATUS_SUCCESS;
138 	}
139 
140 	if (argc == 3) {
141 		uint32_t id = atoi(argv[2]);
142 		conference_member_t *member;
143 
144 		if ((member = conference_member_get(conference, id))) {
145 			switch_mutex_lock(member->fnode_mutex);
146 			conference_fnode_toggle_pause(member->fnode, stream);
147 			switch_mutex_unlock(member->fnode_mutex);
148 			switch_thread_rwlock_unlock(member->rwlock);
149 			return SWITCH_STATUS_SUCCESS;
150 		} else {
151 			stream->write_function(stream, "-ERR Member: %u not found.\n", id);
152 		}
153 	}
154 
155 	return SWITCH_STATUS_GENERR;
156 }
157 
conference_api_sub_play_status(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)158 switch_status_t conference_api_sub_play_status(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
159 {
160 	if (argc == 2) {
161 		switch_mutex_lock(conference->mutex);
162 		conference_fnode_check_status(conference->fnode, stream);
163 		switch_mutex_unlock(conference->mutex);
164 
165 		return SWITCH_STATUS_SUCCESS;
166 	}
167 
168 	if (argc == 3) {
169 		uint32_t id = atoi(argv[2]);
170 		conference_member_t *member;
171 
172 		if ((member = conference_member_get(conference, id))) {
173 			switch_mutex_lock(member->fnode_mutex);
174 			conference_fnode_check_status(member->fnode, stream);
175 			switch_mutex_unlock(member->fnode_mutex);
176 			switch_thread_rwlock_unlock(member->rwlock);
177 			return SWITCH_STATUS_SUCCESS;
178 		} else {
179 			stream->write_function(stream, "-ERR Member: %u not found.\n", id);
180 		}
181 	}
182 
183 	return SWITCH_STATUS_GENERR;
184 }
185 
186 /* _In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream */
conference_api_main_real(const char * cmd,switch_core_session_t * session,switch_stream_handle_t * stream)187 switch_status_t conference_api_main_real(const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream)
188 {
189 	char *lbuf = NULL;
190 	switch_status_t status = SWITCH_STATUS_SUCCESS;
191 	char *http = NULL, *type = NULL;
192 	int argc;
193 	char *argv[25] = { 0 };
194 
195 	if (!cmd) {
196 		cmd = "help";
197 	}
198 
199 	if (stream->param_event) {
200 		http = switch_event_get_header(stream->param_event, "http-host");
201 		type = switch_event_get_header(stream->param_event, "content-type");
202 	}
203 
204 	if (http) {
205 		/* Output must be to a web browser */
206 		if (type && !strcasecmp(type, "text/html")) {
207 			stream->write_function(stream, "<pre>\n");
208 		}
209 	}
210 
211 	if (!(lbuf = strdup(cmd))) {
212 		return status;
213 	}
214 
215 	argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
216 
217 	/* try to find a command to execute */
218 	if (argc && argv[0]) {
219 		conference_obj_t *conference = NULL;
220 
221 		if ((conference = conference_find(argv[0], NULL))) {
222 			if (argc >= 2) {
223 				conference_api_dispatch(conference, stream, argc, argv, cmd, 1);
224 			} else {
225 				stream->write_function(stream, "Conference command, not specified.\nTry 'help'\n");
226 			}
227 			switch_thread_rwlock_unlock(conference->rwlock);
228 
229 		} else if (argv[0]) {
230 			/* special case the list command, because it doesn't require a conference argument */
231 			if (strcasecmp(argv[0], "list") == 0) {
232 				conference_api_sub_list(NULL, stream, argc, argv);
233 			} else if (strcasecmp(argv[0], "count") == 0) {
234 				conference_api_sub_count(NULL, stream, argc, argv);
235 			} else if (strcasecmp(argv[0], "xml_list") == 0) {
236 				conference_api_sub_xml_list(NULL, stream, argc, argv);
237 			} else if (strcasecmp(argv[0], "json_list") == 0) {
238 				conference_api_sub_json_list(NULL, stream, argc, argv);
239 			} else if (strcasecmp(argv[0], "help") == 0 || strcasecmp(argv[0], "commands") == 0) {
240 				stream->write_function(stream, "%s\n", api_syntax);
241 			} else if (argv[1] && strcasecmp(argv[1], "dial") == 0) {
242 				if (conference_api_sub_dial(NULL, stream, argc, argv) != SWITCH_STATUS_SUCCESS) {
243 					/* command returned error, so show syntax usage */
244 					stream->write_function(stream, "%s %s", conference_api_sub_commands[CONF_API_COMMAND_DIAL].pcommand,
245 										   conference_api_sub_commands[CONF_API_COMMAND_DIAL].psyntax);
246 				}
247 			} else if (argv[1] && strcasecmp(argv[1], "bgdial") == 0) {
248 				if (conference_api_sub_bgdial(NULL, stream, argc, argv) != SWITCH_STATUS_SUCCESS) {
249 					/* command returned error, so show syntax usage */
250 					stream->write_function(stream, "%s %s", conference_api_sub_commands[CONF_API_COMMAND_BGDIAL].pcommand,
251 										   conference_api_sub_commands[CONF_API_COMMAND_BGDIAL].psyntax);
252 				}
253 			} else {
254 				stream->write_function(stream, "-ERR Conference %s not found\n", argv[0]);
255 			}
256 		}
257 
258 	} else {
259 		int i;
260 
261 		for (i = 0; i < CONFFUNCAPISIZE; i++) {
262 			stream->write_function(stream, "<conf name> %s %s\n", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
263 		}
264 	}
265 
266 
267 	switch_safe_free(lbuf);
268 
269 	return status;
270 }
271 
conference_api_sub_syntax(char ** syntax)272 switch_status_t conference_api_sub_syntax(char **syntax)
273 {
274 	/* build api interface help ".syntax" field string */
275 	uint32_t i;
276 	size_t nl = 0, ol = 0;
277 	char cmd_str[256];
278 	char *tmp = NULL, *p = strdup("");
279 
280 	for (i = 0; i < CONFFUNCAPISIZE; i++) {
281 		nl = strlen(conference_api_sub_commands[i].pcommand) + strlen(conference_api_sub_commands[i].psyntax) + 5;
282 
283 		switch_snprintf(cmd_str, sizeof(cmd_str), "add conference ::conference::conference_list_conferences %s", conference_api_sub_commands[i].pcommand);
284 		switch_console_set_complete(cmd_str);
285 
286 		if (p != NULL) {
287 			ol = strlen(p);
288 		}
289 		tmp = realloc(p, ol + nl);
290 		if (tmp != NULL) {
291 			p = tmp;
292 			strcat(p, "\t\t");
293 			strcat(p, conference_api_sub_commands[i].pcommand);
294 			if (!zstr(conference_api_sub_commands[i].psyntax)) {
295 				strcat(p, " ");
296 				strcat(p, conference_api_sub_commands[i].psyntax);
297 			}
298 			if (i < CONFFUNCAPISIZE - 1) {
299 				strcat(p, "\n");
300 			}
301 		} else {
302 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't realloc\n");
303 			switch_safe_free(p);
304 			return SWITCH_STATUS_TERM;
305 		}
306 	}
307 
308 	*syntax = p;
309 
310 	return SWITCH_STATUS_SUCCESS;
311 }
312 
conference_api_sub_mute(conference_member_t * member,switch_stream_handle_t * stream,void * data)313 switch_status_t conference_api_sub_mute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
314 {
315 	switch_event_t *event;
316 
317 	if (member == NULL)
318 		return SWITCH_STATUS_GENERR;
319 
320 	conference_utils_member_clear_flag_locked(member, MFLAG_CAN_SPEAK);
321 	conference_utils_member_clear_flag_locked(member, MFLAG_TALKING);
322 
323 	if (member->session && !conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT) && !conference_utils_member_test_flag(member, MFLAG_HOLD)) {
324 		switch_core_media_hard_mute(member->session, SWITCH_TRUE);
325 	}
326 
327 	if (!(data) || !strstr((char *) data, "quiet")) {
328 		conference_utils_member_set_flag(member, MFLAG_INDICATE_MUTE);
329 	}
330 	conference_member_set_score_iir(member, 0);
331 
332 	if (stream != NULL) {
333 		stream->write_function(stream, "+OK mute %u\n", member->id);
334 	}
335 
336 	if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
337 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
338 		conference_member_add_event_data(member, event);
339 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "mute-member");
340 		switch_event_fire(&event);
341 	}
342 
343 	if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
344 		conference_al_gen_arc(member->conference, NULL);
345 	}
346 
347 	conference_member_update_status_field(member);
348 
349 	return SWITCH_STATUS_SUCCESS;
350 }
351 
conference_api_sub_unhold(conference_member_t * member,switch_stream_handle_t * stream,void * data)352 switch_status_t conference_api_sub_unhold(conference_member_t *member, switch_stream_handle_t *stream, void *data)
353 {
354 	mcu_layer_t *layer = NULL;
355 	switch_event_t *event;
356 
357 	if (member == NULL)
358 		return SWITCH_STATUS_GENERR;
359 
360 	conference_utils_member_clear_flag_locked(member, MFLAG_HOLD);
361 
362 	if (member->session && !conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT)) {
363 		switch_core_media_hard_mute(member->session, SWITCH_FALSE);
364 	}
365 
366 	conference_member_stop_file(member, FILE_STOP_ALL);
367 
368 	if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) != SWITCH_MEDIA_FLOW_SENDONLY) {
369 		if ((layer = conference_video_get_layer_locked(member))) {
370 			layer->clear = 1;
371 			conference_video_release_layer(&layer);
372 		}
373 
374 		conference_video_reset_video_bitrate_counters(member);
375 
376 		if (member->channel) {
377 			switch_channel_clear_flag(member->channel, CF_VIDEO_PAUSE_READ);
378 			switch_channel_video_sync(member->channel);
379 		}
380 	}
381 
382 	if (stream != NULL) {
383 		stream->write_function(stream, "+OK unhold %u\n", member->id);
384 	}
385 
386 	if (test_eflag(member->conference, EFLAG_HOLD_MEMBER) &&
387 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
388 		conference_member_add_event_data(member, event);
389 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unhold-member");
390 		switch_event_fire(&event);
391 	}
392 
393 	if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
394 		conference_al_gen_arc(member->conference, NULL);
395 	}
396 
397 	conference_member_update_status_field(member);
398 
399 	return SWITCH_STATUS_SUCCESS;
400 }
401 
conference_api_sub_hold(conference_member_t * member,switch_stream_handle_t * stream,void * data)402 switch_status_t conference_api_sub_hold(conference_member_t *member, switch_stream_handle_t *stream, void *data)
403 {
404 	switch_event_t *event;
405 
406 	if (member == NULL)
407 		return SWITCH_STATUS_GENERR;
408 
409 	conference_utils_member_clear_flag_locked(member, MFLAG_TALKING);
410 
411 	if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) != SWITCH_MEDIA_FLOW_SENDONLY) {
412 		conference_video_reset_video_bitrate_counters(member);
413 
414 		if (member->channel) {
415 			switch_channel_set_flag(member->channel, CF_VIDEO_PAUSE_READ);
416 			switch_core_session_request_video_refresh(member->session);
417 			switch_channel_video_sync(member->channel);
418 		}
419 	}
420 
421 	if (member->session) {
422 		switch_core_media_hard_mute(member->session, SWITCH_TRUE);
423 	}
424 
425 	conference_utils_member_set_flag(member, MFLAG_HOLD);
426 
427 	conference_member_set_score_iir(member, 0);
428 
429 	if (!zstr(data)) {
430 		conference_member_play_file(member, data, 0, SWITCH_FALSE);
431 	}
432 
433 	if (stream != NULL) {
434 		stream->write_function(stream, "+OK hold %u\n", member->id);
435 	}
436 
437 	if (test_eflag(member->conference, EFLAG_HOLD_MEMBER) &&
438 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
439 		conference_member_add_event_data(member, event);
440 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "hold-member");
441 		switch_event_fire(&event);
442 	}
443 
444 	if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
445 		conference_al_gen_arc(member->conference, NULL);
446 	}
447 
448 	conference_member_update_status_field(member);
449 
450 	return SWITCH_STATUS_SUCCESS;
451 }
452 
453 
conference_api_sub_tmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)454 switch_status_t conference_api_sub_tmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
455 {
456 
457 	if (member == NULL)
458 		return SWITCH_STATUS_GENERR;
459 
460 	if (conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) {
461 		return conference_api_sub_mute(member, stream, data);
462 	}
463 
464 	return conference_api_sub_unmute(member, stream, data);
465 }
466 
467 
conference_api_sub_unmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)468 switch_status_t conference_api_sub_unmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
469 {
470 	switch_event_t *event;
471 
472 	if (member == NULL)
473 		return SWITCH_STATUS_GENERR;
474 
475 	conference_utils_member_set_flag_locked(member, MFLAG_CAN_SPEAK);
476 
477 	if (member->session && !conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT)) {
478 		switch_core_media_hard_mute(member->session, SWITCH_FALSE);
479 	}
480 
481 	if (!(data) || !strstr((char *) data, "quiet")) {
482 		conference_utils_member_set_flag(member, MFLAG_INDICATE_UNMUTE);
483 	}
484 
485 	if (stream != NULL) {
486 		stream->write_function(stream, "+OK unmute %u\n", member->id);
487 	}
488 
489 	if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
490 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
491 		conference_member_add_event_data(member, event);
492 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unmute-member");
493 		switch_event_fire(&event);
494 	}
495 
496 	if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
497 		conference_al_gen_arc(member->conference, NULL);
498 	}
499 
500 	conference_member_update_status_field(member);
501 
502 	return SWITCH_STATUS_SUCCESS;
503 }
504 
conference_api_sub_conference_video_vmute_snap(conference_member_t * member,switch_stream_handle_t * stream,void * data)505 switch_status_t conference_api_sub_conference_video_vmute_snap(conference_member_t *member, switch_stream_handle_t *stream, void *data)
506 {
507 	switch_bool_t clear = SWITCH_FALSE;
508 
509 	if (member == NULL)
510 		return SWITCH_STATUS_GENERR;
511 
512 	if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
513 		return SWITCH_STATUS_SUCCESS;
514 	}
515 
516 	if (!member->conference->canvases[0]) {
517 		if (stream) stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
518 		return SWITCH_STATUS_SUCCESS;
519 	}
520 
521 	if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
522 		if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
523 		return SWITCH_STATUS_SUCCESS;
524 	}
525 
526 	if (stream != NULL) {
527 		stream->write_function(stream, "+OK vmute image snapped %u\n", member->id);
528 	}
529 
530 	if (data && !strcasecmp((char *)data, "clear")) {
531 		clear = SWITCH_TRUE;
532 	}
533 
534 	conference_video_vmute_snap(member, clear);
535 
536 	return SWITCH_STATUS_SUCCESS;
537 }
538 
conference_api_sub_canvas_auto_clear(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)539 switch_status_t conference_api_sub_canvas_auto_clear(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
540 {
541 	int canvas_id_start = 0;
542 	int canvas_id_end   = 0;
543 	int i = 0;
544 
545 	if (argc < 3) {
546 		stream->write_function(stream, "+OK");
547 
548 		for (i = 0; i < conference->canvas_count; i++) {
549 			stream->write_function(stream, " canvas %d auto_clear=%s", i + 1, conference->canvases[i]->disable_auto_clear ? "false" : "true");
550 		}
551 
552 		stream->write_function(stream, "\n");
553 		return SWITCH_STATUS_SUCCESS;
554 	}
555 
556 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%s\n", argv[1]);
557 
558 	canvas_id_start = atoi(argv[2]);
559 
560 	if (canvas_id_start == 0) {
561 		canvas_id_end = conference->canvas_count - 1;
562 	} else {
563 		canvas_id_start--;
564 		canvas_id_end = canvas_id_start;
565 	}
566 
567 	stream->write_function(stream, "+OK");
568 	switch_mutex_lock(conference->canvas_mutex);
569 
570 	for (i = canvas_id_start; i<= canvas_id_end; i++) {
571 		conference->canvases[i]->disable_auto_clear = !switch_true(argv[3]);
572 		stream->write_function(stream, " canvas %d auto_clear=%s", i + 1, argv[3]);
573 	}
574 
575 	switch_mutex_unlock(conference->canvas_mutex);
576 	stream->write_function(stream, "\n");
577 
578 	return SWITCH_STATUS_SUCCESS;
579 }
580 
conference_api_sub_vmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)581 switch_status_t conference_api_sub_vmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
582 {
583 	switch_event_t *event;
584 
585 	if (member == NULL)
586 		return SWITCH_STATUS_GENERR;
587 
588 	if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
589 		return SWITCH_STATUS_SUCCESS;
590 	}
591 
592 	if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
593 		if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
594 		return SWITCH_STATUS_SUCCESS;
595 	}
596 
597 	conference_utils_member_clear_flag_locked(member, MFLAG_CAN_BE_SEEN);
598 	conference_video_reset_video_bitrate_counters(member);
599 
600 	if (member->channel) {
601 		switch_channel_set_flag(member->channel, CF_VIDEO_PAUSE_READ);
602 		switch_core_session_request_video_refresh(member->session);
603 		switch_channel_video_sync(member->channel);
604 	}
605 
606 	if (!(data) || !strstr((char *) data, "quiet")) {
607 		conference_utils_member_set_flag(member, MFLAG_INDICATE_MUTE);
608 	}
609 
610 	if (stream != NULL) {
611 		stream->write_function(stream, "+OK vmute %u\n", member->id);
612 	}
613 
614 	if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
615 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
616 		conference_member_add_event_data(member, event);
617 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "vmute-member");
618 		switch_event_fire(&event);
619 	}
620 
621 	conference_member_update_status_field(member);
622 
623 	return SWITCH_STATUS_SUCCESS;
624 }
625 
626 
conference_api_sub_tvmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)627 switch_status_t conference_api_sub_tvmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
628 {
629 
630 	if (member == NULL)
631 		return SWITCH_STATUS_GENERR;
632 
633 	if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
634 		if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
635 		return SWITCH_STATUS_SUCCESS;
636 	}
637 
638 	if (conference_utils_member_test_flag(member, MFLAG_CAN_BE_SEEN)) {
639 		return conference_api_sub_vmute(member, stream, data);
640 	}
641 
642 	return conference_api_sub_unvmute(member, stream, data);
643 }
644 
645 
conference_api_sub_unvmute(conference_member_t * member,switch_stream_handle_t * stream,void * data)646 switch_status_t conference_api_sub_unvmute(conference_member_t *member, switch_stream_handle_t *stream, void *data)
647 {
648 	switch_event_t *event;
649 	mcu_layer_t *layer = NULL;
650 
651 	if (member == NULL)
652 		return SWITCH_STATUS_GENERR;
653 
654 	if (switch_core_session_media_flow(member->session, SWITCH_MEDIA_TYPE_VIDEO) == SWITCH_MEDIA_FLOW_SENDONLY) {
655 		return SWITCH_STATUS_SUCCESS;
656 	}
657 
658 	if (conference_utils_member_test_flag(member, MFLAG_HOLD)) {
659 		if (stream) stream->write_function(stream, "-ERR member %u is on hold\n", member->id);
660 		return SWITCH_STATUS_SUCCESS;
661 	}
662 
663 	if ((layer = conference_video_get_layer_locked(member))) {
664 		layer->clear = 1;
665 		conference_video_release_layer(&layer);
666 	}
667 
668 	conference_utils_member_set_flag_locked(member, MFLAG_CAN_BE_SEEN);
669 	conference_video_reset_video_bitrate_counters(member);
670 
671 	if (member->channel) {
672 		switch_channel_clear_flag(member->channel, CF_VIDEO_PAUSE_READ);
673 		switch_channel_video_sync(member->channel);
674 	}
675 
676 	if (!(data) || !strstr((char *) data, "quiet")) {
677 		conference_utils_member_set_flag(member, MFLAG_INDICATE_UNMUTE);
678 	}
679 
680 	if (stream != NULL) {
681 		stream->write_function(stream, "+OK unvmute %u\n", member->id);
682 	}
683 
684 	if (test_eflag(member->conference, EFLAG_MUTE_MEMBER) &&
685 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
686 		conference_member_add_event_data(member, event);
687 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unvmute-member");
688 		switch_event_fire(&event);
689 	}
690 
691 
692 	conference_member_update_status_field(member);
693 
694 	return SWITCH_STATUS_SUCCESS;
695 }
696 
conference_api_sub_vblind(conference_member_t * member,switch_stream_handle_t * stream,void * data)697 switch_status_t conference_api_sub_vblind(conference_member_t *member, switch_stream_handle_t *stream, void *data)
698 {
699 	switch_event_t *event;
700 
701 	if (member == NULL)
702 		return SWITCH_STATUS_GENERR;
703 
704 	conference_utils_member_clear_flag_locked(member, MFLAG_CAN_SEE);
705 
706 	if (!conference_utils_member_test_flag(member, MFLAG_HOLD)) {
707 		switch_core_session_write_blank_video(member->session, 50);
708 		conference_video_reset_video_bitrate_counters(member);
709 	}
710 
711 	if (!(data) || !strstr((char *) data, "quiet")) {
712 		conference_utils_member_set_flag(member, MFLAG_INDICATE_BLIND);
713 	}
714 
715 	if (stream) stream->write_function(stream, "+OK vblind %u\n", member->id);
716 
717 	if (test_eflag(member->conference, EFLAG_BLIND_MEMBER) &&
718 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
719 		conference_member_add_event_data(member, event);
720 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "vblind-member");
721 		switch_event_fire(&event);
722 	}
723 
724 	conference_member_update_status_field(member);
725 
726 	return SWITCH_STATUS_SUCCESS;
727 }
728 
729 
conference_api_sub_tvblind(conference_member_t * member,switch_stream_handle_t * stream,void * data)730 switch_status_t conference_api_sub_tvblind(conference_member_t *member, switch_stream_handle_t *stream, void *data)
731 {
732 
733 	if (member == NULL)
734 		return SWITCH_STATUS_GENERR;
735 
736 	if (conference_utils_member_test_flag(member, MFLAG_CAN_SEE)) {
737 		return conference_api_sub_vblind(member, stream, data);
738 	}
739 
740 	return conference_api_sub_unvblind(member, stream, data);
741 }
742 
743 
conference_api_sub_unvblind(conference_member_t * member,switch_stream_handle_t * stream,void * data)744 switch_status_t conference_api_sub_unvblind(conference_member_t *member, switch_stream_handle_t *stream, void *data)
745 {
746 	switch_event_t *event;
747 
748 	if (member == NULL)
749 		return SWITCH_STATUS_GENERR;
750 
751 	conference_utils_member_set_flag_locked(member, MFLAG_CAN_SEE);
752 
753 	if (!conference_utils_member_test_flag(member, MFLAG_HOLD)) {
754 		conference_video_reset_video_bitrate_counters(member);
755 		switch_channel_set_flag(member->channel, CF_VIDEO_REFRESH_REQ);
756 	}
757 
758 	if (!(data) || !strstr((char *) data, "quiet")) {
759 		conference_utils_member_set_flag(member, MFLAG_INDICATE_UNBLIND);
760 	}
761 
762 	if (stream != NULL) {
763 		stream->write_function(stream, "+OK unvblind %u\n", member->id);
764 	}
765 
766 	if (test_eflag(member->conference, EFLAG_BLIND_MEMBER) &&
767 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
768 		conference_member_add_event_data(member, event);
769 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unvblind-member");
770 		switch_event_fire(&event);
771 	}
772 
773 
774 	conference_member_update_status_field(member);
775 
776 	return SWITCH_STATUS_SUCCESS;
777 }
778 
conference_api_sub_deaf(conference_member_t * member,switch_stream_handle_t * stream,void * data)779 switch_status_t conference_api_sub_deaf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
780 {
781 	switch_event_t *event;
782 
783 	if (member == NULL)
784 		return SWITCH_STATUS_GENERR;
785 
786 	conference_utils_member_clear_flag_locked(member, MFLAG_CAN_HEAR);
787 
788 	if (!(data) || !strstr((char *) data, "quiet")) {
789 		conference_utils_member_set_flag(member, MFLAG_INDICATE_DEAF);
790 	}
791 
792 	if (stream != NULL) {
793 		stream->write_function(stream, "+OK deaf %u\n", member->id);
794 	}
795 	if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
796 		conference_member_add_event_data(member, event);
797 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "deaf-member");
798 		switch_event_fire(&event);
799 	}
800 
801 	if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
802 		conference_al_gen_arc(member->conference, NULL);
803 	}
804 
805 	conference_member_update_status_field(member);
806 
807 	return SWITCH_STATUS_SUCCESS;
808 }
809 
conference_api_sub_video_filter(conference_member_t * member,switch_stream_handle_t * stream,void * data)810 switch_status_t conference_api_sub_video_filter(conference_member_t *member, switch_stream_handle_t *stream, void *data)
811 {
812 	char *filter_str = (char *) data;
813 
814 	switch_core_video_parse_filter_string(&member->video_filters, filter_str);
815 
816 	stream->write_function(stream, "+OK\n");
817 
818 	return SWITCH_STATUS_SUCCESS;
819 }
820 
conference_api_sub_undeaf(conference_member_t * member,switch_stream_handle_t * stream,void * data)821 switch_status_t conference_api_sub_undeaf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
822 {
823 	switch_event_t *event;
824 
825 	if (member == NULL)
826 		return SWITCH_STATUS_GENERR;
827 
828 	conference_utils_member_set_flag_locked(member, MFLAG_CAN_HEAR);
829 
830 	if (!(data) || !strstr((char *) data, "quiet")) {
831 		conference_utils_member_set_flag(member, MFLAG_INDICATE_UNDEAF);
832 	}
833 
834 	if (stream != NULL) {
835 		stream->write_function(stream, "+OK undeaf %u\n", member->id);
836 	}
837 	if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
838 		conference_member_add_event_data(member, event);
839 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "undeaf-member");
840 		switch_event_fire(&event);
841 	}
842 
843 	if (conference_utils_test_flag(member->conference, CFLAG_POSITIONAL)) {
844 		conference_al_gen_arc(member->conference, NULL);
845 	}
846 
847 	conference_member_update_status_field(member);
848 
849 	return SWITCH_STATUS_SUCCESS;
850 }
851 
conference_api_sub_hup(conference_member_t * member,switch_stream_handle_t * stream,void * data)852 switch_status_t conference_api_sub_hup(conference_member_t *member, switch_stream_handle_t *stream, void *data)
853 {
854 	switch_event_t *event;
855 
856 	if (member == NULL) {
857 		if (stream != NULL) {
858 			stream->write_function(stream, "-ERR Invalid member!\n");
859 		}
860 		return SWITCH_STATUS_GENERR;
861 	}
862 
863 	conference_utils_member_clear_flag(member, MFLAG_RUNNING);
864 	if (stream != NULL) {
865 		stream->write_function(stream, "+OK hup %u\n", member->id);
866 	}
867 
868 	if (member->conference && test_eflag(member->conference, EFLAG_HUP_MEMBER)) {
869 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
870 			conference_member_add_event_data(member, event);
871 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "hup-member");
872 			switch_event_fire(&event);
873 		}
874 	}
875 
876 	return SWITCH_STATUS_SUCCESS;
877 }
878 
conference_api_sub_kick(conference_member_t * member,switch_stream_handle_t * stream,void * data)879 switch_status_t conference_api_sub_kick(conference_member_t *member, switch_stream_handle_t *stream, void *data)
880 {
881 	switch_event_t *event;
882 
883 	if (member == NULL) {
884 		return SWITCH_STATUS_GENERR;
885 	}
886 
887 	conference_utils_member_clear_flag(member, MFLAG_RUNNING);
888 	conference_utils_member_set_flag_locked(member, MFLAG_KICKED);
889 	switch_core_session_kill_channel(member->session, SWITCH_SIG_BREAK);
890 
891 	if (data && member->session) {
892 		member->kicked_sound = switch_core_session_strdup(member->session, (char *) data);
893 	}
894 
895 	if (stream != NULL) {
896 		stream->write_function(stream, "+OK kicked %u\n", member->id);
897 	}
898 
899 	if (member->conference && test_eflag(member->conference, EFLAG_KICK_MEMBER)) {
900 		if (switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
901 			conference_member_add_event_data(member, event);
902 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "kick-member");
903 			switch_event_fire(&event);
904 		}
905 	}
906 
907 	return SWITCH_STATUS_SUCCESS;
908 }
909 
conference_api_sub_vid_border(conference_member_t * member,switch_stream_handle_t * stream,void * data)910 switch_status_t conference_api_sub_vid_border(conference_member_t *member, switch_stream_handle_t *stream, void *data)
911 {
912 	char *arg = (char *) data;
913 	mcu_layer_t *layer = NULL;
914 	int len = 5;
915 
916 	if (member == NULL) {
917 		return SWITCH_STATUS_GENERR;
918 	}
919 
920 	if (zstr(arg)) {
921 		if (stream) {
922 			stream->write_function(stream, "-ERR No text supplied\n", switch_channel_get_name(member->channel));
923 		}
924 		goto end;
925 	}
926 
927 	layer = conference_video_get_layer_locked(member);
928 
929 	if (!layer) {
930 		if (stream) {
931 			stream->write_function(stream, "-ERR Channel %s is not in a video layer\n", switch_channel_get_name(member->channel));
932 		}
933 		goto end;
934 	}
935 
936 	if (!strcasecmp(arg, "toggle")) {
937 		if (member->video_manual_border) {
938 			len = 0;
939 		} else {
940 			len = 5;
941 		}
942 	} else {
943 		len = atoi(arg);
944 	}
945 
946 	if (len < 0 || len > 20) {
947 		len = 0;
948 	}
949 
950 	member->video_manual_border = len;
951 	layer->manual_border = len;
952 
953 	if (stream) {
954 		stream->write_function(stream, "+OK\n");
955 	}
956 
957  end:
958 
959 	if (layer) {
960 		conference_video_release_layer(&layer);
961 	}
962 
963 	return SWITCH_STATUS_SUCCESS;
964 }
965 
966 
conference_api_sub_vid_flip(conference_member_t * member,switch_stream_handle_t * stream,void * data)967 switch_status_t conference_api_sub_vid_flip(conference_member_t *member, switch_stream_handle_t *stream, void *data)
968 {
969 	char *arg = (char *) data;
970 
971 	if (member == NULL) {
972 		return SWITCH_STATUS_GENERR;
973 	}
974 
975 	if ((conference_utils_member_test_flag(member, MFLAG_FLIP_VIDEO) || conference_utils_member_test_flag(member, MFLAG_MIRROR_VIDEO)) && !arg) {
976 		conference_utils_member_clear_flag_locked(member, MFLAG_FLIP_VIDEO);
977 		conference_utils_member_clear_flag_locked(member, MFLAG_ROTATE_VIDEO);
978 		conference_utils_member_clear_flag_locked(member, MFLAG_MIRROR_VIDEO);
979 	} else {
980 
981 		if (arg && !strcasecmp(arg, "mirror")) {
982 			if (conference_utils_member_test_flag(member, MFLAG_MIRROR_VIDEO)) {
983 				conference_utils_member_clear_flag_locked(member, MFLAG_MIRROR_VIDEO);
984 			} else {
985 				conference_utils_member_set_flag_locked(member, MFLAG_MIRROR_VIDEO);
986 			}
987 		} else {
988 			conference_utils_member_set_flag_locked(member, MFLAG_FLIP_VIDEO);
989 
990 			if (arg) {
991 				if (!strcasecmp(arg, "rotate")) {
992 					conference_utils_member_set_flag_locked(member, MFLAG_ROTATE_VIDEO);
993 				} else if (switch_is_number(arg)) {
994 					int num = atoi(arg);
995 
996 					if (num == 0 || num == 90 || num == 180 || num == 270) {
997 						member->flip = num;
998 					}
999 				}
1000 			} else {
1001 				member->flip = 180;
1002 			}
1003 		}
1004 	}
1005 
1006 	if (stream != NULL) {
1007 		stream->write_function(stream, "+OK flipped %u\n", member->id);
1008 	}
1009 
1010 
1011 	return SWITCH_STATUS_SUCCESS;
1012 }
1013 
1014 
conference_api_sub_dtmf(conference_member_t * member,switch_stream_handle_t * stream,void * data)1015 switch_status_t conference_api_sub_dtmf(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1016 {
1017 	switch_event_t *event;
1018 	char *dtmf = (char *) data;
1019 
1020 	if (member == NULL) {
1021 		if (stream != NULL) stream->write_function(stream, "-ERR Invalid member!\n");
1022 		return SWITCH_STATUS_GENERR;
1023 	}
1024 
1025 	if (zstr(dtmf)) {
1026 		if (stream != NULL) stream->write_function(stream, "-ERR Invalid input!\n");
1027 		return SWITCH_STATUS_GENERR;
1028 	} else {
1029 		char *p;
1030 
1031 		for(p = dtmf; p && *p; p++) {
1032 			switch_dtmf_t *dt, digit = { *p, SWITCH_DEFAULT_DTMF_DURATION };
1033 
1034 			switch_zmalloc(dt, sizeof(*dt));
1035 			*dt = digit;
1036 
1037 			switch_queue_push(member->dtmf_queue, dt);
1038 			switch_core_session_kill_channel(member->session, SWITCH_SIG_BREAK);
1039 		}
1040 	}
1041 
1042 	if (stream != NULL) {
1043 		stream->write_function(stream, "+OK sent %s to %u\n", (char *) data, member->id);
1044 	}
1045 
1046 	if (test_eflag(member->conference, EFLAG_DTMF_MEMBER) &&
1047 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1048 		conference_member_add_event_data(member, event);
1049 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "dtmf-member");
1050 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Digits", dtmf);
1051 		switch_event_fire(&event);
1052 	}
1053 
1054 	return SWITCH_STATUS_SUCCESS;
1055 }
1056 
conference_api_sub_watching_canvas(conference_member_t * member,switch_stream_handle_t * stream,void * data)1057 switch_status_t conference_api_sub_watching_canvas(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1058 {
1059 	int index;
1060 	char *val = (char *) data;
1061 
1062 	if (member->conference->canvas_count == 1) {
1063 		stream->write_function(stream, "-ERR Only 1 Canvas\n");
1064 		return SWITCH_STATUS_SUCCESS;
1065 	}
1066 
1067 	index = conference_member_get_canvas_id(member, val, SWITCH_TRUE);
1068 
1069 	if (index < 0) {
1070 		stream->write_function(stream, "-ERR Invalid DATA\n");
1071 		return SWITCH_STATUS_SUCCESS;
1072 	}
1073 
1074 	member->watching_canvas_id = index;
1075 	conference_video_reset_member_codec_index(member);
1076 	switch_core_session_request_video_refresh(member->session);
1077 	switch_core_media_gen_key_frame(member->session);
1078 	member->conference->canvases[index]->send_keyframe = 10;
1079 	member->conference->canvases[index]->refresh = 1;
1080 	stream->write_function(stream, "+OK watching canvas %d\n", index + 1);
1081 
1082 	conference_member_update_status_field(member);
1083 
1084 	return SWITCH_STATUS_SUCCESS;
1085 }
1086 
conference_api_sub_canvas(conference_member_t * member,switch_stream_handle_t * stream,void * data)1087 switch_status_t conference_api_sub_canvas(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1088 {
1089 	int index;
1090 	char *val = (char *) data;
1091 	//mcu_canvas_t *canvas = NULL;
1092 
1093 	if (member->conference->canvas_count == 1) {
1094 		stream->write_function(stream, "-ERR Only 1 Canvas\n");
1095 		return SWITCH_STATUS_SUCCESS;
1096 	}
1097 
1098 	switch_mutex_lock(member->conference->canvas_mutex);
1099 
1100 	index = conference_member_get_canvas_id(member, val, SWITCH_FALSE);
1101 
1102 	if (index < 0) {
1103 		stream->write_function(stream, "-ERR Invalid DATA\n");
1104 		switch_mutex_unlock(member->conference->canvas_mutex);
1105 		return SWITCH_STATUS_SUCCESS;
1106 	}
1107 
1108 	conference_video_detach_video_layer(member);
1109 	member->canvas_id = index;
1110 	member->layer_timeout = DEFAULT_LAYER_TIMEOUT;
1111 
1112 	//canvas = member->conference->canvases[member->canvas_id];
1113 	//conference_video_attach_video_layer(member, canvas, index);
1114 	conference_video_reset_member_codec_index(member);
1115 	switch_mutex_unlock(member->conference->canvas_mutex);
1116 
1117 	switch_core_session_request_video_refresh(member->session);
1118 	switch_core_media_gen_key_frame(member->session);
1119 	member->conference->canvases[index]->send_keyframe = 10;
1120 	member->conference->canvases[index]->refresh = 1;
1121 	stream->write_function(stream, "+OK canvas %d\n", member->canvas_id + 1);
1122 
1123 	return SWITCH_STATUS_SUCCESS;
1124 }
1125 
1126 
1127 
conference_api_sub_layer(conference_member_t * member,switch_stream_handle_t * stream,void * data)1128 switch_status_t conference_api_sub_layer(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1129 {
1130 	int index = -1;
1131 	mcu_canvas_t *canvas = NULL;
1132 	char *val = (char *) data;
1133 
1134 	if (!val) {
1135 		stream->write_function(stream, "-ERR Invalid DATA\n");
1136 		return SWITCH_STATUS_SUCCESS;
1137 	}
1138 
1139 	if (member->canvas_id < 0) {
1140 		stream->write_function(stream, "-ERR Invalid Canvas\n");
1141 		return SWITCH_STATUS_FALSE;
1142 	}
1143 
1144 
1145 	switch_mutex_lock(member->conference->canvas_mutex);
1146 
1147 	if (switch_is_number(val)) {
1148 		index = atoi(val) - 1;
1149 
1150 		if (index < 0) {
1151 			index = 0;
1152 		}
1153 	} else {
1154 		index = member->video_layer_id;
1155 
1156 		if (index < 0) index = 0;
1157 
1158 		if (!strcasecmp(val, "next")) {
1159 			index++;
1160 		} else if (!strcasecmp(val, "prev")) {
1161 			index--;
1162 		}
1163 	}
1164 
1165 	canvas = member->conference->canvases[member->canvas_id];
1166 
1167 	if (index >= canvas->total_layers) {
1168 		index = 0;
1169 	}
1170 
1171 	if (index < 0) {
1172 		index = canvas->total_layers - 1;
1173 	}
1174 
1175 	conference_video_attach_video_layer(member, canvas, index);
1176 	switch_mutex_unlock(member->conference->canvas_mutex);
1177 
1178 	switch_core_session_request_video_refresh(member->session);
1179 	switch_core_media_gen_key_frame(member->session);
1180 	canvas->send_keyframe = 10;
1181 	canvas->refresh = 1;
1182 	stream->write_function(stream, "+OK layer %d\n", member->video_layer_id + 1);
1183 
1184 	return SWITCH_STATUS_SUCCESS;
1185 }
1186 
1187 
conference_api_sub_energy(conference_member_t * member,switch_stream_handle_t * stream,void * data)1188 switch_status_t conference_api_sub_energy(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1189 {
1190 	switch_event_t *event;
1191 
1192 	if (member == NULL) {
1193 		return SWITCH_STATUS_GENERR;
1194 	}
1195 
1196 	if (data) {
1197 		lock_member(member);
1198 		if (!strcasecmp(data, "up")) {
1199 			member->energy_level += 200;
1200 			if (member->energy_level > 1800) {
1201 				member->energy_level = 1800;
1202 			}
1203 		} else if (!strcasecmp(data, "down")) {
1204 			member->energy_level -= 200;
1205 			if (member->energy_level < 0) {
1206 				member->energy_level = 0;
1207 			}
1208 		} else {
1209 			member->energy_level = atoi((char *) data);
1210 		}
1211 		unlock_member(member);
1212 	}
1213 	if (stream != NULL) {
1214 		stream->write_function(stream, "Energy %u = %d\n", member->id, member->energy_level);
1215 	}
1216 
1217 	if (member->auto_energy_level && member->energy_level > member->auto_energy_level) {
1218 		member->auto_energy_level  = 0;
1219 		if (stream != NULL) {
1220 			stream->write_function(stream, "Auto-Energy level exceeded, Auto-Energy mode disabled\n", SWITCH_VA_NONE);
1221 		}
1222 	}
1223 
1224 
1225 	if (test_eflag(member->conference, EFLAG_ENERGY_LEVEL_MEMBER) &&
1226 		data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1227 		conference_member_add_event_data(member, event);
1228 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "energy-level-member");
1229 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->energy_level);
1230 		switch_event_fire(&event);
1231 	}
1232 
1233 	return SWITCH_STATUS_SUCCESS;
1234 }
1235 
conference_api_set_agc(conference_member_t * member,const char * data)1236 void conference_api_set_agc(conference_member_t *member, const char *data)
1237 {
1238 	int tmp = 0;
1239 	char *argv[4] = { 0 };
1240 	char *conf;
1241 
1242 	if (data) {
1243 		conf = switch_core_strdup(member->pool, data);
1244 		switch_split(conf, ':', argv);
1245 	} else {
1246 		member->agc_level = member->conference->agc_level;
1247 		member->agc_low_energy_level = member->conference->agc_low_energy_level;
1248 		member->agc_change_factor = member->conference->agc_change_factor;
1249 		member->agc_margin = member->conference->agc_margin;
1250 		member->agc_period_len = member->conference->agc_period_len;
1251 	}
1252 
1253 	if (argv[0]) {
1254 		tmp = atoi(argv[0]);
1255 
1256 		if (tmp > 0) {
1257 			member->agc_level = tmp;
1258 		}
1259 	}
1260 
1261 	if (argv[1]) {
1262 		tmp = atoi(argv[1]);
1263 
1264 		if (tmp > 0) {
1265 			member->agc_low_energy_level = tmp;
1266 		}
1267 	}
1268 
1269 
1270 	if (argv[2]) {
1271 		tmp = atoi(argv[2]);
1272 
1273 		if (tmp > 0) {
1274 			member->agc_change_factor = tmp;
1275 		}
1276 	}
1277 
1278 
1279 	if (argv[0]) {
1280 		tmp = atoi(argv[0]);
1281 
1282 		if (tmp > 0) {
1283 			member->agc_period_len = (1000 / member->conference->interval) * tmp;
1284 		}
1285 	}
1286 
1287 
1288 	if (!member->agc) {
1289 		switch_agc_create(&member->agc, member->agc_level, member->agc_low_energy_level, member->agc_margin,
1290 						  member->agc_change_factor, member->agc_period_len);
1291 		switch_agc_set_token(member->agc, switch_channel_get_name(member->channel));
1292 	} else {
1293 		switch_agc_set(member->agc, member->agc_level, member->agc_low_energy_level, member->agc_margin,
1294 					   member->agc_change_factor, member->agc_period_len);
1295 	}
1296 
1297 }
1298 
1299 
conference_api_sub_agc(conference_member_t * member,switch_stream_handle_t * stream,void * data)1300 switch_status_t conference_api_sub_agc(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1301 {
1302 	switch_event_t *event;
1303 
1304 	if (member == NULL) {
1305 		return SWITCH_STATUS_GENERR;
1306 	}
1307 
1308 	if (data) {
1309 		lock_member(member);
1310 		if (!strcasecmp(data, "up")) {
1311 			member->agc_level += 200;
1312 			if (member->agc_level > 1800) {
1313 				member->agc_level = 1800;
1314 			}
1315 		} else if (!strcasecmp(data, "down")) {
1316 			member->agc_level -= 200;
1317 			if (member->agc_level < 0) {
1318 				member->agc_level = 0;
1319 			}
1320 		} else {
1321 			conference_api_set_agc(member, (char *)data);
1322 		}
1323 		unlock_member(member);
1324 	}
1325 	if (stream != NULL) {
1326 		stream->write_function(stream, "Agc %u = %d\n", member->id, member->agc_level);
1327 	}
1328 
1329 
1330 	//	if (test_eflag(member->conference, EFLAG_AGC_LEVEL_MEMBER) &&
1331 	if (data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1332 		conference_member_add_event_data(member, event);
1333 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "agc-level-member");
1334 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Agc-Level", "%d", member->agc_level);
1335 		switch_event_fire(&event);
1336 	}
1337 
1338 	return SWITCH_STATUS_SUCCESS;
1339 }
1340 
1341 
conference_api_sub_auto_energy(conference_member_t * member,switch_stream_handle_t * stream,void * data)1342 switch_status_t conference_api_sub_auto_energy(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1343 {
1344 	switch_event_t *event;
1345 
1346 	if (member == NULL) {
1347 		return SWITCH_STATUS_GENERR;
1348 	}
1349 
1350 	if (data) {
1351 		lock_member(member);
1352 		if (!strcasecmp(data, "up")) {
1353 			member->auto_energy_level += 200;
1354 			if (member->auto_energy_level > 1800) {
1355 				member->auto_energy_level = 1800;
1356 			}
1357 		} else if (!strcasecmp(data, "down")) {
1358 			member->auto_energy_level -= 200;
1359 			if (member->auto_energy_level < 0) {
1360 				member->auto_energy_level = 0;
1361 			}
1362 		} else {
1363 			member->auto_energy_level = atoi((char *) data);
1364 		}
1365 		unlock_member(member);
1366 	}
1367 	if (stream != NULL) {
1368 		stream->write_function(stream, "%u = Auto-Energy: %d Energy: %d\n", member->id, member->auto_energy_level, member->energy_level);
1369 	}
1370 
1371 	if (!member->energy_level) {
1372 		member->energy_level = member->auto_energy_level / 2;
1373 	}
1374 
1375 
1376 	if (test_eflag(member->conference, EFLAG_ENERGY_LEVEL_MEMBER) &&
1377 		data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1378 		conference_member_add_event_data(member, event);
1379 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "auto-energy-level-member");
1380 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Auto-Energy-Level", "%d", member->auto_energy_level);
1381 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->energy_level);
1382 		switch_event_fire(&event);
1383 	}
1384 
1385 	return SWITCH_STATUS_SUCCESS;
1386 }
1387 
conference_api_sub_max_energy(conference_member_t * member,switch_stream_handle_t * stream,void * data)1388 switch_status_t conference_api_sub_max_energy(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1389 {
1390 	switch_event_t *event;
1391 
1392 	if (member == NULL) {
1393 		return SWITCH_STATUS_GENERR;
1394 	}
1395 
1396 	if (data) {
1397 		lock_member(member);
1398 		if (!strcasecmp(data, "up")) {
1399 			member->max_energy_level += 200;
1400 			if (member->max_energy_level > 1800) {
1401 				member->max_energy_level = 1800;
1402 			}
1403 		} else if (!strcasecmp(data, "down")) {
1404 			member->max_energy_level -= 200;
1405 			if (member->max_energy_level < 0) {
1406 				member->max_energy_level = 0;
1407 			}
1408 		} else {
1409 			member->max_energy_level = atoi((char *) data);
1410 		}
1411 		unlock_member(member);
1412 	}
1413 
1414 	if (member->max_energy_level && member->max_energy_level < member->energy_level) {
1415 		member->max_energy_level = 0;
1416 		if (stream != NULL) stream->write_function(stream, "-ERR %u Max-Energy cannot exceed energy level.\n", member->id);
1417 	} else if (data) {
1418 		char *p, *q;
1419 		if ((p = strchr(data, ':'))) {
1420 			p++;
1421 			if (*p) {
1422 				int tmp = atoi(p);
1423 				if (tmp >= 0) {
1424 					member->burst_mute_count = tmp / member->conference->interval;
1425 				}
1426 
1427 				if ((q = strchr(p, ':'))) {
1428 					q++;
1429 					if (*q) {
1430 						int tmp = atoi(q);
1431 
1432 						if (tmp >= 0) {
1433 							member->max_energy_hit_trigger = tmp;
1434 						}
1435 					}
1436 				}
1437 			}
1438 		}
1439 	}
1440 
1441 
1442 	if (stream != NULL) {
1443 		stream->write_function(stream, "%u = Max-Energy: %d Energy: %d Max-Energy-Mute: %dms Max-Energy-Hit-Trigger %d\n",
1444 							   member->id, member->energy_level, member->max_energy_level, member->burst_mute_count * member->conference->interval, member->max_energy_hit_trigger);
1445 	}
1446 
1447 
1448 	if (test_eflag(member->conference, EFLAG_ENERGY_LEVEL_MEMBER) &&
1449 		data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1450 		conference_member_add_event_data(member, event);
1451 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "max-energy-level-member");
1452 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Energy-Level", "%d", member->max_energy_level);
1453 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Energy-Mute", "%d", member->burst_mute_count * member->conference->interval);
1454 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Max-Energy-Hit-Trigger", "%d", member->max_energy_hit_trigger);
1455 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Energy-Level", "%d", member->max_energy_level);
1456 
1457 		switch_event_fire(&event);
1458 	}
1459 
1460 	return SWITCH_STATUS_SUCCESS;
1461 }
1462 
conference_api_sub_auto_position(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1463 switch_status_t conference_api_sub_auto_position(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1464 {
1465 #ifdef OPENAL_POSITIONING
1466 	char *arg = NULL;
1467 	int set = 0;
1468 
1469 	if (argc > 2) {
1470 		arg = argv[2];
1471 	}
1472 
1473 
1474 	if (!zstr(arg)) {
1475 		if (!strcasecmp(arg, "on")) {
1476 			conference_utils_set_flag(conference, CFLAG_POSITIONAL);
1477 			set = 1;
1478 		} else if (!strcasecmp(arg, "off")) {
1479 			conference_utils_clear_flag(conference, CFLAG_POSITIONAL);
1480 		}
1481 	}
1482 
1483 	if (set && conference_utils_test_flag(conference, CFLAG_POSITIONAL)) {
1484 		conference_al_gen_arc(conference, stream);
1485 	}
1486 
1487 	stream->write_function(stream, "+OK positioning %s\n", conference_utils_test_flag(conference, CFLAG_POSITIONAL) ? "on" : "off");
1488 
1489 #else
1490 	stream->write_function(stream, "-ERR not supported\n");
1491 
1492 #endif
1493 
1494 	return SWITCH_STATUS_SUCCESS;
1495 }
1496 
conference_api_sub_position(conference_member_t * member,switch_stream_handle_t * stream,void * data)1497 switch_status_t conference_api_sub_position(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1498 {
1499 #ifndef OPENAL_POSITIONING
1500 	if (stream) stream->write_function(stream, "-ERR not supported\n");
1501 #else
1502 	switch_event_t *event;
1503 
1504 	if (member == NULL) {
1505 		return SWITCH_STATUS_GENERR;
1506 	}
1507 
1508 	if (conference_utils_member_test_flag(member, MFLAG_NO_POSITIONAL)) {
1509 		if (stream) stream->write_function(stream,
1510 										   "-ERR %s has positional audio blocked.\n", switch_channel_get_name(member->channel));
1511 		return SWITCH_STATUS_SUCCESS;
1512 	}
1513 
1514 	if (!member->al) {
1515 		if (!conference_utils_member_test_flag(member, MFLAG_POSITIONAL) && member->conference->channels == 2) {
1516 			conference_utils_member_set_flag(member, MFLAG_POSITIONAL);
1517 			member->al = conference_al_create(member->pool);
1518 		} else {
1519 
1520 			if (stream) {
1521 				stream->write_function(stream, "-ERR Positional audio not avalilable %d\n", member->conference->channels);
1522 			}
1523 			return SWITCH_STATUS_FALSE;
1524 		}
1525 	}
1526 
1527 
1528 	if (data) {
1529 		if (conference_member_parse_position(member, data) != SWITCH_STATUS_SUCCESS) {
1530 			if (stream) {
1531 				stream->write_function(stream, "-ERR invalid input!\n");
1532 			}
1533 			return SWITCH_STATUS_FALSE;
1534 		}
1535 	}
1536 
1537 
1538 	if (stream != NULL) {
1539 		stream->write_function(stream, "+OK Position %u = %0.2f:%0.2f:%0.2f\n", member->id, member->al->pos_x, member->al->pos_y, member->al->pos_z);
1540 	}
1541 
1542 	if (test_eflag(member->conference, EFLAG_SET_POSITION_MEMBER) &&
1543 		data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1544 		conference_member_add_event_data(member, event);
1545 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "set-position-member");
1546 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Position", "%0.2f:%0.2f:%0.2f", member->al->pos_x, member->al->pos_y, member->al->pos_z);
1547 		switch_event_fire(&event);
1548 	}
1549 
1550 #endif
1551 
1552 	return SWITCH_STATUS_SUCCESS;
1553 }
1554 
conference_api_sub_volume_in(conference_member_t * member,switch_stream_handle_t * stream,void * data)1555 switch_status_t conference_api_sub_volume_in(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1556 {
1557 	switch_event_t *event;
1558 
1559 	if (member == NULL)
1560 		return SWITCH_STATUS_GENERR;
1561 
1562 	if (data) {
1563 		lock_member(member);
1564 		if (!strcasecmp(data, "up")) {
1565 			member->volume_in_level++;
1566 			switch_normalize_volume(member->volume_in_level);
1567 		} else if (!strcasecmp(data, "down")) {
1568 			member->volume_in_level--;
1569 			switch_normalize_volume(member->volume_in_level);
1570 		} else {
1571 			member->volume_in_level = atoi((char *) data);
1572 			switch_normalize_volume(member->volume_in_level);
1573 		}
1574 		unlock_member(member);
1575 
1576 	}
1577 	if (stream != NULL) {
1578 		stream->write_function(stream, "+OK Volume IN %u = %d\n", member->id, member->volume_in_level);
1579 	}
1580 	if (test_eflag(member->conference, EFLAG_VOLUME_IN_MEMBER) &&
1581 		data && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1582 		conference_member_add_event_data(member, event);
1583 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "volume-in-member");
1584 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Volume-Level", "%d", member->volume_in_level);
1585 		switch_event_fire(&event);
1586 	}
1587 
1588 	return SWITCH_STATUS_SUCCESS;
1589 }
1590 
conference_api_sub_volume_out(conference_member_t * member,switch_stream_handle_t * stream,void * data)1591 switch_status_t conference_api_sub_volume_out(conference_member_t *member, switch_stream_handle_t *stream, void *data)
1592 {
1593 	switch_event_t *event;
1594 
1595 	if (member == NULL)
1596 		return SWITCH_STATUS_GENERR;
1597 
1598 	if (data) {
1599 		lock_member(member);
1600 		if (!strcasecmp(data, "up")) {
1601 			member->volume_out_level++;
1602 			switch_normalize_volume(member->volume_out_level);
1603 		} else if (!strcasecmp(data, "down")) {
1604 			member->volume_out_level--;
1605 			switch_normalize_volume(member->volume_out_level);
1606 		} else {
1607 			member->volume_out_level = atoi((char *) data);
1608 			switch_normalize_volume(member->volume_out_level);
1609 		}
1610 		unlock_member(member);
1611 	}
1612 	if (stream != NULL) {
1613 		stream->write_function(stream, "+OK Volume OUT %u = %d\n", member->id, member->volume_out_level);
1614 	}
1615 	if (test_eflag(member->conference, EFLAG_VOLUME_OUT_MEMBER) && data &&
1616 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
1617 		conference_member_add_event_data(member, event);
1618 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "volume-out-member");
1619 		switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Volume-Level", "%d", member->volume_out_level);
1620 		switch_event_fire(&event);
1621 	}
1622 
1623 	return SWITCH_STATUS_SUCCESS;
1624 }
1625 
conference_api_sub_vid_personal(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1626 switch_status_t conference_api_sub_vid_personal(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1627 {
1628 	int on = 0;
1629 
1630 	if (!conference->canvases[0]) {
1631 		stream->write_function(stream, "-ERR conference is not in mixing mode\n");
1632 		return SWITCH_STATUS_SUCCESS;
1633 	}
1634 
1635 	if (argv[2]) {
1636 		on = switch_true(argv[2]);
1637 		if (on) {
1638 			conference_utils_set_flag(conference, CFLAG_PERSONAL_CANVAS);
1639 			conference->video_layout_group = "grid";
1640 			conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
1641 		} else {
1642 			conference_utils_clear_flag(conference, CFLAG_PERSONAL_CANVAS);
1643 		}
1644 	}
1645 
1646 	stream->write_function(stream, "+OK personal is %s\n", on ? "on" : "off");
1647 
1648 	return SWITCH_STATUS_SUCCESS;
1649 }
1650 
conference_api_sub_vid_bandwidth(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1651 switch_status_t conference_api_sub_vid_bandwidth(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1652 {
1653 	uint32_t i;
1654 	int32_t video_write_bandwidth;
1655 	int x = 0, id = -1;
1656 	char *group = NULL;
1657 	char *array[4] = {0};
1658 	float sdiv = 0;
1659 	int fdiv = 0;
1660 	int force_w = 0, force_h = 0;
1661 	conference_member_t *imember;
1662 
1663 	if (!argv[2]) {
1664 		stream->write_function(stream, "-ERR Invalid input\n");
1665 		return SWITCH_STATUS_SUCCESS;
1666 	}
1667 
1668 	switch_split(argv[2], ':', array);
1669 
1670 	if (array[1]) {
1671 		if (*array[1] == '=') {
1672 			char *p = array[1];
1673 
1674 			force_w = atoi((p+1));
1675 			if ((p = strchr(p+1, 'x'))) {
1676 				p++;
1677 				if (*p) {
1678 					force_h = atoi(p);
1679 				}
1680 			}
1681 
1682 			if (!(force_w > 100 && force_w < 1920 && force_h > 100 && force_h < 1080)) {
1683 				force_w = force_h = 0;
1684 			}
1685 		} else {
1686 			sdiv = atof(array[1]);
1687 			if (sdiv < 1.5 || sdiv > 8.0) {
1688 				sdiv = 0;
1689 			}
1690 		}
1691 	}
1692 
1693 	if (array[2]) {
1694 		fdiv = atoi(array[2]);
1695 		if (fdiv < 2 || fdiv > 8) {
1696 			fdiv = 0;
1697 		}
1698 	}
1699 
1700 	video_write_bandwidth = switch_parse_bandwidth_string(array[0]);
1701 
1702 	if (argv[3]) {
1703 		group = argv[3];
1704 	}
1705 
1706 	if (argv[4]) {
1707 		id = atoi(argv[4]);
1708 
1709 		if (id < 1 || id > MAX_CANVASES+1) {
1710 			id = -1;
1711 		}
1712 
1713 		if (id < 1 || id > conference->canvas_count) {
1714 			stream->write_function(stream, "-ERR Invalid canvas\n");
1715 			goto end;
1716 		}
1717 	}
1718 
1719 	switch_mutex_lock(conference->member_mutex);
1720 
1721 	for (imember = conference->members; imember; imember = imember->next) {
1722 
1723 		if (!imember->session || !switch_channel_test_flag(imember->channel, CF_VIDEO_READY)) {
1724 			continue;
1725 		}
1726 
1727 		switch_core_media_set_outgoing_bitrate(imember->session, SWITCH_MEDIA_TYPE_VIDEO, video_write_bandwidth);
1728 
1729 		stream->write_function(stream, "+OK Set Bandwidth %d kps for member %s\n", video_write_bandwidth, switch_channel_get_name(imember->channel));
1730 	}
1731 
1732 	for (i = 0; i <= conference->canvas_count; i++) {
1733 		if (i != id - 1) {
1734 			continue;
1735 		}
1736 
1737 		if (conference->canvases[i]) {
1738 			mcu_canvas_t *canvas = conference->canvases[i];
1739 			int j;
1740 
1741 			for (j = 0; j < canvas->write_codecs_count; j++) {
1742 				int w = canvas->width, h = canvas->height;
1743 
1744 				if ((zstr(group) || !strcmp(group, switch_str_nil(canvas->write_codecs[j]->video_codec_group)))) {
1745 					switch_core_codec_control(&canvas->write_codecs[j]->codec, SCC_VIDEO_BANDWIDTH,
1746 											  SCCT_INT, &video_write_bandwidth, SCCT_NONE, NULL, NULL, NULL);
1747 
1748 					if (fdiv) {
1749 						canvas->write_codecs[j]->fps_divisor = fdiv;
1750 					}
1751 
1752 					if (force_w && force_h) {
1753 						w = force_w;
1754 						h = force_h;
1755 					} else if (sdiv) {
1756 						w = (int)((float) w / sdiv);
1757 						h = (int)((float) h / sdiv);
1758 					}
1759 
1760 					if (w && h) {
1761 						switch_img_free(&canvas->write_codecs[j]->scaled_img);
1762 						if (w != canvas->img->d_w || h != canvas->img->d_h) {
1763 							canvas->write_codecs[j]->scaled_img = switch_img_alloc(NULL, SWITCH_IMG_FMT_I420, w, h, 16);
1764 						}
1765 					}
1766 
1767 					if (!sdiv && w) {
1768 						sdiv = (float)canvas->img->d_w / w;
1769 					}
1770 
1771 
1772 					stream->write_function(stream, "+OK Set Bandwidth for canvas %d index %d group[%s] to %d sdiv %.2f %dx%d fdiv %d\n", i + 1, j,
1773 										   switch_str_nil(canvas->write_codecs[j]->video_codec_group), video_write_bandwidth, sdiv, w,h, fdiv);
1774 
1775 
1776 					x++;
1777 				}
1778 			}
1779 		}
1780 	}
1781 	switch_mutex_unlock(conference->member_mutex);
1782 
1783  end:
1784 
1785 	if (!x) {
1786 		stream->write_function(stream, "-ERR Bandwidth not set\n");
1787 	}
1788 
1789 	return SWITCH_STATUS_SUCCESS;
1790 }
1791 
1792 
conference_api_sub_canvas_bgimg(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1793 switch_status_t conference_api_sub_canvas_bgimg(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1794 {
1795 	mcu_canvas_t *canvas = NULL;
1796 	int idx = 0;
1797 	char *file = NULL;
1798 	switch_status_t status = SWITCH_STATUS_FALSE;
1799 
1800 	if (!argv[2]) {
1801 		stream->write_function(stream, "-ERR Invalid input\n");
1802 		return SWITCH_STATUS_SUCCESS;
1803 	}
1804 
1805 	file = argv[2];
1806 
1807 	if (argv[3]) {
1808 		idx = atoi(argv[3]) - 1;
1809 	}
1810 
1811 	if (idx < 0 || idx > SUPER_CANVAS_ID || !conference->canvases[idx]) {
1812 		stream->write_function(stream, "-ERR Invalid canvas\n");
1813 		return SWITCH_STATUS_SUCCESS;
1814 	}
1815 
1816 	canvas = conference->canvases[idx];
1817 	switch_mutex_lock(canvas->mutex);
1818 	if (!strcasecmp(file, "clear")) {
1819 		conference_video_reset_image(canvas->img, &canvas->bgcolor);
1820 	} else {
1821 		status = conference_video_set_canvas_bgimg(canvas, file);
1822 	}
1823 	switch_mutex_unlock(canvas->mutex);
1824 
1825 	if (status == SWITCH_STATUS_SUCCESS) {
1826 		stream->write_function(stream, "+OK Set Bgimg %s\n", file);
1827 	} else {
1828 		stream->write_function(stream, "-ERR Error Setting Bgimg %s\n", file);
1829 	}
1830 
1831 	return SWITCH_STATUS_SUCCESS;
1832 }
1833 
1834 
conference_api_sub_canvas_fgimg(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1835 switch_status_t conference_api_sub_canvas_fgimg(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1836 {
1837 	mcu_canvas_t *canvas = NULL;
1838 	int idx = 0;
1839 	char *file = NULL;
1840 	switch_status_t status = SWITCH_STATUS_FALSE;
1841 
1842 	if (!argv[2]) {
1843 		stream->write_function(stream, "-ERR Invalid input\n");
1844 		return SWITCH_STATUS_SUCCESS;
1845 	}
1846 
1847 	file = argv[2];
1848 
1849 	if (argv[3]) {
1850 		idx = atoi(argv[3]) - 1;
1851 	}
1852 
1853 	if (idx < 0 || idx > SUPER_CANVAS_ID || !conference->canvases[idx]) {
1854 		stream->write_function(stream, "-ERR Invalid canvas\n");
1855 		return SWITCH_STATUS_SUCCESS;
1856 	}
1857 
1858 	if ((canvas = conference->canvases[idx])) {
1859 		switch_mutex_lock(canvas->mutex);
1860 		if (!strcasecmp(file, "clear")) {
1861 			conference_video_reset_image(canvas->img, &canvas->bgcolor);
1862 		} else {
1863 			status = conference_video_set_canvas_fgimg(canvas, file);
1864 		}
1865 		switch_mutex_unlock(canvas->mutex);
1866 	}
1867 
1868 	if (status == SWITCH_STATUS_SUCCESS) {
1869 		stream->write_function(stream, "+OK Set FGimg %s\n", file);
1870 	} else {
1871 		stream->write_function(stream, "-ERR Error Setting FGimg %s\n", file);
1872 	}
1873 
1874 	return SWITCH_STATUS_SUCCESS;
1875 }
1876 
conference_api_sub_vid_res(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1877 switch_status_t conference_api_sub_vid_res(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1878 {
1879 	int canvas_w = 0, canvas_h = 0, id = 0;
1880 	char *video_canvas_size = argv[2];
1881 
1882 
1883 	if (!conference->canvases[0]) {
1884 		stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
1885 		return SWITCH_STATUS_SUCCESS;
1886 	}
1887 
1888 	if (zstr(video_canvas_size)) {
1889 		stream->write_function(stream, "-ERR Invalid size\n");
1890 		return SWITCH_STATUS_SUCCESS;
1891 	} else {
1892 		char *p;
1893 
1894 		if ((canvas_w = atoi(video_canvas_size))) {
1895 			if ((p = strchr(video_canvas_size, 'x'))) {
1896 				p++;
1897 				if (*p) {
1898 					canvas_h = atoi(p);
1899 				}
1900 			}
1901 		}
1902 	}
1903 
1904 	if (canvas_w < 320 || canvas_h < 180) {
1905 		stream->write_function(stream, "-ERR Invalid size, [%dx%d] is too small\n", canvas_w, canvas_h);
1906 		return SWITCH_STATUS_SUCCESS;
1907 	}
1908 
1909 	if (canvas_w > 7680 || canvas_h > 4320) {
1910 		stream->write_function(stream, "-ERR Invalid size, [%dx%d] is too large.\n", canvas_w, canvas_h);
1911 		return SWITCH_STATUS_SUCCESS;
1912 	}
1913 
1914 
1915 	if (argv[3]) {
1916 
1917 		id = atoi(argv[3]);
1918 
1919 		if (id < 1 || id > MAX_CANVASES+1) {
1920 			id = -1;
1921 		}
1922 
1923 		if (id < 1) {
1924 			stream->write_function(stream, "-ERR Invalid canvas\n");
1925 		}
1926 
1927 	}
1928 
1929 	if (id == 0) id = 1;
1930 
1931 	if (id > conference->canvas_count + 1) {
1932 		id = 1;
1933 	}
1934 
1935 	if (conference_video_change_res(conference, canvas_w, canvas_h, id - 1) == SWITCH_STATUS_SUCCESS) {
1936 		stream->write_function(stream, "+OK Resolution set to [%dx%d]\n", canvas_w, canvas_h);
1937 	} else {
1938 		stream->write_function(stream, "-ERR Resolution not set\n");
1939 	}
1940 
1941 	return SWITCH_STATUS_SUCCESS;
1942 }
1943 
conference_api_sub_vid_fps(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1944 switch_status_t conference_api_sub_vid_fps(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1945 {
1946 	float fps = 0;
1947 
1948 	if (!conference->canvases[0]) {
1949 		stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
1950 		return SWITCH_STATUS_SUCCESS;
1951 	}
1952 
1953 	if (!argv[2]) {
1954 		stream->write_function(stream, "+OK Current FPS [%0.2f]\n", conference->video_fps.fps);
1955 		return SWITCH_STATUS_SUCCESS;
1956 	}
1957 
1958 	fps = (float)atof(argv[2]);
1959 
1960 	if (conference_video_set_fps(conference, fps)) {
1961 		stream->write_function(stream, "+OK FPS set to [%0.2f]\n", conference->video_fps.fps);
1962 	} else {
1963 		stream->write_function(stream, "-ERR Invalid FPS [%s]\n", argv[2]);
1964 	}
1965 
1966 	return SWITCH_STATUS_SUCCESS;
1967 
1968 }
1969 
conference_api_sub_write_png(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)1970 switch_status_t conference_api_sub_write_png(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
1971 {
1972 	switch_status_t status = SWITCH_STATUS_FALSE;
1973 	mcu_canvas_t *canvas = NULL;
1974 
1975 	if (!argv[2]) {
1976 		stream->write_function(stream, "-ERR Invalid input\n");
1977 		return SWITCH_STATUS_SUCCESS;
1978 	}
1979 
1980 	if (!conference->canvas_count) {
1981 		stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
1982 		return SWITCH_STATUS_SUCCESS;
1983 	}
1984 
1985 	if (conference->canvas_count > 1) {
1986 		/* pick super canvas */
1987 		canvas = conference->canvases[conference->canvas_count];
1988 	} else {
1989 		canvas = conference->canvases[0];
1990 	}
1991 
1992 	switch_mutex_lock(canvas->mutex);
1993 	status = switch_img_write_png(canvas->img, argv[2]);
1994 	switch_mutex_unlock(canvas->mutex);
1995 
1996 	stream->write_function(stream, "%s\n", status == SWITCH_STATUS_SUCCESS ? "+OK" : "-ERR");
1997 
1998 	return SWITCH_STATUS_SUCCESS;
1999 }
2000 
conference_api_sub_vid_layout(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2001 switch_status_t conference_api_sub_vid_layout(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2002 {
2003 	video_layout_t *vlayout = NULL;
2004 	char *group_name = NULL;
2005 
2006 	int idx = 0;
2007 
2008 	if (!argv[2]) {
2009 		stream->write_function(stream, "-ERR Invalid input\n");
2010 		return SWITCH_STATUS_SUCCESS;
2011 	}
2012 
2013 	if (!conference->canvases[0]) {
2014 		stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
2015 		return SWITCH_STATUS_SUCCESS;
2016 	}
2017 
2018 	if (!strcasecmp(argv[2], "list")) {
2019 		switch_hash_index_t *hi;
2020 		void *val;
2021 		const void *vvar;
2022 		for (hi = switch_core_hash_first(conference->layout_hash); hi; hi = switch_core_hash_next(&hi)) {
2023 			switch_core_hash_this(hi, &vvar, NULL, &val);
2024 			stream->write_function(stream, "%s\n", (char *)vvar);
2025 		}
2026 		return SWITCH_STATUS_SUCCESS;
2027 	}
2028 
2029 	if (!strncasecmp(argv[2], "group", 5)) {
2030 		layout_group_t *lg = NULL;
2031 		int xx = 4;
2032 
2033 		if ((group_name = strchr(argv[2], ':'))) {
2034 			group_name++;
2035 			xx--;
2036 		} else {
2037 			group_name = argv[3];
2038 		}
2039 
2040 		if (!group_name) {
2041 			stream->write_function(stream, "-ERR Group name not specified.\n");
2042 			return SWITCH_STATUS_SUCCESS;
2043 		} else {
2044 			if (((lg = switch_core_hash_find(conference->layout_group_hash, group_name)))) {
2045 				if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
2046 					stream->write_function(stream, "-ERR Change personal canvas to layout group [%s]\n", group_name);
2047 					conference->video_layout_group = switch_core_strdup(conference->pool, group_name);
2048 					conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
2049 					return SWITCH_STATUS_SUCCESS;
2050 				}
2051 			} else {
2052 				group_name = NULL;
2053 			}
2054 
2055 			stream->write_function(stream, "+OK Change to layout group [%s]\n", group_name);
2056 
2057 			if (argv[xx]) {
2058 				if ((idx = atoi(argv[xx])) > 0) {
2059 					idx--;
2060 				}
2061 			}
2062 		}
2063 	}
2064 
2065 	if ((vlayout = switch_core_hash_find(conference->layout_hash, argv[2]))) {
2066 		if (argv[3]) {
2067 			if ((idx = atoi(argv[3]))) {
2068 				idx--;
2069 			}
2070 		}
2071 	}
2072 
2073 	if (!vlayout && !group_name) {
2074 		stream->write_function(stream, "-ERR Invalid layout [%s]\n", argv[2]);
2075 		return SWITCH_STATUS_SUCCESS;
2076 	}
2077 
2078 	if (idx < 0 || idx > (int)(conference->canvas_count - 1)) idx = 0;
2079 
2080 
2081 	if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
2082 		if (vlayout) {
2083 			stream->write_function(stream, "+OK Change personal canvas set to layout [%s]\n", vlayout->name);
2084 			switch_mutex_lock(conference->member_mutex);
2085 			conference->new_personal_vlayout = vlayout;
2086 			switch_mutex_unlock(conference->member_mutex);
2087 		} else {
2088 			stream->write_function(stream, "-ERR no layout for personal canvas\n");
2089 			return SWITCH_STATUS_SUCCESS;
2090 		}
2091 	} else {
2092 
2093 		switch_mutex_lock(conference->canvases[idx]->mutex);
2094 		if (vlayout) {
2095 			stream->write_function(stream, "+OK Change canvas %d to layout [%s]\n", idx + 1, vlayout->name);
2096 			conference->canvases[idx]->new_vlayout = vlayout;
2097 			conference->canvases[idx]->video_layout_group = NULL;
2098 		} else if (group_name) {
2099 			conference->canvases[idx]->video_layout_group = switch_core_strdup(conference->pool, group_name);
2100 			conference_utils_set_flag(conference, CFLAG_REFRESH_LAYOUT);
2101 		}
2102 		switch_mutex_unlock(conference->canvases[idx]->mutex);
2103 	}
2104 
2105 	return SWITCH_STATUS_SUCCESS;
2106 }
2107 
conference_api_sub_count(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2108 switch_status_t conference_api_sub_count(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2109 {
2110 
2111 	if (conference) {
2112 		conference_list_count_only(conference, stream);
2113 	} else {
2114 		int count = 0;
2115 		switch_hash_index_t *hi;
2116 		switch_mutex_lock(conference_globals.hash_mutex);
2117 		for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
2118 			count++;
2119 		}
2120 		switch_mutex_unlock(conference_globals.hash_mutex);
2121 		stream->write_function(stream, "%d", count);
2122 	}
2123 
2124 	return SWITCH_STATUS_SUCCESS;
2125 }
2126 
conference_api_sub_list(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2127 switch_status_t conference_api_sub_list(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2128 {
2129 	int ret_status = SWITCH_STATUS_GENERR;
2130 	int count = 0;
2131 	switch_hash_index_t *hi;
2132 	void *val;
2133 	char *d = ";";
2134 	int pretty = 0;
2135 	int summary = 0;
2136 	int countonly = 0;
2137 	int argofs = (argc >= 2 && strcasecmp(argv[1], "list") == 0);	/* detect being called from chat vs. api */
2138 
2139 	if (argv[1 + argofs]) {
2140 		if (argv[2 + argofs] && !strcasecmp(argv[1 + argofs], "delim")) {
2141 			d = argv[2 + argofs];
2142 
2143 			if (*d == '"') {
2144 				if (++d) {
2145 					char *p;
2146 					if ((p = strchr(d, '"'))) {
2147 						*p = '\0';
2148 					}
2149 				} else {
2150 					d = ";";
2151 				}
2152 			}
2153 		} else if (strcasecmp(argv[1 + argofs], "pretty") == 0) {
2154 			pretty = 1;
2155 		} else if (strcasecmp(argv[1 + argofs], "summary") == 0) {
2156 			summary = 1;
2157 		} else if (strcasecmp(argv[1 + argofs], "count") == 0) {
2158 			countonly = 1;
2159 		}
2160 	}
2161 
2162 	if (conference == NULL) {
2163 		switch_mutex_lock(conference_globals.hash_mutex);
2164 		for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
2165 			int fcount = 0;
2166 			switch_core_hash_this(hi, NULL, NULL, &val);
2167 			conference = (conference_obj_t *) val;
2168 
2169 			stream->write_function(stream, "+OK Conference %s (%u member%s rate: %u%s flags: ",
2170 								   conference->name,
2171 								   conference->count,
2172 								   conference->count == 1 ? "" : "s", conference->rate, conference_utils_test_flag(conference, CFLAG_LOCKED) ? " locked" : "");
2173 
2174 			if (conference_utils_test_flag(conference, CFLAG_LOCKED)) {
2175 				stream->write_function(stream, "%slocked", fcount ? "|" : "");
2176 				fcount++;
2177 			}
2178 
2179 			if (conference_utils_test_flag(conference, CFLAG_DESTRUCT)) {
2180 				stream->write_function(stream, "%sdestruct", fcount ? "|" : "");
2181 				fcount++;
2182 			}
2183 
2184 			if (conference_utils_test_flag(conference, CFLAG_WAIT_MOD)) {
2185 				stream->write_function(stream, "%swait_mod", fcount ? "|" : "");
2186 				fcount++;
2187 			}
2188 
2189 			if (conference_utils_test_flag(conference, CFLAG_AUDIO_ALWAYS)) {
2190 				stream->write_function(stream, "%saudio_always", fcount ? "|" : "");
2191 				fcount++;
2192 			}
2193 
2194 			if (conference_utils_test_flag(conference, CFLAG_RUNNING)) {
2195 				stream->write_function(stream, "%srunning", fcount ? "|" : "");
2196 				fcount++;
2197 			}
2198 
2199 			if (conference_utils_test_flag(conference, CFLAG_ANSWERED)) {
2200 				stream->write_function(stream, "%sanswered", fcount ? "|" : "");
2201 				fcount++;
2202 			}
2203 
2204 			if (conference_utils_test_flag(conference, CFLAG_ENFORCE_MIN)) {
2205 				stream->write_function(stream, "%senforce_min", fcount ? "|" : "");
2206 				fcount++;
2207 			}
2208 
2209 			if (conference_utils_test_flag(conference, CFLAG_BRIDGE_TO)) {
2210 				stream->write_function(stream, "%sbridge_to", fcount ? "|" : "");
2211 				fcount++;
2212 			}
2213 
2214 			if (conference_utils_test_flag(conference, CFLAG_DYNAMIC)) {
2215 				stream->write_function(stream, "%sdynamic", fcount ? "|" : "");
2216 				fcount++;
2217 			}
2218 
2219 			if (conference_utils_test_flag(conference, CFLAG_EXIT_SOUND)) {
2220 				stream->write_function(stream, "%sexit_sound", fcount ? "|" : "");
2221 				fcount++;
2222 			}
2223 
2224 			if (conference_utils_test_flag(conference, CFLAG_ENTER_SOUND)) {
2225 				stream->write_function(stream, "%senter_sound", fcount ? "|" : "");
2226 				fcount++;
2227 			}
2228 
2229 			if (conference->record_count > 0) {
2230 				stream->write_function(stream, "%srecording", fcount ? "|" : "");
2231 				fcount++;
2232 			}
2233 
2234 			if (conference_utils_test_flag(conference, CFLAG_VID_FLOOR)) {
2235 				stream->write_function(stream, "%svideo_floor_only", fcount ? "|" : "");
2236 				fcount++;
2237 			}
2238 
2239 			if (conference_utils_test_flag(conference, CFLAG_RFC4579)) {
2240 				stream->write_function(stream, "%svideo_rfc4579", fcount ? "|" : "");
2241 				fcount++;
2242 			}
2243 
2244 			if (conference_utils_test_flag(conference, CFLAG_LIVEARRAY_SYNC)) {
2245 				stream->write_function(stream, "%slivearray_sync", fcount ? "|" : "");
2246 				fcount++;
2247 			}
2248 
2249 			if (conference_utils_test_flag(conference, CFLAG_VID_FLOOR_LOCK)) {
2250 				stream->write_function(stream, "%svideo_floor_lock", fcount ? "|" : "");
2251 				fcount++;
2252 			}
2253 
2254 			if (conference_utils_test_flag(conference, CFLAG_TRANSCODE_VIDEO)) {
2255 				stream->write_function(stream, "%stranscode_video", fcount ? "|" : "");
2256 				fcount++;
2257 			}
2258 
2259 			if (conference_utils_test_flag(conference, CFLAG_VIDEO_MUXING)) {
2260 				stream->write_function(stream, "%svideo_muxing", fcount ? "|" : "");
2261 				fcount++;
2262 			}
2263 
2264 			if (conference_utils_test_flag(conference, CFLAG_MINIMIZE_VIDEO_ENCODING)) {
2265 				stream->write_function(stream, "%sminimize_video_encoding", fcount ? "|" : "");
2266 				fcount++;
2267 			}
2268 
2269 			if (conference_utils_test_flag(conference, CFLAG_MANAGE_INBOUND_VIDEO_BITRATE)) {
2270 				stream->write_function(stream, "%smanage_inbound_bitrate", fcount ? "|" : "");
2271 				fcount++;
2272 			}
2273 
2274 			if (conference_utils_test_flag(conference, CFLAG_JSON_STATUS)) {
2275 				stream->write_function(stream, "%sjson_status", fcount ? "|" : "");
2276 				fcount++;
2277 			}
2278 
2279 			if (conference_utils_test_flag(conference, CFLAG_VIDEO_BRIDGE_FIRST_TWO)) {
2280 				stream->write_function(stream, "%svideo_bridge_first_two", fcount ? "|" : "");
2281 				fcount++;
2282 			}
2283 
2284 			if (conference_utils_test_flag(conference, CFLAG_VIDEO_REQUIRED_FOR_CANVAS)) {
2285 				stream->write_function(stream, "%svideo_required_for_canvas", fcount ? "|" : "");
2286 				fcount++;
2287 			}
2288 
2289 			if (conference_utils_test_flag(conference, CFLAG_PERSONAL_CANVAS)) {
2290 				stream->write_function(stream, "%spersonal_canvas", fcount ? "|" : "");
2291 				fcount++;
2292 			}
2293 
2294 			if (!fcount) {
2295 				stream->write_function(stream, "none");
2296 			}
2297 
2298 			stream->write_function(stream, ")\n");
2299 
2300 			count++;
2301 			if (!summary) {
2302 				if (pretty) {
2303 					conference_list_pretty(conference, stream);
2304 				} else {
2305 					conference_list(conference, stream, d);
2306 				}
2307 			}
2308 		}
2309 		switch_mutex_unlock(conference_globals.hash_mutex);
2310 	} else {
2311 		count++;
2312 		if (countonly) {
2313 			conference_list_count_only(conference, stream);
2314 		} else if (pretty) {
2315 			conference_list_pretty(conference, stream);
2316 		} else {
2317 			conference_list(conference, stream, d);
2318 		}
2319 	}
2320 
2321 	if (!count) {
2322 		stream->write_function(stream, "+OK No active conferences.\n");
2323 	}
2324 
2325 	ret_status = SWITCH_STATUS_SUCCESS;
2326 
2327 	return ret_status;
2328 }
2329 
conference_api_sub_floor(conference_member_t * member,switch_stream_handle_t * stream,void * data)2330 switch_status_t conference_api_sub_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2331 {
2332 
2333 	if (member == NULL)
2334 		return SWITCH_STATUS_GENERR;
2335 
2336 	if (conference_utils_member_test_flag(member, MFLAG_DED_VID_LAYER) && !conference_utils_test_flag(member->conference, CFLAG_DED_VID_LAYER_AUDIO_FLOOR)) {
2337 		if (stream != NULL) {
2338 			stream->write_function(stream, "-ERR cannot set floor on a member in an active video role\n");
2339 		}
2340 
2341 		return SWITCH_STATUS_SUCCESS;
2342 	}
2343 
2344 	if (member->conference->floor_holder == member->id) {
2345 		conference_member_set_floor_holder(member->conference, NULL, 0);
2346 		if (stream != NULL) {
2347 			stream->write_function(stream, "+OK floor none\n");
2348 		}
2349 	} else if (member->conference->floor_holder == 0) {
2350 		conference_member_set_floor_holder(member->conference, member, 0);
2351 		if (stream != NULL) {
2352 			stream->write_function(stream, "+OK floor %u\n", member->id);
2353 		}
2354 	} else {
2355 		if (stream != NULL) {
2356 			stream->write_function(stream, "-ERR floor is held by %u\n", member->conference->floor_holder);
2357 		}
2358 	}
2359 
2360 	return SWITCH_STATUS_SUCCESS;
2361 }
2362 
conference_api_sub_clear_vid_floor(conference_obj_t * conference,switch_stream_handle_t * stream,void * data)2363 switch_status_t conference_api_sub_clear_vid_floor(conference_obj_t *conference, switch_stream_handle_t *stream, void *data)
2364 {
2365 
2366 	switch_mutex_lock(conference->mutex);
2367 	conference_utils_clear_flag(conference, CFLAG_VID_FLOOR_LOCK);
2368 	//conference_video_set_floor_holder(conference, NULL);
2369 	switch_mutex_unlock(conference->mutex);
2370 
2371 	stream->write_function(stream, "+OK floor Cleared\n", SWITCH_VA_NONE);
2372 
2373 	return SWITCH_STATUS_SUCCESS;
2374 }
2375 
conference_api_sub_vid_mute_img(conference_member_t * member,switch_stream_handle_t * stream,void * data)2376 switch_status_t conference_api_sub_vid_mute_img(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2377 {
2378 	char *text = (char *) data;
2379 	mcu_layer_t *layer = NULL;
2380 
2381 	if (member == NULL)
2382 		return SWITCH_STATUS_GENERR;
2383 
2384 	if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2385 		return SWITCH_STATUS_FALSE;
2386 	}
2387 
2388 	layer = conference_video_get_layer_locked(member);
2389 
2390 	if (!layer) {
2391 		goto end;
2392 	}
2393 
2394 	member->video_mute_png = NULL;
2395 
2396 	if (text) {
2397 		switch_img_free(&layer->mute_img);
2398 	}
2399 
2400 	if (text && strcasecmp(text, "clear")) {
2401 		member->video_mute_png = switch_core_strdup(member->pool, text);
2402 	}
2403 
2404 end:
2405 
2406 	stream->write_function(stream, "%s\n", member->video_mute_png ? member->video_mute_png : "_undef_");
2407 
2408 	conference_video_release_layer(&layer);
2409 
2410 	return SWITCH_STATUS_SUCCESS;
2411 
2412 }
2413 
2414 
conference_api_sub_vid_logo_img(conference_member_t * member,switch_stream_handle_t * stream,void * data)2415 switch_status_t conference_api_sub_vid_logo_img(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2416 {
2417 	char *text = (char *) data;
2418 	mcu_layer_t *layer = NULL;
2419 
2420 	if (member == NULL)
2421 		return SWITCH_STATUS_GENERR;
2422 
2423 	if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2424 		return SWITCH_STATUS_FALSE;
2425 	}
2426 
2427 	conference_member_set_logo(member, text);
2428 
2429 	layer = conference_video_get_layer_locked(member);
2430 
2431 	if (!layer) {
2432 		goto end;
2433 	}
2434 
2435 	conference_video_layer_set_logo(member, layer);
2436 
2437  end:
2438 
2439 	stream->write_function(stream, "+OK Video logo %s\n", member->video_logo ? "set" : "cleared");
2440 
2441 	conference_video_release_layer(&layer);
2442 
2443 	return SWITCH_STATUS_SUCCESS;
2444 
2445 }
2446 
2447 
conference_api_sub_vid_codec_group(conference_member_t * member,switch_stream_handle_t * stream,void * data)2448 switch_status_t conference_api_sub_vid_codec_group(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2449 {
2450 	char *text = (char *) data;
2451 
2452 	if (member == NULL)
2453 		return SWITCH_STATUS_GENERR;
2454 
2455 	if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2456 		return SWITCH_STATUS_FALSE;
2457 	}
2458 
2459 	if (text) {
2460 
2461 		if (!strcmp(text, "clear")) {
2462 			member->video_codec_group = NULL;
2463 		} else {
2464 			member->video_codec_group = switch_core_strdup(member->pool, text);
2465 		}
2466 
2467 		switch_mutex_lock(member->conference->member_mutex);
2468 		member->video_codec_index = -1;
2469 		switch_mutex_unlock(member->conference->member_mutex);
2470 		stream->write_function(stream, "+OK Video codec group %s %s\n", member->video_codec_group ? "set" : "cleared", switch_str_nil(member->video_codec_group));
2471 	} else {
2472 		stream->write_function(stream, "+OK Video codec group is %s\n", member->video_codec_group);
2473 	}
2474 
2475 
2476 	return SWITCH_STATUS_SUCCESS;
2477 
2478 }
2479 
conference_api_sub_get_uuid(conference_member_t * member,switch_stream_handle_t * stream,void * data)2480 switch_status_t conference_api_sub_get_uuid(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2481 {
2482 	if (member->session) {
2483 		stream->write_function(stream, "%s", switch_core_session_get_uuid(member->session));
2484 	} else {
2485 		stream->write_function(stream, "_undef_");
2486 	}
2487 
2488 	return SWITCH_STATUS_SUCCESS;
2489 }
2490 
clear_res_id(conference_obj_t * conference,conference_member_t * member,const char * id)2491 static void clear_res_id(conference_obj_t *conference, conference_member_t *member, const char *id)
2492 {
2493 	conference_member_t *imember;
2494 
2495 	switch_mutex_lock(conference->member_mutex);
2496 	for (imember = conference->members; imember; imember = imember->next) {
2497 		if (imember == member) {
2498 			continue;
2499 		}
2500 
2501 		if (imember->video_reservation_id && !strcasecmp(imember->video_reservation_id, id)) {
2502 			imember->video_reservation_id = NULL;
2503 			conference_video_detach_video_layer(imember);
2504 		}
2505 	}
2506 	switch_mutex_unlock(conference->member_mutex);
2507 }
2508 
clear_role_id(conference_obj_t * conference,conference_member_t * member,const char * id)2509 static void clear_role_id(conference_obj_t *conference, conference_member_t *member, const char *id)
2510 {
2511 	conference_member_t *imember;
2512 
2513 	switch_mutex_lock(conference->member_mutex);
2514 	for (imember = conference->members; imember; imember = imember->next) {
2515 		if (imember == member) {
2516 			continue;
2517 		}
2518 
2519 		if (imember->video_role_id && !strcasecmp(imember->video_role_id, id)) {
2520 			imember->video_role_id = NULL;
2521 			conference_video_detach_video_layer(imember);
2522 		}
2523 	}
2524 	switch_mutex_unlock(conference->member_mutex);
2525 }
2526 
conference_api_sub_vid_res_id(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2527 switch_status_t conference_api_sub_vid_res_id(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2528 {
2529 	uint8_t all = 0, clear = 0, force = 0;
2530 	uint32_t member_id;
2531 	char *res_id = NULL;
2532 	conference_member_t *member;
2533 
2534 	if (argc < 3 || argc > 5)
2535 		return SWITCH_STATUS_GENERR;
2536 
2537 	res_id = argv[3];
2538 
2539 	if (argc > 3) {
2540 		if (!strcasecmp(res_id, "clear")) {
2541 			clear = 1;
2542 		}
2543 	} else {
2544 		clear = 1;
2545 	}
2546 
2547 	if (argc > 4)
2548 		force = strcasecmp(argv[4], "force") ? 0 : 1;
2549 
2550 	if (!(member_id = atoi(argv[2]))) {
2551 		all = strcasecmp(argv[2], "all") ? 0 : 1;
2552 	}
2553 
2554 	if (all && clear) {
2555 		switch_mutex_lock(conference->member_mutex);
2556 		for (member = conference->members; member; member = member->next) {
2557 			if (member->session && !conference_utils_member_test_flag(member, MFLAG_NOCHANNEL)) {
2558 				conference_api_sub_vid_res_id_member(member, stream, res_id, clear, force);
2559 			}
2560 		}
2561 		switch_mutex_unlock(conference->member_mutex);
2562 	} else if (member_id) {
2563 		if (!(member = conference_member_get(conference, member_id)))
2564 			return SWITCH_STATUS_GENERR;
2565 		conference_api_sub_vid_res_id_member(member, stream, res_id, clear, force);
2566 		switch_thread_rwlock_unlock(member->rwlock);
2567 	} else {
2568 		return SWITCH_STATUS_GENERR;
2569 	}
2570 
2571 	return SWITCH_STATUS_SUCCESS;
2572 
2573 }
2574 
2575 
conference_api_sub_vid_res_id_member(conference_member_t * member,switch_stream_handle_t * stream,char * res_id,int clear,int force)2576 switch_status_t conference_api_sub_vid_res_id_member(conference_member_t *member, switch_stream_handle_t *stream, char *res_id, int clear, int force)
2577 {
2578 
2579 	if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2580 		return SWITCH_STATUS_FALSE;
2581 	}
2582 
2583 	if (!member->conference->canvases[0]) {
2584 		stream->write_function(stream, "-ERR conference is not in mixing mode\n");
2585 		return SWITCH_STATUS_SUCCESS;
2586 	}
2587 
2588 	if (clear || (!force && member->video_reservation_id && !strcasecmp(res_id, member->video_reservation_id))) {
2589 		member->video_reservation_id = NULL;
2590 		stream->write_function(stream, "+OK reservation_id cleared\n");
2591 		conference_video_detach_video_layer(member);
2592 	} else {
2593 		clear_res_id(member->conference, member, res_id);
2594 		if (!member->video_reservation_id || strcmp(member->video_reservation_id, res_id)) {
2595 			member->video_reservation_id = switch_core_strdup(member->pool, res_id);
2596 		}
2597 		stream->write_function(stream, "+OK reservation_id %s\n", res_id);
2598 		conference_video_detach_video_layer(member);
2599 		conference_video_find_floor(member, SWITCH_FALSE);
2600 	}
2601 
2602 	return SWITCH_STATUS_SUCCESS;
2603 
2604 }
2605 
2606 
conference_api_sub_vid_role_id(conference_member_t * member,switch_stream_handle_t * stream,void * data)2607 switch_status_t conference_api_sub_vid_role_id(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2608 {
2609 	char *text = (char *) data;
2610 	int force = 0;
2611 
2612 	if (member == NULL)
2613 		return SWITCH_STATUS_GENERR;
2614 
2615 	if (!switch_channel_test_flag(member->channel, CF_VIDEO)) {
2616 		return SWITCH_STATUS_FALSE;
2617 	}
2618 
2619 	if (!member->conference->canvases[0]) {
2620 		stream->write_function(stream, "-ERR conference is not in mixing mode\n");
2621 		return SWITCH_STATUS_SUCCESS;
2622 	}
2623 
2624 	if (!zstr(text) && *text == '=') {
2625 		text++;
2626 		force = 1;
2627 	}
2628 
2629 	if (zstr(text) || !strcasecmp(text, "clear") || (!force && member->video_role_id && !strcasecmp(text, member->video_role_id))) {
2630 		member->video_role_id = NULL;
2631 		stream->write_function(stream, "+OK role_id cleared\n");
2632 	} else {
2633 		clear_role_id(member->conference, member, text);
2634 		if (!member->video_role_id || strcmp(member->video_role_id, text)) {
2635 			member->video_role_id = switch_core_strdup(member->pool, text);
2636 		}
2637 		stream->write_function(stream, "+OK role_id %s\n", text);
2638 		conference_video_find_floor(member, SWITCH_FALSE);
2639 	}
2640 
2641 
2642 
2643 	return SWITCH_STATUS_SUCCESS;
2644 
2645 }
2646 
conference_api_sub_vid_banner(conference_member_t * member,switch_stream_handle_t * stream,void * data)2647 switch_status_t conference_api_sub_vid_banner(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2648 {
2649 	mcu_layer_t *layer = NULL;
2650 	char *text = (char *) data;
2651 
2652 	if (member == NULL) {
2653 		return SWITCH_STATUS_GENERR;
2654 	}
2655 
2656 	switch_url_decode(text);
2657 
2658  	if (zstr(text)) goto end;
2659 
2660 	member->video_banner_text = switch_core_strdup(member->pool, text);
2661 
2662 	layer = conference_video_get_layer_locked(member);
2663 
2664 	if (!layer) goto end;
2665 
2666 	conference_video_layer_set_banner(member, layer, NULL);
2667 
2668  end:
2669 
2670 	stream->write_function(stream, "+OK\n");
2671 
2672 	conference_video_release_layer(&layer);
2673 
2674 	return SWITCH_STATUS_SUCCESS;
2675 }
2676 
conference_api_sub_vid_floor(conference_member_t * member,switch_stream_handle_t * stream,void * data)2677 switch_status_t conference_api_sub_vid_floor(conference_member_t *member, switch_stream_handle_t *stream, void *data)
2678 {
2679 	int force = 0;
2680 
2681 	if (member == NULL)
2682 		return SWITCH_STATUS_GENERR;
2683 
2684 	if (!switch_channel_test_flag(member->channel, CF_VIDEO) && !member->avatar_png_img) {
2685 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Channel %s does not have video capability!\n", switch_channel_get_name(member->channel));
2686 		return SWITCH_STATUS_FALSE;
2687 	}
2688 
2689 	if (conference_utils_member_test_flag(member, MFLAG_DED_VID_LAYER)) {
2690 		if (stream != NULL) {
2691 			stream->write_function(stream, "-ERR cannot set floor on a member in an active video role\n");
2692 		}
2693 
2694 		return SWITCH_STATUS_SUCCESS;
2695 	}
2696 
2697 	if (data && switch_stristr("force", (char *) data)) {
2698 		force = 1;
2699 	}
2700 
2701 	if (member->conference->video_floor_holder == member->id && conference_utils_test_flag(member->conference, CFLAG_VID_FLOOR_LOCK)) {
2702 		conference_utils_clear_flag(member->conference, CFLAG_VID_FLOOR_LOCK);
2703 
2704 		conference_member_set_floor_holder(member->conference, member, 0);
2705 		if (stream == NULL) {
2706 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK video floor auto\n", member->conference->name);
2707 		} else {
2708 			stream->write_function(stream, "+OK floor none\n");
2709 		}
2710 
2711 	} else if (force || member->conference->video_floor_holder == 0) {
2712 		conference_utils_set_flag(member->conference, CFLAG_VID_FLOOR_LOCK);
2713 		conference_video_set_floor_holder(member->conference, member, SWITCH_TRUE);
2714 		if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) {
2715 			if (stream == NULL) {
2716 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s OK video floor %d %s\n",
2717 								  member->conference->name, member->id, switch_channel_get_name(member->channel));
2718 			} else {
2719 				stream->write_function(stream, "+OK floor %u\n", member->id);
2720 			}
2721 		}
2722 	} else {
2723 		if (stream == NULL) {
2724 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "conference %s floor already held by %d %s\n",
2725 							  member->conference->name, member->id, switch_channel_get_name(member->channel));
2726 		} else {
2727 			stream->write_function(stream, "-ERR floor is held by %u\n", member->conference->video_floor_holder);
2728 		}
2729 	}
2730 
2731 	return SWITCH_STATUS_SUCCESS;
2732 }
2733 
2734 
2735 
conference_api_sub_file_seek(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2736 switch_status_t conference_api_sub_file_seek(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2737 {
2738 	if (argc == 3) {
2739 		switch_mutex_lock(conference->mutex);
2740 		conference_fnode_seek(conference->fnode, stream, argv[2]);
2741 		switch_mutex_unlock(conference->mutex);
2742 
2743 		return SWITCH_STATUS_SUCCESS;
2744 	}
2745 
2746 	if (argc == 4) {
2747 		uint32_t id = atoi(argv[3]);
2748 		conference_member_t *member = conference_member_get(conference, id);
2749 		if (member == NULL) {
2750 			stream->write_function(stream, "-ERR Member: %u not found.\n", id);
2751 			return SWITCH_STATUS_GENERR;
2752 		}
2753 
2754 		switch_mutex_lock(member->fnode_mutex);
2755 		conference_fnode_seek(member->fnode, stream, argv[2]);
2756 		switch_mutex_unlock(member->fnode_mutex);
2757 		switch_thread_rwlock_unlock(member->rwlock);
2758 		return SWITCH_STATUS_SUCCESS;
2759 	}
2760 
2761 	return SWITCH_STATUS_GENERR;
2762 }
2763 
conference_api_set_moh(conference_obj_t * conference,const char * what)2764 switch_status_t conference_api_set_moh(conference_obj_t *conference, const char *what)
2765 {
2766 	if (!what) {
2767 		return SWITCH_STATUS_FALSE;
2768 	}
2769 
2770 	if (!strcasecmp(what, "toggle")) {
2771 		if (conference_utils_test_flag(conference, CFLAG_NO_MOH)) {
2772 			conference_utils_clear_flag(conference, CFLAG_NO_MOH);
2773 		} else {
2774 			conference_utils_set_flag(conference, CFLAG_NO_MOH);
2775 		}
2776 	} else if (!strcasecmp(what, "on")) {
2777 		conference_utils_clear_flag(conference, CFLAG_NO_MOH);
2778 	} else if (!strcasecmp(what, "off")) {
2779 		conference_utils_set_flag(conference, CFLAG_NO_MOH);
2780 	} else if (!strcasecmp(what, "reset")) {
2781 		conference->tmp_moh_sound = NULL;
2782 	} else {
2783 		conference->tmp_moh_sound = switch_core_strdup(conference->pool, what);
2784 	}
2785 
2786 	if (conference_utils_test_flag(conference, CFLAG_NO_MOH) || conference->tmp_moh_sound) {
2787 		conference_file_stop(conference, FILE_STOP_ASYNC);
2788 	}
2789 
2790 	return SWITCH_STATUS_SUCCESS;
2791 }
2792 
2793 
conference_api_sub_moh(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2794 switch_status_t conference_api_sub_moh(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2795 {
2796 
2797 	if (conference_api_set_moh(conference, argv[2]) == SWITCH_STATUS_SUCCESS) {
2798 		if (stream) {
2799 			stream->write_function(stream, "+OK moh\n");
2800 		}
2801 	} else {
2802 		if (stream) {
2803 			stream->write_function(stream, "-ERR invalid moh param\n");
2804 		}
2805 	}
2806 
2807 	return SWITCH_STATUS_SUCCESS;
2808 }
2809 
conference_api_sub_play(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2810 switch_status_t conference_api_sub_play(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2811 {
2812 	int ret_status = SWITCH_STATUS_GENERR;
2813 	switch_event_t *event;
2814 	uint8_t async = 0;
2815 
2816 	switch_assert(conference != NULL);
2817 	switch_assert(stream != NULL);
2818 
2819 	if ((argc == 4 && !strcasecmp(argv[3], "async")) || (argc == 5 && !strcasecmp(argv[4], "async"))) {
2820 		argc--;
2821 		async++;
2822 	}
2823 
2824 	if (argc == 3) {
2825 		if (conference_file_play(conference, argv[2], 0, NULL, async) == SWITCH_STATUS_SUCCESS) {
2826 			stream->write_function(stream, "+OK (play) Playing file %s\n", argv[2]);
2827 			if (test_eflag(conference, EFLAG_PLAY_FILE) &&
2828 				switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2829 				conference_event_add_data(conference, event);
2830 
2831 				if (conference->fnode && conference->fnode->fh.params) {
2832 					switch_event_merge(event, conference->fnode->fh.params);
2833 				}
2834 
2835 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "play-file");
2836 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
2837 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Async", async ? "true" : "false");
2838 				switch_event_fire(&event);
2839 			}
2840 		} else {
2841 			stream->write_function(stream, "-ERR (play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
2842 		}
2843 		ret_status = SWITCH_STATUS_SUCCESS;
2844 	} else if (argc >= 4) {
2845 		uint32_t id = atoi(argv[3]);
2846 		conference_member_t *member;
2847 		switch_bool_t mux = SWITCH_TRUE;
2848 
2849 		if (argc > 4 && !strcasecmp(argv[4], "nomux")) {
2850 			mux = SWITCH_FALSE;
2851 		}
2852 
2853 		if ((member = conference_member_get(conference, id))) {
2854 			if (conference_member_play_file(member, argv[2], 0, mux) == SWITCH_STATUS_SUCCESS) {
2855 				stream->write_function(stream, "+OK (play) Playing file %s to member %u\n", argv[2], id);
2856 				if (test_eflag(conference, EFLAG_PLAY_FILE_MEMBER) &&
2857 					switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2858 					conference_member_add_event_data(member, event);
2859 
2860 					if (member->fnode->fh.params) {
2861 						switch_event_merge(event, member->fnode->fh.params);
2862 					}
2863 
2864 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "play-file-member");
2865 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "File", argv[2]);
2866 					switch_event_fire(&event);
2867 				}
2868 			} else {
2869 				stream->write_function(stream, "-ERR (play) File: %s not found.\n", argv[2] ? argv[2] : "(unspecified)");
2870 			}
2871 			switch_thread_rwlock_unlock(member->rwlock);
2872 			ret_status = SWITCH_STATUS_SUCCESS;
2873 		} else {
2874 			stream->write_function(stream, "-ERR Member: %u not found.\n", id);
2875 		}
2876 	}
2877 
2878 	return ret_status;
2879 }
2880 
conference_api_sub_say(conference_obj_t * conference,switch_stream_handle_t * stream,const char * text)2881 switch_status_t conference_api_sub_say(conference_obj_t *conference, switch_stream_handle_t *stream, const char *text)
2882 {
2883 	switch_event_t *event;
2884 
2885 	if (zstr(text)) {
2886 		stream->write_function(stream, "-ERR (say) Error! No text.\n");
2887 		return SWITCH_STATUS_GENERR;
2888 	}
2889 
2890 	if (conference_say(conference, text, 0) != SWITCH_STATUS_SUCCESS) {
2891 		stream->write_function(stream, "-ERR (say) Error!\n");
2892 		return SWITCH_STATUS_GENERR;
2893 	}
2894 
2895 	stream->write_function(stream, "+OK (say) OK\n");
2896 	if (test_eflag(conference, EFLAG_SPEAK_TEXT) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2897 		conference_event_add_data(conference, event);
2898 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "speak-text");
2899 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Text", text);
2900 		switch_event_fire(&event);
2901 	}
2902 	return SWITCH_STATUS_SUCCESS;
2903 }
2904 
conference_api_sub_saymember(conference_obj_t * conference,switch_stream_handle_t * stream,const char * text)2905 switch_status_t conference_api_sub_saymember(conference_obj_t *conference, switch_stream_handle_t *stream, const char *text)
2906 {
2907 	int ret_status = SWITCH_STATUS_GENERR;
2908 	char *expanded = NULL;
2909 	char *start_text = NULL;
2910 	char *workspace = NULL;
2911 	uint32_t id = 0;
2912 	conference_member_t *member = NULL;
2913 	switch_event_t *event;
2914 
2915 	if (zstr(text)) {
2916 		stream->write_function(stream, "-ERR (saymember) No Text!\n");
2917 		goto done;
2918 	}
2919 
2920 	if (!(workspace = strdup(text))) {
2921 		stream->write_function(stream, "-ERR (saymember) Memory Error!\n");
2922 		goto done;
2923 	}
2924 
2925 	if ((start_text = strchr(workspace, ' '))) {
2926 		*start_text++ = '\0';
2927 		text = start_text;
2928 	}
2929 
2930 	id = atoi(workspace);
2931 
2932 	if (!id || zstr(text)) {
2933 		stream->write_function(stream, "-ERR (saymember) No Text!\n");
2934 		goto done;
2935 	}
2936 
2937 	if (!(member = conference_member_get(conference, id))) {
2938 		stream->write_function(stream, "-ERR (saymember) Unknown Member %u!\n", id);
2939 		goto done;
2940 	}
2941 
2942 	if ((expanded = switch_channel_expand_variables(switch_core_session_get_channel(member->session), (char *) text)) != text) {
2943 		text = expanded;
2944 	} else {
2945 		expanded = NULL;
2946 	}
2947 
2948 	if (!text || conference_member_say(member, (char *) text, 0) != SWITCH_STATUS_SUCCESS) {
2949 		stream->write_function(stream, "-ERR (saymember) Error!\n");
2950 		goto done;
2951 	}
2952 
2953 	stream->write_function(stream, "-ERR (saymember) OK\n");
2954 	if (test_eflag(member->conference, EFLAG_SPEAK_TEXT_MEMBER) &&
2955 		switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
2956 		conference_member_add_event_data(member, event);
2957 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "speak-text-member");
2958 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Text", text);
2959 		switch_event_fire(&event);
2960 	}
2961 	ret_status = SWITCH_STATUS_SUCCESS;
2962 
2963  done:
2964 
2965 	if (member) {
2966 		switch_thread_rwlock_unlock(member->rwlock);
2967 	}
2968 
2969 	switch_safe_free(workspace);
2970 	switch_safe_free(expanded);
2971 	return ret_status;
2972 }
2973 
conference_api_sub_cam(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)2974 switch_status_t conference_api_sub_cam(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
2975 {
2976 	int x;
2977 	int canvas_id = -1;
2978 	int layer_id = -1;
2979 	int ok = 0;
2980 	mcu_canvas_t *canvas = NULL;
2981 	mcu_layer_t *layer = NULL;
2982 
2983 	if (!conference->canvases[0]) {
2984 		stream->write_function(stream, "-ERR Conference is not in mixing mode\n");
2985 		return SWITCH_STATUS_SUCCESS;
2986 	}
2987 
2988 	if (argc > 4) {
2989 		canvas_id = atoi(argv[2]);
2990 		layer_id = atoi(argv[3]);
2991 
2992 		if (canvas_id > -1 && layer_id > -1 && canvas_id < conference->canvas_count) {
2993 			switch_mutex_lock(conference->canvas_mutex);
2994 			canvas = conference->canvases[canvas_id];
2995 			switch_mutex_lock(canvas->mutex);
2996 			if (layer_id < canvas->total_layers) {
2997 				layer = &canvas->layers[layer_id];
2998 				ok = 1;
2999 				for (x = 4; x < argc; x++) {
3000 					char *p = strchr(argv[x], '=');
3001 					int val = -1, isfalse = 0;
3002 					char *str_arg = NULL;
3003 
3004 					if (p) {
3005 						*p++ = '\0';
3006 
3007 						if (!strcasecmp(argv[x], "zoom") || !strcasecmp(argv[x], "pan")) {
3008 							str_arg = p;
3009 							if (switch_false(p)) {
3010 								isfalse = 1;
3011 							}
3012 						} else {
3013 							if (switch_is_number(p)) {
3014 								val = atoi(p);
3015 							} else if (switch_true(p)) {
3016 								val = 1;
3017 							} else {
3018 								val = 0;
3019 								isfalse = 1;
3020 							}
3021 						}
3022 					} else if (!strcasecmp(argv[x], "reset")) {
3023 						str_arg = "true";
3024 					}
3025 
3026 					if (val < 0 && !str_arg) {
3027 						stream->write_function(stream, "-ERR invalid val for option [%s]\n", argv[x]);
3028 						continue;
3029 					}
3030 
3031 					if (!strcasecmp(argv[x], "autozoom")) {
3032 						if ((layer->cam_opts.autozoom = val)) {
3033 							layer->cam_opts.manual_zoom = 0;
3034 						}
3035 
3036 					} else if (!strcasecmp(argv[x], "autopan")) {
3037 						if ((layer->cam_opts.autopan = val)) {
3038 							layer->cam_opts.manual_pan = 0;
3039 						}
3040 					} else if (!strcasecmp(argv[x], "zoom_factor")) {
3041 						if (val > 0 && val < 5) {
3042 							layer->cam_opts.zoom_factor = val;
3043 						} else {
3044 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-4\n", argv[x]);
3045 						}
3046 					} else if (!strcasecmp(argv[x], "snap_factor")) {
3047 						if (val > 0 && val < layer->screen_w / 2) {
3048 							layer->cam_opts.snap_factor = val;
3049 						} else {
3050 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-%d\n", argv[x], layer->screen_w / 2);
3051 						}
3052 					} else if (!strcasecmp(argv[x], "zoom_move_factor")) {
3053 						if (val > 0 && val < layer->screen_w / 2) {
3054 							layer->cam_opts.zoom_move_factor = val;
3055 						} else {
3056 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-4\n", argv[x], layer->screen_w / 2);
3057 						}
3058 					} else if (!strcasecmp(argv[x], "pan_speed")) {
3059 						if (val > 0 && val < 100) {
3060 							layer->cam_opts.pan_speed = val;
3061 						} else {
3062 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3063 						}
3064 					} else if (!strcasecmp(argv[x], "pan_accel_speed")) {
3065 						if (val > 0 && val < 100) {
3066 							layer->cam_opts.pan_accel_speed = val;
3067 						} else {
3068 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3069 						}
3070 					} else if (!strcasecmp(argv[x], "pan_accel_min")) {
3071 						if (val > 0 && val < 100) {
3072 							layer->cam_opts.pan_accel_min = val;
3073 						} else {
3074 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3075 						}
3076 					} else if (!strcasecmp(argv[x], "zoom_speed")) {
3077 						if (val > 0 && val < 100) {
3078 							layer->cam_opts.zoom_speed = val;
3079 						} else {
3080 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3081 						}
3082 					} else if (!strcasecmp(argv[x], "zoom_accel_speed")) {
3083 						if (val > 0 && val < 100) {
3084 							layer->cam_opts.zoom_accel_speed = val;
3085 						} else {
3086 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3087 						}
3088 					} else if (!strcasecmp(argv[x], "zoom_accel_min")) {
3089 						if (val > 0 && val < 100) {
3090 							layer->cam_opts.zoom_accel_min = val;
3091 						} else {
3092 							stream->write_function(stream, "-ERR invalid val for option [%s] must be 1-100\n");
3093 						}
3094 					} else if (!strcasecmp(argv[x], "reset")) {
3095 						conference_video_reset_layer_cam(layer);
3096 					} else if (!strcasecmp(argv[x], "pan")) {
3097 						char *x_val = NULL, *y_val = NULL;
3098 						int x = -1, y = -1;
3099 						int on = 0;
3100 
3101 						if (isfalse) {
3102 							layer->pan_geometry.x = 0;
3103 							layer->pan_geometry.y = 0;
3104 							layer->cam_opts.manual_pan = 0;
3105 						} else {
3106 
3107 							if (str_arg) {
3108 								char *p = strchr(str_arg, ':');
3109 
3110 								if (p) {
3111 									*p++ = '\0';
3112 
3113 									if (*str_arg == 'x') {
3114 										x_val = p;
3115 									} else if (*str_arg == 'y') {
3116 										y_val = p;
3117 									}
3118 								}
3119 							}
3120 
3121 							if (!x_val && !y_val) {
3122 								stream->write_function(stream, "-ERR invalid val for pan\n");
3123 							}
3124 
3125 							if (x_val) x = atoi(x_val);
3126 							if (y_val) y = atoi(y_val);
3127 
3128 							if (x_val && strrchr(x_val, 'i')) {
3129 								int nx = (int)layer->pan_geometry.x + x;
3130 
3131 								if (nx < 0) nx = 0;
3132 								if (nx + layer->pan_geometry.w > layer->img->d_w) nx = layer->img->d_w - layer->pan_geometry.w;
3133 
3134 								layer->pan_geometry.x = nx;
3135 								on++;
3136 							} else if (x > -1) {
3137 								layer->pan_geometry.x = x;
3138 								on++;
3139 							}
3140 
3141 							if (y_val && strrchr(y_val, 'i')) {
3142 								int ny = (int)layer->pan_geometry.y + y;
3143 
3144 								if (ny < 0) ny = 0;
3145 								if (ny + layer->pan_geometry.h > layer->img->d_h) ny = layer->img->d_h - layer->pan_geometry.h;
3146 
3147 								layer->pan_geometry.y = ny;
3148 								on++;
3149 							} else if (y > -1) {
3150 								layer->pan_geometry.y = y;
3151 								on++;
3152 							}
3153 
3154 
3155 							if (on) {
3156 								layer->cam_opts.manual_pan = 1;
3157 								layer->cam_opts.autopan = 0;
3158 								stream->write_function(stream, "+OK PAN %d,%d\n", layer->pan_geometry.x, layer->pan_geometry.y);
3159 							}
3160 						}
3161 
3162 
3163 					} else if (!strcasecmp(argv[x], "zoom")) {
3164 						if (str_arg && !isfalse) {
3165 							char *array[4] = {0};
3166 							int iray[4] = {0};
3167 							int ac;
3168 
3169 							if ((ac = switch_split(str_arg, ':', array)) >= 3) {
3170 								int i;
3171 
3172 								for (i = 0; i < ac; i++) {
3173 									int tmp = atoi(array[i]);
3174 
3175 									if (tmp < 0) break;
3176 									iray[i] = tmp;
3177 								}
3178 
3179 								if (i == ac) {
3180 									layer->cam_opts.manual_zoom = 1;
3181 									layer->cam_opts.autozoom = 0;
3182 
3183 									layer->zoom_geometry.x = iray[0];
3184 									layer->zoom_geometry.y = iray[1];
3185 									layer->zoom_geometry.w = iray[2];
3186 									if (iray[3]) {
3187 										layer->zoom_geometry.h = iray[3];
3188 									} else {
3189 										layer->zoom_geometry.h = iray[2];
3190 									}
3191 
3192 									layer->crop_x = iray[0];
3193 									layer->crop_y = iray[1];
3194 									layer->crop_w = iray[2];
3195 									layer->crop_h = iray[2];
3196 									layer->pan_geometry = layer->zoom_geometry;
3197 								} else {
3198 									ok = 0;
3199 								}
3200 							}
3201 						} else {
3202 							layer->zoom_geometry.x = 0;
3203 							layer->zoom_geometry.y = 0;
3204 							layer->zoom_geometry.w = 0;
3205 							layer->zoom_geometry.h = 0;
3206 							layer->cam_opts.manual_zoom = 0;
3207 						}
3208 					} else {
3209 						stream->write_function(stream, "-ERR invalid option [%s]\n", argv[x]);
3210 					}
3211 				}
3212 			}
3213 			switch_mutex_unlock(canvas->mutex);
3214 			switch_mutex_unlock(conference->canvas_mutex);
3215 
3216 		}
3217 	}
3218 
3219 	if (ok) {
3220 		stream->write_function(stream, "+OK\n");
3221 	} else {
3222 		stream->write_function(stream, "-ERR invalid args\n");
3223 	}
3224 
3225 
3226 	return SWITCH_STATUS_SUCCESS;
3227 }
3228 
conference_api_sub_stop(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3229 switch_status_t conference_api_sub_stop(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3230 {
3231 	uint8_t current = 0, all = 0, async = 0;
3232 
3233 	switch_assert(conference != NULL);
3234 	switch_assert(stream != NULL);
3235 
3236 	if (argc > 2) {
3237 		current = strcasecmp(argv[2], "current") ? 0 : 1;
3238 		all = strcasecmp(argv[2], "all") ? 0 : 1;
3239 		async = strcasecmp(argv[2], "async") ? 0 : 1;
3240 	} else {
3241 		all = 1;
3242 	}
3243 
3244 	if (!(current || all || async))
3245 		return SWITCH_STATUS_GENERR;
3246 
3247 	if (argc == 4) {
3248 		uint32_t id = atoi(argv[3]);
3249 		conference_member_t *member;
3250 
3251 		if ((member = conference_member_get(conference, id))) {
3252 			uint32_t stopped = conference_member_stop_file(member, async ? FILE_STOP_ASYNC : current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
3253 			stream->write_function(stream, "+OK Stopped %u files.\n", stopped);
3254 			switch_thread_rwlock_unlock(member->rwlock);
3255 		} else {
3256 			stream->write_function(stream, "-ERR Member: %u not found.\n", id);
3257 		}
3258 	} else {
3259 		uint32_t stopped = conference_file_stop(conference, async ? FILE_STOP_ASYNC : current ? FILE_STOP_CURRENT : FILE_STOP_ALL);
3260 		stream->write_function(stream, "+OK Stopped %u files.\n", stopped);
3261 	}
3262 	return SWITCH_STATUS_SUCCESS;
3263 }
3264 
_conference_api_sub_relate_show_member_relationships(conference_obj_t * conference,switch_stream_handle_t * stream,uint32_t member_id)3265 void _conference_api_sub_relate_show_member_relationships(conference_obj_t *conference, switch_stream_handle_t *stream, uint32_t member_id)
3266 {
3267 	conference_member_t *member;
3268 	for (member = conference->members; member; member = member->next) {
3269 		conference_relationship_t *rel;
3270 
3271 		if (member_id > 0 && member->id != member_id) continue;
3272 
3273 		for (rel = member->relationships; rel; rel = rel->next) {
3274 			stream->write_function(stream, "%d -> %d %s%s%s\n", member->id, rel->id,
3275 								   (rel->flags & RFLAG_CAN_SPEAK) ? "SPEAK " : "NOSPEAK ",
3276 								   (rel->flags & RFLAG_CAN_HEAR) ? "HEAR " : "NOHEAR ",
3277 								   (rel->flags & RFLAG_CAN_SEND_VIDEO) ? "SENDVIDEO " : "NOSENDVIDEO ");
3278 		}
3279 	}
3280 }
3281 
_conference_api_sub_relate_clear_member_relationship(conference_obj_t * conference,switch_stream_handle_t * stream,uint32_t id,uint32_t oid)3282 void _conference_api_sub_relate_clear_member_relationship(conference_obj_t *conference, switch_stream_handle_t *stream, uint32_t id, uint32_t oid)
3283 {
3284 	conference_member_t *member = NULL, *other_member = NULL;
3285 	if ((member = conference_member_get(conference, id))) {
3286 		conference_member_del_relationship(member, oid);
3287 		other_member = conference_member_get(conference, oid);
3288 
3289 		if (other_member) {
3290 			if (conference_utils_member_test_flag(other_member, MFLAG_RECEIVING_VIDEO)) {
3291 				conference_utils_member_clear_flag(other_member, MFLAG_RECEIVING_VIDEO);
3292 				if (conference->floor_holder) {
3293 					conference_member_t *omember = NULL;
3294 
3295 					if ((omember = conference_member_get(member->conference, conference->floor_holder))) {
3296 						switch_core_session_request_video_refresh(omember->session);
3297 						switch_thread_rwlock_unlock(omember->rwlock);
3298 					}
3299 				}
3300 			}
3301 			switch_thread_rwlock_unlock(other_member->rwlock);
3302 		}
3303 
3304 		stream->write_function(stream, "+OK relationship %u->%u cleared.\n", id, oid);
3305 		switch_thread_rwlock_unlock(member->rwlock);
3306 	} else {
3307 		stream->write_function(stream, "-ERR relationship %u->%u not found.\n", id, oid);
3308 	}
3309 }
3310 
_conference_api_sub_relate_set_member_relationship(conference_obj_t * conference,switch_stream_handle_t * stream,uint32_t id,uint32_t oid,uint8_t nospeak,uint8_t nohear,uint8_t sendvideo,char * action)3311 void _conference_api_sub_relate_set_member_relationship(conference_obj_t *conference, switch_stream_handle_t *stream, uint32_t id, uint32_t oid, uint8_t nospeak, uint8_t nohear, uint8_t sendvideo, char *action)
3312 {
3313 
3314 	conference_member_t *member = NULL, *other_member = NULL;
3315 
3316 	if ((member = conference_member_get(conference, id))) {
3317 		other_member = conference_member_get(conference, oid);
3318 	}
3319 
3320 	if (member && other_member) {
3321 		conference_relationship_t *rel = NULL;
3322 
3323 		if (sendvideo && conference_utils_member_test_flag(other_member, MFLAG_RECEIVING_VIDEO) && (! (nospeak || nohear))) {
3324 			stream->write_function(stream, "-ERR member %d already receiving video", oid);
3325 			goto skip;
3326 		}
3327 
3328 		if ((rel = conference_member_get_relationship(member, other_member))) {
3329 			rel->flags = 0;
3330 		} else {
3331 			rel = conference_member_add_relationship(member, oid);
3332 		}
3333 
3334 		if (rel) {
3335 			switch_set_flag(rel, RFLAG_CAN_SPEAK | RFLAG_CAN_HEAR);
3336 			if (nospeak) {
3337 				switch_clear_flag(rel, RFLAG_CAN_SPEAK);
3338 				conference_utils_member_clear_flag_locked(member, MFLAG_TALKING);
3339 			}
3340 			if (nohear) {
3341 				switch_clear_flag(rel, RFLAG_CAN_HEAR);
3342 			}
3343 			if (sendvideo) {
3344 				switch_set_flag(rel, RFLAG_CAN_SEND_VIDEO);
3345 				conference_utils_member_set_flag(other_member, MFLAG_RECEIVING_VIDEO);
3346 				switch_core_session_request_video_refresh(member->session);
3347 			}
3348 
3349 			stream->write_function(stream, "+OK %u->%u %s set\n", id, oid, action);
3350 		} else {
3351 			stream->write_function(stream, "-ERR error!\n");
3352 		}
3353 	} else {
3354 		stream->write_function(stream, "-ERR relationship %u->%u not found.\n", id, oid);
3355 	}
3356 
3357 skip:
3358 	if (member) {
3359 		switch_thread_rwlock_unlock(member->rwlock);
3360 	}
3361 
3362 	if (other_member) {
3363 		switch_thread_rwlock_unlock(other_member->rwlock);
3364 	}
3365 }
3366 
conference_api_sub_relate(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3367 switch_status_t conference_api_sub_relate(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3368 {
3369 	uint8_t nospeak = 0, nohear = 0, sendvideo = 0, clear = 0;
3370 	int members = 0;
3371 	int other_members = 0;
3372 	char *members_array[100] = { 0 };
3373 	char *other_members_array[100] = { 0 };
3374 	char *lbuf_members = NULL, *lbuf_other_members = NULL, *action = NULL;
3375 
3376 	switch_assert(conference != NULL);
3377 	switch_assert(stream != NULL);
3378 
3379 	if (argc <= 3) {
3380 		switch_mutex_lock(conference->mutex);
3381 
3382 		if (conference->relationship_total) {
3383 			if (argc == 3) {
3384 				char *lbuf = NULL;
3385 				lbuf = strdup(argv[2]);
3386 				members = switch_separate_string(lbuf, ',', members_array, (sizeof(members_array) / sizeof(members_array[0])));
3387 				if (members) {
3388 					int i;
3389 					uint32_t member_id;
3390 					for (i = 0; i < members && members_array[i]; i++) {
3391 						member_id = atoi(members_array[i]);
3392 						_conference_api_sub_relate_show_member_relationships(conference, stream, member_id);
3393 					}
3394 				}
3395 				switch_safe_free(lbuf);
3396 			}
3397 
3398 		} else {
3399 			stream->write_function(stream, "+OK No relationships\n");
3400 		}
3401 		switch_mutex_unlock(conference->mutex);
3402 		return SWITCH_STATUS_SUCCESS;
3403 	}
3404 
3405 	if (argc <= 4)
3406 		return SWITCH_STATUS_GENERR;
3407 
3408 	nospeak = strstr(argv[4], "nospeak") ? 1 : 0;
3409 	nohear = strstr(argv[4], "nohear") ? 1 : 0;
3410 	sendvideo = strstr(argv[4], "sendvideo") ? 1 : 0;
3411 
3412 	if (!strcasecmp(argv[4], "clear")) {
3413 		clear = 1;
3414 	}
3415 
3416 	if (!(clear || nospeak || nohear || sendvideo)) {
3417 		return SWITCH_STATUS_GENERR;
3418 	}
3419 
3420 	lbuf_members = strdup(argv[2]);
3421 	lbuf_other_members = strdup(argv[3]);
3422 	action = strdup(argv[4]);
3423 	members = switch_separate_string(lbuf_members, ',', members_array, (sizeof(members_array) / sizeof(members_array[0])));
3424 	other_members = switch_separate_string(lbuf_other_members, ',', other_members_array, (sizeof(other_members_array) / sizeof(other_members_array[0])));
3425 	if (members && other_members) {
3426 		int i, i2;
3427 		uint32_t member_id, other_member_id;
3428 		for (i = 0; i < members && members_array[i]; i++) {
3429 			member_id = atoi(members_array[i]);
3430 			for (i2 = 0; i2 < other_members && other_members_array[i2]; i2++) {
3431 				other_member_id = atoi(other_members_array[i2]);
3432 				if (clear) {
3433 					_conference_api_sub_relate_clear_member_relationship(conference, stream, member_id, other_member_id);
3434 				}
3435 				if (nospeak || nohear || sendvideo) {
3436 					_conference_api_sub_relate_set_member_relationship(conference, stream, member_id, other_member_id, nospeak, nohear, sendvideo, action);
3437 				}
3438 			}
3439 		}
3440 	}
3441 	switch_safe_free(lbuf_members);
3442 	switch_safe_free(lbuf_other_members);
3443 	switch_safe_free(action);
3444 
3445 	return SWITCH_STATUS_SUCCESS;
3446 }
3447 
conference_api_sub_lock(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3448 switch_status_t conference_api_sub_lock(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3449 {
3450 	switch_event_t *event;
3451 
3452 	switch_assert(conference != NULL);
3453 	switch_assert(stream != NULL);
3454 
3455 	if (conference->is_locked_sound) {
3456 		conference_file_play(conference, conference->is_locked_sound, CONF_DEFAULT_LEADIN, NULL, 0);
3457 	}
3458 
3459 	conference_utils_set_flag_locked(conference, CFLAG_LOCKED);
3460 	stream->write_function(stream, "+OK %s locked\n", argv[0]);
3461 	if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3462 		conference_event_add_data(conference, event);
3463 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "lock");
3464 		switch_event_fire(&event);
3465 	}
3466 
3467 	return 0;
3468 }
3469 
conference_api_sub_unlock(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3470 switch_status_t conference_api_sub_unlock(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3471 {
3472 	switch_event_t *event;
3473 
3474 	switch_assert(conference != NULL);
3475 	switch_assert(stream != NULL);
3476 
3477 	if (conference->is_unlocked_sound) {
3478 		conference_file_play(conference, conference->is_unlocked_sound, CONF_DEFAULT_LEADIN, NULL, 0);
3479 	}
3480 
3481 	conference_utils_clear_flag_locked(conference, CFLAG_LOCKED);
3482 	stream->write_function(stream, "+OK %s unlocked\n", argv[0]);
3483 	if (test_eflag(conference, EFLAG_UNLOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3484 		conference_event_add_data(conference, event);
3485 		switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "unlock");
3486 		switch_event_fire(&event);
3487 	}
3488 
3489 	return 0;
3490 }
3491 
conference_api_sub_exit_sound(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3492 switch_status_t conference_api_sub_exit_sound(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3493 {
3494 	switch_event_t *event;
3495 
3496 	switch_assert(conference != NULL);
3497 	switch_assert(stream != NULL);
3498 
3499 	if (argc <= 2) {
3500 		stream->write_function(stream, "-ERR Not enough args\n");
3501 		return SWITCH_STATUS_GENERR;
3502 	}
3503 
3504 	if ( !strcasecmp(argv[2], "on") ) {
3505 		conference_utils_set_flag_locked(conference, CFLAG_EXIT_SOUND);
3506 		stream->write_function(stream, "+OK %s exit sounds on (%s)\n", argv[0], conference->exit_sound);
3507 		if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3508 			conference_event_add_data(conference, event);
3509 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit-sounds-on");
3510 			switch_event_fire(&event);
3511 		}
3512 	} else if ( !strcasecmp(argv[2], "off") || !strcasecmp(argv[2], "none") ) {
3513 		conference_utils_clear_flag_locked(conference, CFLAG_EXIT_SOUND);
3514 		stream->write_function(stream, "+OK %s exit sounds off (%s)\n", argv[0], conference->exit_sound);
3515 		if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3516 			conference_event_add_data(conference, event);
3517 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit-sounds-off");
3518 			switch_event_fire(&event);
3519 		}
3520 	} else if ( !strcasecmp(argv[2], "file") ) {
3521 		if (! argv[3]) {
3522 			stream->write_function(stream, "-ERR No filename specified\n");
3523 		} else {
3524 			/* TODO: if possible, verify file exists before setting it */
3525 			stream->write_function(stream,"+OK Old exit sound: [%s]\n", conference->exit_sound);
3526 			conference->exit_sound = switch_core_strdup(conference->pool, argv[3]);
3527 			stream->write_function(stream, "+OK %s exit sound file set to %s\n", argv[0], conference->exit_sound);
3528 			if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3529 				conference_event_add_data(conference, event);
3530 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "exit-sound-file-changed");
3531 				switch_event_fire(&event);
3532 			}
3533 		}
3534 	} else {
3535 		stream->write_function(stream, "-ERR Bad args\n");
3536 		return SWITCH_STATUS_GENERR;
3537 	}
3538 
3539 	return 0;
3540 }
3541 
3542 
conference_api_sub_enter_sound(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3543 switch_status_t conference_api_sub_enter_sound(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3544 {
3545 	switch_event_t *event;
3546 
3547 	switch_assert(conference != NULL);
3548 	switch_assert(stream != NULL);
3549 
3550 	if (argc <= 2) {
3551 		stream->write_function(stream, "-ERR Not enough args\n");
3552 		return SWITCH_STATUS_GENERR;
3553 	}
3554 
3555 	if ( !strcasecmp(argv[2], "on") ) {
3556 		conference_utils_set_flag_locked(conference, CFLAG_ENTER_SOUND);
3557 		stream->write_function(stream, "+OK %s enter sounds on (%s)\n", argv[0], conference->enter_sound);
3558 		if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3559 			conference_event_add_data(conference, event);
3560 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "enter-sounds-on");
3561 			switch_event_fire(&event);
3562 		}
3563 	} else if ( !strcasecmp(argv[2], "off") || !strcasecmp(argv[2], "none") ) {
3564 		conference_utils_clear_flag_locked(conference, CFLAG_ENTER_SOUND);
3565 		stream->write_function(stream, "+OK %s enter sounds off (%s)\n", argv[0], conference->enter_sound);
3566 		if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3567 			conference_event_add_data(conference, event);
3568 			switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "enter-sounds-off");
3569 			switch_event_fire(&event);
3570 		}
3571 	} else if ( !strcasecmp(argv[2], "file") ) {
3572 		if (! argv[3]) {
3573 			stream->write_function(stream, "-ERR No filename specified\n");
3574 		} else {
3575 			/* TODO: verify file exists before setting it */
3576 			conference->enter_sound = switch_core_strdup(conference->pool, argv[3]);
3577 			stream->write_function(stream, "+OK %s enter sound file set to %s\n", argv[0], conference->enter_sound);
3578 			if (test_eflag(conference, EFLAG_LOCK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3579 				conference_event_add_data(conference, event);
3580 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "enter-sound-file-changed");
3581 				switch_event_fire(&event);
3582 			}
3583 		}
3584 	} else {
3585 		stream->write_function(stream, "-ERR Bad args\n");
3586 		return SWITCH_STATUS_GENERR;
3587 	}
3588 
3589 	return 0;
3590 }
3591 
3592 
conference_api_sub_dial(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3593 switch_status_t conference_api_sub_dial(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3594 {
3595 	switch_call_cause_t cause;
3596 	char *tmp;
3597 
3598 	switch_assert(stream != NULL);
3599 
3600 	if (argc <= 2) {
3601 		stream->write_function(stream, "-ERR Bad Args\n");
3602 		return SWITCH_STATUS_GENERR;
3603 	}
3604 
3605 	if (conference && argv[2] && strstr(argv[2], "vlc/")) {
3606 		tmp = switch_core_sprintf(conference->pool, "{vlc_rate=%d,vlc_channels=%d,vlc_interval=%d}%s",
3607 								  conference->rate, conference->channels, conference->interval, argv[2]);
3608 		argv[2] = tmp;
3609 	}
3610 
3611 	if (conference) {
3612 		conference_outcall(conference, NULL, NULL, argv[2], 60, NULL, argv[4], argv[3], NULL, &cause, NULL, NULL, NULL);
3613 	} else {
3614 		conference_outcall(NULL, argv[0], NULL, argv[2], 60, NULL, argv[4], argv[3], NULL, &cause, NULL, NULL, NULL);
3615 	}
3616 	stream->write_function(stream, "+OK Call Requested: result: [%s]\n", switch_channel_cause2str(cause));
3617 
3618 	return SWITCH_STATUS_SUCCESS;
3619 }
3620 
conference_api_sub_bgdial(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3621 switch_status_t conference_api_sub_bgdial(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3622 {
3623 	switch_uuid_t uuid;
3624 	char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1];
3625 
3626 	switch_assert(stream != NULL);
3627 
3628 	if (argc <= 2) {
3629 		stream->write_function(stream, "-ERR Bad Args\n");
3630 		return SWITCH_STATUS_GENERR;
3631 	}
3632 
3633 	switch_uuid_get(&uuid);
3634 	switch_uuid_format(uuid_str, &uuid);
3635 
3636 	if (conference) {
3637 		conference_outcall_bg(conference, NULL, NULL, argv[2], 60, NULL, argv[4], argv[3], uuid_str, NULL, NULL, NULL);
3638 	} else {
3639 		conference_outcall_bg(NULL, argv[0], NULL, argv[2], 60, NULL, argv[4], argv[3], uuid_str, NULL, NULL, NULL);
3640 	}
3641 
3642 	stream->write_function(stream, "+OK Job-UUID: %s\n", uuid_str);
3643 
3644 	return SWITCH_STATUS_SUCCESS;
3645 }
3646 
3647 
3648 
conference_api_sub_transfer(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3649 switch_status_t conference_api_sub_transfer(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3650 {
3651 	switch_status_t ret_status = SWITCH_STATUS_SUCCESS;
3652 	char *conference_name = NULL, *profile_name;
3653 
3654 	switch_assert(conference != NULL);
3655 	switch_assert(stream != NULL);
3656 
3657 	if (argc > 3 && !zstr(argv[2])) {
3658 		int x;
3659 
3660 		conference_name = strdup(argv[2]);
3661 		switch_assert(conference_name);
3662 
3663 		if ((profile_name = strchr(conference_name, '@'))) {
3664 			*profile_name++ = '\0';
3665 		} else {
3666 			profile_name = "default";
3667 		}
3668 
3669 		for (x = 3; x < argc; x++) {
3670 			conference_member_t *member = NULL;
3671 			uint32_t id = atoi(argv[x]);
3672 			switch_channel_t *channel;
3673 			switch_event_t *event;
3674 			char *xdest = NULL;
3675 
3676 			if (!id || !(member = conference_member_get(conference, id))) {
3677 				stream->write_function(stream, "-ERR No Member %u in conference %s.\n", id, conference->name);
3678 				continue;
3679 			}
3680 
3681 			channel = switch_core_session_get_channel(member->session);
3682 			xdest = switch_core_session_sprintf(member->session, "conference:%s@%s", conference_name, profile_name);
3683 			switch_ivr_session_transfer(member->session, xdest, "inline", NULL);
3684 
3685 			switch_channel_set_variable(channel, "last_transfered_conference", conference_name);
3686 
3687 			stream->write_function(stream, "+OK Member '%d' sent to conference %s.\n", member->id, argv[2]);
3688 
3689 			/* tell them what happened */
3690 			if (test_eflag(conference, EFLAG_TRANSFER) &&
3691 				switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) {
3692 				conference_member_add_event_data(member, event);
3693 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Old-Conference-Name", conference->name);
3694 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "New-Conference-Name", argv[2]);
3695 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "transfer");
3696 				switch_event_fire(&event);
3697 			}
3698 
3699 			switch_thread_rwlock_unlock(member->rwlock);
3700 		}
3701 	} else {
3702 		ret_status = SWITCH_STATUS_GENERR;
3703 	}
3704 
3705 	switch_safe_free(conference_name);
3706 
3707 	return ret_status;
3708 }
3709 
conference_api_sub_check_record(conference_obj_t * conference,switch_stream_handle_t * stream,int arc,char ** argv)3710 switch_status_t conference_api_sub_check_record(conference_obj_t *conference, switch_stream_handle_t *stream, int arc, char **argv)
3711 {
3712 	conference_record_t *rec;
3713 	int x = 0;
3714 
3715 	switch_mutex_lock(conference->flag_mutex);
3716 	for (rec = conference->rec_node_head; rec; rec = rec->next) {
3717 		stream->write_function(stream, "+OK Record file %s%s%s\n", rec->path, rec->autorec ? " " : "", rec->autorec ? "(Auto)" : "");
3718 		x++;
3719 	}
3720 
3721 	if (!x) {
3722 		stream->write_function(stream, "-ERR Conference is not being recorded.\n");
3723 	}
3724 	switch_mutex_unlock(conference->flag_mutex);
3725 
3726 	return SWITCH_STATUS_SUCCESS;
3727 }
3728 
conference_api_sub_record(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3729 switch_status_t conference_api_sub_record(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3730 {
3731 	int id = 0;
3732 	conference_record_t *rec;
3733 
3734 	switch_assert(conference != NULL);
3735 	switch_assert(stream != NULL);
3736 
3737 	if (argc <= 2) {
3738 		return SWITCH_STATUS_GENERR;
3739 	}
3740 
3741 	if (argv[3]) {
3742 
3743 		id = atoi(argv[3]);
3744 
3745 		if (id < 1 || id > MAX_CANVASES+1) {
3746 			id = -1;
3747 		}
3748 
3749 		if (id < 1) {
3750 			stream->write_function(stream, "-ERR Invalid canvas\n");
3751 		}
3752 
3753 	}
3754 
3755 	if (id == 0 && conference->canvases[0]) id = 1;
3756 
3757 	if (id > conference->canvas_count + 1) {
3758 		id = 1;
3759 	}
3760 
3761 
3762 	switch_mutex_lock(conference->flag_mutex);
3763 	for (rec = conference->rec_node_head; rec; rec = rec->next) {
3764 		char *path_a, *path_b;
3765 
3766 		if ((path_a = strrchr(rec->path, '}'))) {
3767 			while(*path_a == ' ' || *path_a == '}') path_a++;
3768 		} else {
3769 			path_a = rec->path;
3770 		}
3771 
3772 		if ((path_b = strrchr(argv[2], '}'))) {
3773 			while(*path_b == ' ' || *path_b == '}') path_b++;
3774 		} else {
3775 			path_b = argv[2];
3776 		}
3777 
3778 		if (!strcmp(path_a, path_b)) {
3779 			stream->write_function(stream, "-ERR file [%s] is already being used for recording.\n", rec->path);
3780 			switch_mutex_unlock(conference->flag_mutex);
3781 			return SWITCH_STATUS_SUCCESS;
3782 		}
3783 	}
3784 	switch_mutex_unlock(conference->flag_mutex);
3785 
3786 
3787 	if (id > 0) {
3788 		stream->write_function(stream, "+OK Record file %s canvas %d\n", argv[2], id);
3789 	} else {
3790 		stream->write_function(stream, "+OK Record file %s\n", argv[2]);
3791 	}
3792 
3793 	conference->record_filename = switch_core_strdup(conference->pool, argv[2]);
3794 	conference->record_count++;
3795 	conference_record_launch_thread(conference, argv[2], id - 1, SWITCH_FALSE);
3796 	return SWITCH_STATUS_SUCCESS;
3797 }
3798 
conference_api_sub_norecord(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3799 switch_status_t conference_api_sub_norecord(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3800 {
3801 	int all, before = conference->record_count, ttl = 0;
3802 
3803 	switch_assert(conference != NULL);
3804 	switch_assert(stream != NULL);
3805 
3806 	if (argc <= 2)
3807 		return SWITCH_STATUS_GENERR;
3808 
3809 	all = (strcasecmp(argv[2], "all") == 0);
3810 
3811 	if (!conference_record_stop(conference, stream, all ? NULL : argv[2]) && !all) {
3812 		stream->write_function(stream, "-ERR non-existant recording '%s'\n", argv[2]);
3813 	}
3814 
3815 	ttl = before - conference->record_count;
3816 	stream->write_function(stream, "+OK Stopped recording %d file%s\n", ttl, ttl == 1 ? "" : "s");
3817 
3818 	return SWITCH_STATUS_SUCCESS;
3819 }
3820 
conference_api_sub_pauserec(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3821 switch_status_t conference_api_sub_pauserec(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3822 {
3823 	switch_event_t *event;
3824 	recording_action_type_t action;
3825 
3826 	switch_assert(conference != NULL);
3827 	switch_assert(stream != NULL);
3828 
3829 	if (argc <= 2)
3830 		return SWITCH_STATUS_GENERR;
3831 
3832 	if (strcasecmp(argv[1], "pause") == 0) {
3833 		action = REC_ACTION_PAUSE;
3834 	} else if (strcasecmp(argv[1], "resume") == 0) {
3835 		action = REC_ACTION_RESUME;
3836 	} else {
3837 		return SWITCH_STATUS_GENERR;
3838 	}
3839 	stream->write_function(stream, "+OK %s recording file %s\n",
3840 						   action == REC_ACTION_PAUSE ? "Pause" : "Resume", argv[2]);
3841 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,	"%s recording file %s\n",
3842 					  action == REC_ACTION_PAUSE ? "Pause" : "Resume", argv[2]);
3843 
3844 	if (!conference_record_action(conference, argv[2], action)) {
3845 		stream->write_function(stream, "-ERR non-existant recording '%s'\n", argv[2]);
3846 	} else {
3847 		if (test_eflag(conference, EFLAG_RECORD) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS)
3848 			{
3849 				conference_event_add_data(conference, event);
3850 				if (action == REC_ACTION_PAUSE) {
3851 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "pause-recording");
3852 				} else {
3853 					switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "resume-recording");
3854 				}
3855 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Path", argv[2]);
3856 				switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Other-Recordings", conference->record_count ? "true" : "false");
3857 				switch_event_fire(&event);
3858 			}
3859 	}
3860 
3861 	return SWITCH_STATUS_SUCCESS;
3862 }
3863 
conference_api_sub_recording(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3864 switch_status_t conference_api_sub_recording(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3865 {
3866 	switch_assert(conference != NULL);
3867 	switch_assert(stream != NULL);
3868 
3869 	if (argc > 2 && argc <= 3) {
3870 		if (strcasecmp(argv[2], "stop") == 0 || strcasecmp(argv[2], "check") == 0) {
3871 			argv[3] = "all";
3872 			argc++;
3873 		}
3874 	}
3875 
3876 	if (argc <= 3) {
3877 		/* It means that old syntax is used */
3878 		return conference_api_sub_record(conference,stream,argc,argv);
3879 	} else {
3880 		/* for new syntax call existing functions with fixed parameter list */
3881 		if (strcasecmp(argv[2], "start") == 0) {
3882 			argv[1] = argv[2];
3883 			argv[2] = argv[3];
3884 			argv[3] = argv[4];
3885 			return conference_api_sub_record(conference,stream,4,argv);
3886 		} else if (strcasecmp(argv[2], "stop") == 0) {
3887 			argv[1] = argv[2];
3888 			argv[2] = argv[3];
3889 			return conference_api_sub_norecord(conference,stream,4,argv);
3890 		} else if (strcasecmp(argv[2], "check") == 0) {
3891 			argv[1] = argv[2];
3892 			argv[2] = argv[3];
3893 			return conference_api_sub_check_record(conference,stream,4,argv);
3894 		} else if (strcasecmp(argv[2], "pause") == 0) {
3895 			argv[1] = argv[2];
3896 			argv[2] = argv[3];
3897 			return conference_api_sub_pauserec(conference,stream,4,argv);
3898 		} else if (strcasecmp(argv[2], "resume") == 0) {
3899 			argv[1] = argv[2];
3900 			argv[2] = argv[3];
3901 			return conference_api_sub_pauserec(conference,stream,4,argv);
3902 		} else {
3903 			return SWITCH_STATUS_GENERR;
3904 		}
3905 	}
3906 }
3907 
conference_api_sub_file_vol(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3908 switch_status_t conference_api_sub_file_vol(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3909 {
3910 	if (argc >= 1) {
3911 		conference_file_node_t *fnode;
3912 		int vol = 0;
3913 		int ok = 0;
3914 
3915 		if (argc < 3) {
3916 			stream->write_function(stream, "-ERR missing args\n");
3917 			return SWITCH_STATUS_GENERR;
3918 		}
3919 
3920 		switch_mutex_lock(conference->mutex);
3921 
3922 		fnode = conference->fnode;
3923 
3924 		vol = atoi(argv[2]);
3925 
3926 		if (argc > 3) {
3927 			if (strcasecmp(argv[3], "async")) {
3928 				fnode = conference->async_fnode;
3929 			}
3930 		}
3931 
3932 		if (fnode && fnode->type == NODE_TYPE_FILE) {
3933 			fnode->fh.vol = vol;
3934 			ok = 1;
3935 		}
3936 		switch_mutex_unlock(conference->mutex);
3937 
3938 
3939 		if (ok) {
3940 			stream->write_function(stream, "+OK volume changed\n");
3941 			return SWITCH_STATUS_SUCCESS;
3942 		} else {
3943 			stream->write_function(stream, "-ERR File not playing\n");
3944 			return SWITCH_STATUS_GENERR;
3945 		}
3946 
3947 
3948 	} else {
3949 		stream->write_function(stream, "-ERR Invalid parameters:\n");
3950 		return SWITCH_STATUS_GENERR;
3951 	}
3952 }
3953 
conference_api_sub_pin(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3954 switch_status_t conference_api_sub_pin(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
3955 {
3956 	switch_assert(conference != NULL);
3957 	switch_assert(stream != NULL);
3958 
3959 	if ((argc == 4) && (!strcmp(argv[2], "mod"))) {
3960 		conference->mpin = switch_core_strdup(conference->pool, argv[3]);
3961 		stream->write_function(stream, "+OK Moderator Pin for conference %s set: %s\n", argv[0], conference->mpin);
3962 		return SWITCH_STATUS_SUCCESS;
3963 	} else if ((argc == 3) && (!strcmp(argv[1], "pin"))) {
3964 		conference->pin = switch_core_strdup(conference->pool, argv[2]);
3965 		stream->write_function(stream, "+OK Pin for conference %s set: %s\n", argv[0], conference->pin);
3966 		return SWITCH_STATUS_SUCCESS;
3967 	} else if (argc == 2 && (!strcmp(argv[1], "nopin"))) {
3968 		conference->pin = NULL;
3969 		stream->write_function(stream, "+OK Pin for conference %s deleted\n", argv[0]);
3970 		return SWITCH_STATUS_SUCCESS;
3971 	} else {
3972 		stream->write_function(stream, "-ERR Invalid parameters:\n");
3973 		return SWITCH_STATUS_GENERR;
3974 	}
3975 }
3976 
conference_api_sub_get(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)3977 switch_status_t conference_api_sub_get(conference_obj_t *conference,
3978 									   switch_stream_handle_t *stream, int argc, char **argv) {
3979 	int ret_status = SWITCH_STATUS_GENERR;
3980 
3981 	if (argc != 3) {
3982 		ret_status = SWITCH_STATUS_FALSE;
3983 	} else {
3984 		ret_status = SWITCH_STATUS_SUCCESS;
3985 		if (strcasecmp(argv[2], "run_time") == 0) {
3986 			stream->write_function(stream, "%ld",
3987 								   switch_epoch_time_now(NULL) - conference->run_time);
3988 		} else if (strcasecmp(argv[2], "count") == 0) {
3989 			stream->write_function(stream, "%d",
3990 								   conference->count);
3991 		} else if (strcasecmp(argv[2], "count_ghosts") == 0) {
3992 			stream->write_function(stream, "%d",
3993 								   conference->count_ghosts);
3994 		} else if (strcasecmp(argv[2], "max_members") == 0) {
3995 			stream->write_function(stream, "%d",
3996 								   conference->max_members);
3997 		} else if (strcasecmp(argv[2], "rate") == 0) {
3998 			stream->write_function(stream, "%d",
3999 								   conference->rate);
4000 		} else if (strcasecmp(argv[2], "profile_name") == 0) {
4001 			stream->write_function(stream, "%s",
4002 								   conference->profile_name);
4003 		} else if (strcasecmp(argv[2], "sound_prefix") == 0) {
4004 			stream->write_function(stream, "%s",
4005 								   conference->sound_prefix);
4006 		} else if (strcasecmp(argv[2], "caller_id_name") == 0) {
4007 			stream->write_function(stream, "%s",
4008 								   conference->caller_id_name);
4009 		} else if (strcasecmp(argv[2], "caller_id_number") == 0) {
4010 			stream->write_function(stream, "%s",
4011 								   conference->caller_id_number);
4012 		} else if (strcasecmp(argv[2], "is_locked") == 0) {
4013 			stream->write_function(stream, "%s",
4014 								   conference_utils_test_flag(conference, CFLAG_LOCKED) ? "locked" : "");
4015 		} else if (strcasecmp(argv[2], "endconference_grace_time") == 0) {
4016 			stream->write_function(stream, "%d",
4017 								   conference->endconference_grace_time);
4018 		} else if (strcasecmp(argv[2], "uuid") == 0) {
4019 			stream->write_function(stream, "%s",
4020 								   conference->uuid_str);
4021 		} else if (strcasecmp(argv[2], "wait_mod") == 0) {
4022 			stream->write_function(stream, "%s",
4023 								   conference_utils_test_flag(conference, CFLAG_WAIT_MOD) ? "true" : "");
4024 		} else {
4025 			ret_status = SWITCH_STATUS_FALSE;
4026 		}
4027 	}
4028 
4029 	return ret_status;
4030 }
4031 
conference_api_sub_set(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)4032 switch_status_t conference_api_sub_set(conference_obj_t *conference,
4033 									   switch_stream_handle_t *stream, int argc, char **argv) {
4034 	int ret_status = SWITCH_STATUS_GENERR;
4035 
4036 	if (argc != 4 || zstr(argv[3])) {
4037 		ret_status = SWITCH_STATUS_FALSE;
4038 	} else {
4039 		ret_status = SWITCH_STATUS_SUCCESS;
4040 		if (strcasecmp(argv[2], "max_members") == 0) {
4041 			int new_max = atoi(argv[3]);
4042 			if (new_max >= 0) {
4043 				stream->write_function(stream, "%d", conference->max_members);
4044 				conference->max_members = new_max;
4045 			} else {
4046 				ret_status = SWITCH_STATUS_FALSE;
4047 			}
4048 		} else	if (strcasecmp(argv[2], "sound_prefix") == 0) {
4049 			stream->write_function(stream, "%s",conference->sound_prefix);
4050 			conference->sound_prefix = switch_core_strdup(conference->pool, argv[3]);
4051 		} else	if (strcasecmp(argv[2], "caller_id_name") == 0) {
4052 			stream->write_function(stream, "%s",conference->caller_id_name);
4053 			conference->caller_id_name = switch_core_strdup(conference->pool, argv[3]);
4054 		} else	if (strcasecmp(argv[2], "caller_id_number") == 0) {
4055 			stream->write_function(stream, "%s",conference->caller_id_number);
4056 			conference->caller_id_number = switch_core_strdup(conference->pool, argv[3]);
4057 		} else if (strcasecmp(argv[2], "endconference_grace_time") == 0) {
4058 			int new_gt = atoi(argv[3]);
4059 			if (new_gt >= 0) {
4060 				stream->write_function(stream, "%d", conference->endconference_grace_time);
4061 				conference->endconference_grace_time = new_gt;
4062 			} else {
4063 				ret_status = SWITCH_STATUS_FALSE;
4064 			}
4065 		} else {
4066 			ret_status = SWITCH_STATUS_FALSE;
4067 		}
4068 	}
4069 
4070 	return ret_status;
4071 }
4072 
4073 
4074 
conference_api_sub_xml_list(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)4075 switch_status_t conference_api_sub_xml_list(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
4076 {
4077 	int count = 0;
4078 	switch_hash_index_t *hi;
4079 	void *val;
4080 	switch_xml_t x_conference, x_conferences;
4081 	int off = 0;
4082 	char *ebuf;
4083 
4084 	x_conferences = switch_xml_new("conferences");
4085 	switch_assert(x_conferences);
4086 
4087 	if (conference == NULL) {
4088 		switch_mutex_lock(conference_globals.hash_mutex);
4089 		for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
4090 			switch_core_hash_this(hi, NULL, NULL, &val);
4091 			conference = (conference_obj_t *) val;
4092 
4093 			x_conference = switch_xml_add_child_d(x_conferences, "conference", off++);
4094 			switch_assert(conference);
4095 
4096 			count++;
4097 			conference_xlist(conference, x_conference, off);
4098 
4099 		}
4100 		switch_mutex_unlock(conference_globals.hash_mutex);
4101 	} else {
4102 		x_conference = switch_xml_add_child_d(x_conferences, "conference", off++);
4103 		switch_assert(conference);
4104 		count++;
4105 		conference_xlist(conference, x_conference, off);
4106 	}
4107 
4108 
4109 	ebuf = switch_xml_toxml(x_conferences, SWITCH_TRUE);
4110 
4111 	stream->write_function(stream, "%s", ebuf);
4112 
4113 	switch_xml_free(x_conferences);
4114 	free(ebuf);
4115 
4116 	return SWITCH_STATUS_SUCCESS;
4117 }
4118 
conference_api_sub_json_list(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv)4119 switch_status_t conference_api_sub_json_list(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv)
4120 {
4121 	switch_hash_index_t *hi;
4122 	void *val;
4123 	char *ebuf;
4124 	cJSON *conferences = cJSON_CreateArray();
4125 	switch_bool_t compact = SWITCH_FALSE;
4126 
4127 	switch_assert(conferences);
4128 
4129 	if (conference == NULL) {
4130 		switch_mutex_lock(conference_globals.hash_mutex);
4131 		for (hi = switch_core_hash_first(conference_globals.conference_hash); hi; hi = switch_core_hash_next(&hi)) {
4132 			switch_core_hash_this(hi, NULL, NULL, &val);
4133 			conference = (conference_obj_t *) val;
4134 			conference_jlist(conference, conferences);
4135 		}
4136 		switch_mutex_unlock(conference_globals.hash_mutex);
4137 		compact = (argc == 2 && !strcmp(argv[1], "compact"));
4138 	} else {
4139 		conference_jlist(conference, conferences);
4140 		compact = (argc == 3 && !strcmp(argv[2], "compact"));
4141 	}
4142 
4143 	ebuf = compact ? cJSON_PrintUnformatted(conferences) : cJSON_Print(conferences);
4144 	switch_assert(ebuf);
4145 	stream->write_function(stream, "%s", ebuf);
4146 	free(ebuf);
4147 
4148 	cJSON_Delete(conferences);
4149 
4150 	return SWITCH_STATUS_SUCCESS;
4151 }
4152 
conference_api_dispatch(conference_obj_t * conference,switch_stream_handle_t * stream,int argc,char ** argv,const char * cmdline,int argn)4153 switch_status_t conference_api_dispatch(conference_obj_t *conference, switch_stream_handle_t *stream, int argc, char **argv, const char *cmdline, int argn)
4154 {
4155 	switch_status_t status = SWITCH_STATUS_FALSE;
4156 	uint32_t i, found = 0;
4157 	switch_assert(conference != NULL);
4158 	switch_assert(stream != NULL);
4159 
4160 	/* loop through the command table to find a match */
4161 	for (i = 0; i < CONFFUNCAPISIZE && !found; i++) {
4162 		if (strcasecmp(argv[argn], conference_api_sub_commands[i].pname) == 0) {
4163 			found = 1;
4164 			switch (conference_api_sub_commands[i].fntype) {
4165 
4166 				/* commands that we've broken the command line into arguments for */
4167 			case CONF_API_SUB_ARGS_SPLIT:
4168 				{
4169 					conference_api_args_cmd_t pfn = (conference_api_args_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4170 
4171 					if (pfn(conference, stream, argc, argv) != SWITCH_STATUS_SUCCESS) {
4172 						/* command returned error, so show syntax usage */
4173 						stream->write_function(stream, "%s %s", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
4174 					}
4175 				}
4176 				break;
4177 
4178 				/* member specific command that can be iterated */
4179 			case CONF_API_SUB_MEMBER_TARGET:
4180 				{
4181 					uint32_t id = 0;
4182 					uint8_t all = 0;
4183 					uint8_t last = 0;
4184 					uint8_t non_mod = 0;
4185 
4186 					if (argv[argn + 1]) {
4187 						if (!(id = atoi(argv[argn + 1]))) {
4188 							all = strcasecmp(argv[argn + 1], "all") ? 0 : 1;
4189 							non_mod = strcasecmp(argv[argn + 1], "non_moderator") ? 0 : 1;
4190 							last = strcasecmp(argv[argn + 1], "last") ? 0 : 1;
4191 						}
4192 					}
4193 
4194 					if (all || non_mod) {
4195 						conference_member_itterator(conference, stream, non_mod, (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd, argv[argn + 2]);
4196 					} else if (last) {
4197 						conference_member_t *member = NULL;
4198 						conference_member_t *last_member = NULL;
4199 
4200 						switch_mutex_lock(conference->member_mutex);
4201 
4202 						/* find last (oldest) member */
4203 						member = conference->members;
4204 						while (member != NULL) {
4205 							if (last_member == NULL || member->id > last_member->id) {
4206 								last_member = member;
4207 							}
4208 							member = member->next;
4209 						}
4210 
4211 						/* exec functio on last (oldest) member */
4212 						if (last_member != NULL && last_member->session && !conference_utils_member_test_flag(last_member, MFLAG_NOCHANNEL)) {
4213 							conference_api_member_cmd_t pfn = (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4214 							pfn(last_member, stream, argv[argn + 2]);
4215 						}
4216 
4217 						switch_mutex_unlock(conference->member_mutex);
4218 					} else if (id) {
4219 						conference_api_member_cmd_t pfn = (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4220 						conference_member_t *member = conference_member_get(conference, id);
4221 
4222 						if (member != NULL) {
4223 							pfn(member, stream, argv[argn + 2]);
4224 							switch_thread_rwlock_unlock(member->rwlock);
4225 						} else {
4226 							stream->write_function(stream, "-ERR Non-Existant ID %u\n", id);
4227 						}
4228 					} else if (!zstr(argv[argn + 1]) && strchr(argv[argn + 1], '=')) {
4229 						conference_api_member_cmd_t pfn = (conference_api_member_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4230 						conference_member_t *member;
4231 						char *var, *val;
4232 
4233 						var = strdup(argv[argn + 1]);
4234 						switch_assert(var);
4235 
4236 						if ((val = strchr(var, '='))) {
4237 							*val++ = '\0';
4238 						}
4239 
4240 						member = conference_member_get_by_var(conference, var, val);
4241 
4242 						if (member != NULL) {
4243 							pfn(member, stream, argv[argn + 2]);
4244 							switch_thread_rwlock_unlock(member->rwlock);
4245 						} else {
4246 							stream->write_function(stream, "-ERR Non-Existant member\n");
4247 						}
4248 
4249 						switch_safe_free(var);
4250 					} else {
4251 						stream->write_function(stream, "%s %s", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
4252 					}
4253 				}
4254 				break;
4255 
4256 				/* commands that deals with all text after command */
4257 			case CONF_API_SUB_ARGS_AS_ONE:
4258 				{
4259 					conference_api_text_cmd_t pfn = (conference_api_text_cmd_t) conference_api_sub_commands[i].pfnapicmd;
4260 					char *start_text;
4261 					const char *modified_cmdline = cmdline;
4262 					const char *cmd = conference_api_sub_commands[i].pname;
4263 
4264 					if (!zstr(modified_cmdline) && (start_text = strstr(modified_cmdline, cmd))) {
4265 						modified_cmdline = start_text + strlen(cmd);
4266 						while (modified_cmdline && (*modified_cmdline == ' ' || *modified_cmdline == '\t')) {
4267 							modified_cmdline++;
4268 						}
4269 					}
4270 
4271 					/* call the command handler */
4272 					if (pfn(conference, stream, modified_cmdline) != SWITCH_STATUS_SUCCESS) {
4273 						/* command returned error, so show syntax usage */
4274 						stream->write_function(stream, "%s %s", conference_api_sub_commands[i].pcommand, conference_api_sub_commands[i].psyntax);
4275 					}
4276 				}
4277 				break;
4278 			}
4279 		}
4280 	}
4281 
4282 	if (!found) {
4283 		stream->write_function(stream, "-ERR Conference command '%s' not found.\n", argv[argn]);
4284 	} else {
4285 		status = SWITCH_STATUS_SUCCESS;
4286 	}
4287 
4288 	return status;
4289 }
4290 
4291 /* For Emacs:
4292  * Local Variables:
4293  * mode:c
4294  * indent-tabs-mode:t
4295  * tab-width:4
4296  * c-basic-offset:4
4297  * End:
4298  * For VIM:
4299  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
4300  */
4301