app_confbridge: Repeatedly starting and stopping recording ref leaks the recording...
[asterisk/asterisk.git] / apps / app_confbridge.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2007-2008, Digium, Inc.
5  *
6  * Joshua Colp <jcolp@digium.com>
7  * David Vossel <dvossel@digium.com>
8  *
9  * See http://www.asterisk.org for more information about
10  * the Asterisk project. Please do not directly contact
11  * any of the maintainers of this project for assistance;
12  * the project provides a web site, mailing lists and IRC
13  * channels for your use.
14  *
15  * This program is free software, distributed under the terms of
16  * the GNU General Public License Version 2. See the LICENSE file
17  * at the top of the source tree.
18  */
19
20 /*! \file
21  *
22  * \brief Conference Bridge application
23  *
24  * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
25  * \author\verbatim David Vossel <dvossel@digium.com> \endverbatim
26  *
27  * This is a conference bridge application utilizing the bridging core.
28  * \ingroup applications
29  */
30
31 /*! \li \ref app_confbridge.c uses the configuration file \ref confbridge.conf
32  * \addtogroup configuration_file Configuration Files
33  */
34
35 /*!
36  * \page confbridge.conf confbridge.conf
37  * \verbinclude confbridge.conf.sample
38  */
39
40 /*** MODULEINFO
41         <support_level>core</support_level>
42  ***/
43
44 #include "asterisk.h"
45
46 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
47
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <string.h>
52 #include <signal.h>
53
54 #include "asterisk/cli.h"
55 #include "asterisk/file.h"
56 #include "asterisk/channel.h"
57 #include "asterisk/pbx.h"
58 #include "asterisk/pbx.h"
59 #include "asterisk/module.h"
60 #include "asterisk/lock.h"
61 #include "asterisk/bridge.h"
62 #include "asterisk/musiconhold.h"
63 #include "asterisk/say.h"
64 #include "asterisk/audiohook.h"
65 #include "asterisk/astobj2.h"
66 #include "confbridge/include/confbridge.h"
67 #include "asterisk/paths.h"
68 #include "asterisk/manager.h"
69 #include "asterisk/test.h"
70 #include "asterisk/stasis.h"
71 #include "asterisk/stasis_bridges.h"
72 #include "asterisk/json.h"
73 #include "asterisk/format_cache.h"
74
75 /*** DOCUMENTATION
76         <application name="ConfBridge" language="en_US">
77                 <synopsis>
78                         Conference bridge application.
79                 </synopsis>
80                 <syntax>
81                         <parameter name="conference" required="true">
82                                 <para>Name of the conference bridge.  You are not limited to just
83                                 numbers.</para>
84                         </parameter>
85                         <parameter name="bridge_profile">
86                                 <para>The bridge profile name from confbridge.conf.  When left blank,
87                                 a dynamically built bridge profile created by the CONFBRIDGE dialplan
88                                 function is searched for on the channel and used.  If no dynamic
89                                 profile is present, the 'default_bridge' profile found in
90                                 confbridge.conf is used. </para>
91                                 <para>It is important to note that while user profiles may be unique
92                                 for each participant, mixing bridge profiles on a single conference
93                                 is _NOT_ recommended and will produce undefined results.</para>
94                         </parameter>
95                         <parameter name="user_profile">
96                                 <para>The user profile name from confbridge.conf.  When left blank,
97                                 a dynamically built user profile created by the CONFBRIDGE dialplan
98                                 function is searched for on the channel and used.  If no dynamic
99                                 profile is present, the 'default_user' profile found in
100                                 confbridge.conf is used.</para>
101                         </parameter>
102                         <parameter name="menu">
103                                 <para>The name of the DTMF menu in confbridge.conf to be applied to
104                                 this channel.  When left blank, a dynamically built menu profile
105                                 created by the CONFBRIDGE dialplan function is searched for on
106                                 the channel and used. If no dynamic profile is present, the
107                                 'default_menu' profile found in confbridge.conf is used.</para>
108                         </parameter>
109                 </syntax>
110                 <description>
111                         <para>Enters the user into a specified conference bridge.  The user can
112                         exit the conference by hangup or DTMF menu option.</para>
113                         <para>This application sets the following channel variable upon completion:</para>
114                         <variablelist>
115                                 <variable name="CONFBRIDGE_RESULT">
116                                         <value name="FAILED">The channel encountered an error and could not enter the conference.</value>
117                                         <value name="HANGUP">The channel exited the conference by hanging up.</value>
118                                         <value name="KICKED">The channel was kicked from the conference.</value>
119                                         <value name="ENDMARKED">The channel left the conference as a result of the last marked user leaving.</value>
120                                         <value name="DTMF">The channel pressed a DTMF sequence to exit the conference.</value>
121                                 </variable>
122                         </variablelist>
123                 </description>
124                 <see-also>
125                         <ref type="application">ConfBridge</ref>
126                         <ref type="function">CONFBRIDGE</ref>
127                         <ref type="function">CONFBRIDGE_INFO</ref>
128                 </see-also>
129         </application>
130         <function name="CONFBRIDGE" language="en_US">
131                 <synopsis>
132                         Set a custom dynamic bridge, user, or menu profile on a channel for the ConfBridge application using the same options defined in confbridge.conf.
133                 </synopsis>
134                 <syntax>
135                         <parameter name="type" required="true">
136                                 <para>Type refers to which type of profile the option belongs too.  Type can be <literal>bridge</literal>, <literal>user</literal>, or
137                                 <literal>menu</literal>.</para>
138                         </parameter>
139                         <parameter name="option" required="true">
140                                 <para>Option refers to <filename>confbridge.conf</filename> option that is being set dynamically on this channel, or
141                                 <literal>clear</literal> to remove already applied options from the channel.</para>
142                         </parameter>
143                 </syntax>
144                 <description>
145                         <para>---- Example 1 ----</para>
146                         <para>In this example the custom set user profile on this channel will automatically be used by the ConfBridge app.</para>
147                         <para>exten => 1,1,Answer() </para>
148                         <para>exten => 1,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)</para>
149                         <para>exten => 1,n,Set(CONFBRIDGE(user,startmuted)=yes)</para>
150                         <para>exten => 1,n,ConfBridge(1) </para>
151                         <para>---- Example 2 ----</para>
152                         <para>This example shows how to use a predefined user or bridge profile in confbridge.conf as a template for a dynamic profile. Here we make a admin/marked user out of the default_user profile that is already defined in confbridge.conf.</para>
153                         <para>exten => 1,1,Answer() </para>
154                         <para>exten => 1,n,Set(CONFBRIDGE(user,template)=default_user)</para>
155                         <para>exten => 1,n,Set(CONFBRIDGE(user,admin)=yes)</para>
156                         <para>exten => 1,n,Set(CONFBRIDGE(user,marked)=yes)</para>
157                         <para>exten => 1,n,ConfBridge(1)</para>
158                 </description>
159         </function>
160         <function name="CONFBRIDGE_INFO" language="en_US">
161                 <synopsis>
162                         Get information about a ConfBridge conference.
163                 </synopsis>
164                 <syntax>
165                         <parameter name="type" required="true">
166                                 <para>Type can be <literal>parties</literal>, <literal>admins</literal>, <literal>marked</literal>, or <literal>locked</literal>.</para>
167                         </parameter>
168                         <parameter name="conf" required="true">
169                                 <para>Conf refers to the name of the conference being referenced.</para>
170                         </parameter>
171                 </syntax>
172                 <description>
173                         <para>This function returns a non-negative integer for valid conference identifiers (0 or 1 for <literal>locked</literal>) and "" for invalid conference identifiers.</para>
174                 </description>
175         </function>
176         <manager name="ConfbridgeList" language="en_US">
177                 <synopsis>
178                         List participants in a conference.
179                 </synopsis>
180                 <syntax>
181                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
182                         <parameter name="Conference" required="true">
183                                 <para>Conference number.</para>
184                         </parameter>
185                 </syntax>
186                 <description>
187                         <para>Lists all users in a particular ConfBridge conference.
188                         ConfbridgeList will follow as separate events, followed by a final event called
189                         ConfbridgeListComplete.</para>
190                 </description>
191         </manager>
192         <manager name="ConfbridgeListRooms" language="en_US">
193                 <synopsis>
194                         List active conferences.
195                 </synopsis>
196                 <syntax>
197                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
198                 </syntax>
199                 <description>
200                         <para>Lists data about all active conferences.
201                                 ConfbridgeListRooms will follow as separate events, followed by a final event called
202                                 ConfbridgeListRoomsComplete.</para>
203                 </description>
204         </manager>
205         <manager name="ConfbridgeMute" language="en_US">
206                 <synopsis>
207                         Mute a Confbridge user.
208                 </synopsis>
209                 <syntax>
210                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
211                         <parameter name="Conference" required="true" />
212                         <parameter name="Channel" required="true">
213                                 <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
214                                 <para>If this parameter is "all", all channels will be muted.</para>
215                                 <para>If this parameter is "participants", all non-admin channels will be muted.</para>
216                         </parameter>
217                 </syntax>
218                 <description>
219                 </description>
220         </manager>
221         <manager name="ConfbridgeUnmute" language="en_US">
222                 <synopsis>
223                         Unmute a Confbridge user.
224                 </synopsis>
225                 <syntax>
226                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
227                         <parameter name="Conference" required="true" />
228                         <parameter name="Channel" required="true">
229                                 <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
230                                 <para>If this parameter is "all", all channels will be unmuted.</para>
231                                 <para>If this parameter is "participants", all non-admin channels will be unmuted.</para>
232                         </parameter>
233                 </syntax>
234                 <description>
235                 </description>
236         </manager>
237         <manager name="ConfbridgeKick" language="en_US">
238                 <synopsis>
239                         Kick a Confbridge user.
240                 </synopsis>
241                 <syntax>
242                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
243                         <parameter name="Conference" required="true" />
244                         <parameter name="Channel" required="true" >
245                                 <para>If this parameter is "all", all channels will be kicked from the conference.</para>
246                                 <para>If this parameter is "participants", all non-admin channels will be kicked from the conference.</para>
247                         </parameter>
248                 </syntax>
249                 <description>
250                 </description>
251         </manager>
252         <manager name="ConfbridgeLock" language="en_US">
253                 <synopsis>
254                         Lock a Confbridge conference.
255                 </synopsis>
256                 <syntax>
257                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
258                         <parameter name="Conference" required="true" />
259                 </syntax>
260                 <description>
261                 </description>
262         </manager>
263         <manager name="ConfbridgeUnlock" language="en_US">
264                 <synopsis>
265                         Unlock a Confbridge conference.
266                 </synopsis>
267                 <syntax>
268                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
269                         <parameter name="Conference" required="true" />
270                 </syntax>
271                 <description>
272                 </description>
273         </manager>
274         <manager name="ConfbridgeStartRecord" language="en_US">
275                 <synopsis>
276                         Start recording a Confbridge conference.
277                 </synopsis>
278                 <syntax>
279                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
280                         <parameter name="Conference" required="true" />
281                         <parameter name="RecordFile" required="false" />
282                 </syntax>
283                 <description>
284                         <para>Start recording a conference. If recording is already present an error will be returned. If RecordFile is not provided, the default record file specified in the conference's bridge profile will be used, if that is not present either a file will automatically be generated in the monitor directory.</para>
285                 </description>
286         </manager>
287         <manager name="ConfbridgeStopRecord" language="en_US">
288                 <synopsis>
289                         Stop recording a Confbridge conference.
290                 </synopsis>
291                 <syntax>
292                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
293                         <parameter name="Conference" required="true" />
294                 </syntax>
295                 <description>
296                 </description>
297         </manager>
298         <manager name="ConfbridgeSetSingleVideoSrc" language="en_US">
299                 <synopsis>
300                         Set a conference user as the single video source distributed to all other participants.
301                 </synopsis>
302                 <syntax>
303                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
304                         <parameter name="Conference" required="true" />
305                         <parameter name="Channel" required="true">
306                                 <para>If this parameter is not a complete channel name, the first channel with this prefix will be used.</para>
307                         </parameter>
308                 </syntax>
309                 <description>
310                 </description>
311         </manager>
312
313 ***/
314
315 /*!
316  * \par Playing back a file to a channel in a conference
317  * You might notice in this application that while playing a sound file
318  * to a channel the actual conference bridge lock is not held. This is done so
319  * that other channels are not blocked from interacting with the conference bridge.
320  * Unfortunately because of this it is possible for things to change after the sound file
321  * is done being played. Data must therefore be checked after reacquiring the conference
322  * bridge lock if it is important.
323  */
324
325 static const char app[] = "ConfBridge";
326
327 /*! Number of buckets our conference bridges container can have */
328 #define CONFERENCE_BRIDGE_BUCKETS 53
329
330 /*! Initial recording filename space. */
331 #define RECORD_FILENAME_INITIAL_SPACE   128
332
333 /*! \brief Container to hold all conference bridges in progress */
334 struct ao2_container *conference_bridges;
335
336 static void leave_conference(struct confbridge_user *user);
337 static int play_sound_number(struct confbridge_conference *conference, int say_number);
338 static int execute_menu_entry(struct confbridge_conference *conference,
339         struct confbridge_user *user,
340         struct ast_bridge_channel *bridge_channel,
341         struct conf_menu_entry *menu_entry,
342         struct conf_menu *menu);
343
344 /*! \brief Hashing function used for conference bridges container */
345 static int conference_bridge_hash_cb(const void *obj, const int flags)
346 {
347         const struct confbridge_conference *conference = obj;
348         const char *name = obj;
349         int hash;
350
351         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
352         default:
353         case OBJ_POINTER:
354                 name = conference->name;
355                 /* Fall through */
356         case OBJ_KEY:
357                 hash = ast_str_case_hash(name);
358                 break;
359         case OBJ_PARTIAL_KEY:
360                 /* Should never happen in hash callback. */
361                 ast_assert(0);
362                 hash = 0;
363                 break;
364         }
365         return hash;
366 }
367
368 /*! \brief Comparison function used for conference bridges container */
369 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
370 {
371         const struct confbridge_conference *left = obj;
372         const struct confbridge_conference *right = arg;
373         const char *right_name = arg;
374         int cmp;
375
376         switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
377         default:
378         case OBJ_POINTER:
379                 right_name = right->name;
380                 /* Fall through */
381         case OBJ_KEY:
382                 cmp = strcasecmp(left->name, right_name);
383                 break;
384         case OBJ_PARTIAL_KEY:
385                 cmp = strncasecmp(left->name, right_name, strlen(right_name));
386                 break;
387         }
388         return cmp ? 0 : CMP_MATCH;
389 }
390
391 const char *conf_get_sound(enum conf_sounds sound, struct bridge_profile_sounds *custom_sounds)
392 {
393         switch (sound) {
394         case CONF_SOUND_HAS_JOINED:
395                 return S_OR(custom_sounds->hasjoin, "conf-hasjoin");
396         case CONF_SOUND_HAS_LEFT:
397                 return S_OR(custom_sounds->hasleft, "conf-hasleft");
398         case CONF_SOUND_KICKED:
399                 return S_OR(custom_sounds->kicked, "conf-kicked");
400         case CONF_SOUND_MUTED:
401                 return S_OR(custom_sounds->muted, "conf-muted");
402         case CONF_SOUND_UNMUTED:
403                 return S_OR(custom_sounds->unmuted, "conf-unmuted");
404         case CONF_SOUND_ONLY_ONE:
405                 return S_OR(custom_sounds->onlyone, "conf-onlyone");
406         case CONF_SOUND_THERE_ARE:
407                 return S_OR(custom_sounds->thereare, "conf-thereare");
408         case CONF_SOUND_OTHER_IN_PARTY:
409                 return S_OR(custom_sounds->otherinparty, "conf-otherinparty");
410         case CONF_SOUND_PLACE_IN_CONF:
411                 return S_OR(custom_sounds->placeintoconf, "conf-placeintoconf");
412         case CONF_SOUND_WAIT_FOR_LEADER:
413                 return S_OR(custom_sounds->waitforleader, "conf-waitforleader");
414         case CONF_SOUND_LEADER_HAS_LEFT:
415                 return S_OR(custom_sounds->leaderhasleft, "conf-leaderhasleft");
416         case CONF_SOUND_GET_PIN:
417                 return S_OR(custom_sounds->getpin, "conf-getpin");
418         case CONF_SOUND_INVALID_PIN:
419                 return S_OR(custom_sounds->invalidpin, "conf-invalidpin");
420         case CONF_SOUND_ONLY_PERSON:
421                 return S_OR(custom_sounds->onlyperson, "conf-onlyperson");
422         case CONF_SOUND_LOCKED:
423                 return S_OR(custom_sounds->locked, "conf-locked");
424         case CONF_SOUND_LOCKED_NOW:
425                 return S_OR(custom_sounds->lockednow, "conf-lockednow");
426         case CONF_SOUND_UNLOCKED_NOW:
427                 return S_OR(custom_sounds->unlockednow, "conf-unlockednow");
428         case CONF_SOUND_ERROR_MENU:
429                 return S_OR(custom_sounds->errormenu, "conf-errormenu");
430         case CONF_SOUND_JOIN:
431                 return S_OR(custom_sounds->join, "confbridge-join");
432         case CONF_SOUND_LEAVE:
433                 return S_OR(custom_sounds->leave, "confbridge-leave");
434         case CONF_SOUND_PARTICIPANTS_MUTED:
435                 return S_OR(custom_sounds->participantsmuted, "conf-now-muted");
436         case CONF_SOUND_PARTICIPANTS_UNMUTED:
437                 return S_OR(custom_sounds->participantsunmuted, "conf-now-unmuted");
438         case CONF_SOUND_BEGIN:
439                 return S_OR(custom_sounds->begin, "confbridge-conf-begin");
440         }
441
442         return "";
443 }
444
445 static void send_conf_stasis(struct confbridge_conference *conference, struct ast_channel *chan,
446         struct stasis_message_type *type, struct ast_json *extras, int channel_topic)
447 {
448         RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
449         RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
450
451         json_object = ast_json_pack("{s: s}",
452                 "conference", conference->name);
453         if (!json_object) {
454                 return;
455         }
456
457         if (extras) {
458                 ast_json_object_update(json_object, extras);
459         }
460
461         ast_bridge_lock(conference->bridge);
462         msg = ast_bridge_blob_create(type,
463                 conference->bridge,
464                 chan,
465                 json_object);
466         ast_bridge_unlock(conference->bridge);
467         if (!msg) {
468                 return;
469         }
470
471         if (channel_topic) {
472                 stasis_publish(ast_channel_topic(chan), msg);
473         } else {
474                 stasis_publish(ast_bridge_topic(conference->bridge), msg);
475         }
476 }
477
478 static void send_conf_start_event(struct confbridge_conference *conference)
479 {
480         send_conf_stasis(conference, NULL, confbridge_start_type(), NULL, 0);
481 }
482
483 static void send_conf_end_event(struct confbridge_conference *conference)
484 {
485         send_conf_stasis(conference, NULL, confbridge_end_type(), NULL, 0);
486 }
487
488 static void send_join_event(struct confbridge_user *user, struct confbridge_conference *conference)
489 {
490         struct ast_json *json_object;
491
492         json_object = ast_json_pack("{s: b}",
493                 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
494         );
495         if (!json_object) {
496                 return;
497         }
498         send_conf_stasis(conference, user->chan, confbridge_join_type(), json_object, 0);
499         ast_json_unref(json_object);
500 }
501
502 static void send_leave_event(struct confbridge_user *user, struct confbridge_conference *conference)
503 {
504         struct ast_json *json_object;
505
506         json_object = ast_json_pack("{s: b}",
507                 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
508         );
509         if (!json_object) {
510                 return;
511         }
512         send_conf_stasis(conference, user->chan, confbridge_leave_type(), json_object, 0);
513         ast_json_unref(json_object);
514 }
515
516 static void send_start_record_event(struct confbridge_conference *conference)
517 {
518         send_conf_stasis(conference, NULL, confbridge_start_record_type(), NULL, 0);
519 }
520
521 static void send_stop_record_event(struct confbridge_conference *conference)
522 {
523         send_conf_stasis(conference, NULL, confbridge_stop_record_type(), NULL, 0);
524 }
525
526 static void send_mute_event(struct confbridge_user *user, struct confbridge_conference *conference)
527 {
528         struct ast_json *json_object;
529
530         json_object = ast_json_pack("{s: b}",
531                 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
532         );
533         if (!json_object) {
534                 return;
535         }
536         send_conf_stasis(conference, user->chan, confbridge_mute_type(), json_object, 1);
537         ast_json_unref(json_object);
538 }
539
540 static void send_unmute_event(struct confbridge_user *user, struct confbridge_conference *conference)
541 {
542         struct ast_json *json_object;
543
544         json_object = ast_json_pack("{s: b}",
545                 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN)
546         );
547         if (!json_object) {
548                 return;
549         }
550         send_conf_stasis(conference, user->chan, confbridge_unmute_type(), json_object, 1);
551         ast_json_unref(json_object);
552 }
553
554 static void set_rec_filename(struct confbridge_conference *conference, struct ast_str **filename, int is_new)
555 {
556         char *rec_file = conference->b_profile.rec_file;
557         char *ext;
558         time_t now;
559
560         if (ast_str_strlen(*filename)
561                 && ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND)
562                 && !is_new) {
563                 return;
564         }
565
566         time(&now);
567
568         ast_str_reset(*filename);
569         if (ast_strlen_zero(rec_file)) {
570                 ast_str_set(filename, 0, "confbridge-%s-%u.wav", conference->name,
571                         (unsigned int) now);
572         } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_TIMESTAMP)) {
573                 /* insert time before file extension */
574                 ext = strrchr(rec_file, '.');
575                 if (ext) {
576                         ast_str_set_substr(filename, 0, rec_file, ext - rec_file);
577                         ast_str_append(filename, 0, "-%u%s", (unsigned int) now, ext);
578                 } else {
579                         ast_str_set(filename, 0, "%s-%u", rec_file, (unsigned int) now);
580                 }
581         } else {
582                 ast_str_set(filename, 0, "%s", rec_file);
583         }
584         ast_str_append(filename, 0, ",%s%s,%s",
585                 ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_FILE_APPEND) ? "a" : "",
586                 conference->b_profile.rec_options,
587                 conference->b_profile.rec_command);
588 }
589
590 static int is_new_rec_file(const char *rec_file, struct ast_str **orig_rec_file)
591 {
592         if (!ast_strlen_zero(rec_file)) {
593                 if (!*orig_rec_file) {
594                         *orig_rec_file = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
595                 }
596
597                 if (*orig_rec_file
598                         && strcmp(ast_str_buffer(*orig_rec_file), rec_file)) {
599                         ast_str_set(orig_rec_file, 0, "%s", rec_file);
600                         return 1;
601                 }
602         }
603         return 0;
604 }
605
606 /*!
607  * \internal
608  * \brief Returns whether or not conference is being recorded.
609  *
610  * \param conference The bridge to check for recording
611  *
612  * \note Must be called with the conference locked
613  *
614  * \retval 1, conference is recording.
615  * \retval 0, conference is NOT recording.
616  */
617 static int conf_is_recording(struct confbridge_conference *conference)
618 {
619         return conference->record_chan != NULL;
620 }
621
622 /*!
623  * \internal
624  * \brief Stop recording a conference bridge
625  *
626  * \param conference The conference bridge on which to stop the recording
627  *
628  * \note Must be called with the conference locked
629  *
630  * \retval -1 Failure
631  * \retval 0 Success
632  */
633 static int conf_stop_record(struct confbridge_conference *conference)
634 {
635         struct ast_channel *chan;
636         struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_HANGUP };
637
638         if (!conf_is_recording(conference)) {
639                 return -1;
640         }
641
642         /* Remove the recording channel from the conference bridge. */
643         chan = conference->record_chan;
644         conference->record_chan = NULL;
645         ast_queue_frame(chan, &f);
646         ast_channel_unref(chan);
647
648         ast_test_suite_event_notify("CONF_STOP_RECORD", "Message: stopped conference recording channel\r\nConference: %s", conference->b_profile.name);
649         send_stop_record_event(conference);
650
651         return 0;
652 }
653
654 /*!
655  * \internal
656  * \brief Start recording the conference
657  *
658  * \param conference The conference bridge to start recording
659  *
660  * \note Must be called with the conference locked
661  *
662  * \retval 0 success
663  * \retval non-zero failure
664  */
665 static int conf_start_record(struct confbridge_conference *conference)
666 {
667         struct ast_app *mixmonapp;
668         struct ast_channel *chan;
669         struct ast_format_cap *cap;
670         struct ast_bridge_features *features;
671
672         if (conf_is_recording(conference)) {
673                 return -1;
674         }
675
676         mixmonapp = pbx_findapp("MixMonitor");
677         if (!mixmonapp) {
678                 ast_log(LOG_WARNING, "Cannot record ConfBridge, MixMonitor app is not installed\n");
679                 return -1;
680         }
681
682         features = ast_bridge_features_new();
683         if (!features) {
684                 return -1;
685         }
686         ast_set_flag(&features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE);
687
688         cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
689         if (!cap) {
690                 ast_bridge_features_destroy(features);
691                 return -1;
692         }
693         ast_format_cap_append(cap, ast_format_slin, 0);
694
695         /* Create the recording channel. */
696         chan = ast_request("CBRec", cap, NULL, NULL, conference->name, NULL);
697         ao2_ref(cap, -1);
698         if (!chan) {
699                 ast_bridge_features_destroy(features);
700                 return -1;
701         }
702
703         /* Start recording. */
704         set_rec_filename(conference, &conference->record_filename,
705                 is_new_rec_file(conference->b_profile.rec_file, &conference->orig_rec_file));
706         ast_answer(chan);
707         pbx_exec(chan, mixmonapp, ast_str_buffer(conference->record_filename));
708
709         /* Put the channel into the conference bridge. */
710         ast_channel_ref(chan);
711         conference->record_chan = chan;
712         if (ast_bridge_impart(conference->bridge, chan, NULL, features,
713                 AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
714                 ast_hangup(chan);
715                 ast_channel_unref(chan);
716                 conference->record_chan = NULL;
717                 return -1;
718         }
719
720         ast_test_suite_event_notify("CONF_START_RECORD", "Message: started conference recording channel\r\nConference: %s", conference->b_profile.name);
721         send_start_record_event(conference);
722
723         return 0;
724 }
725
726 /*!
727  * \internal
728  * \brief Complain if the given sound file does not exist.
729  *
730  * \param filename Sound file to check if exists.
731  *
732  * \retval non-zero if the file exists.
733  */
734 static int sound_file_exists(const char *filename)
735 {
736         if (ast_fileexists(filename, NULL, NULL)) {
737                 return -1;
738         }
739         ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
740         return 0;
741 }
742
743 /*!
744  * \brief Announce number of users in the conference bridge to the caller
745  *
746  * \param conference Conference bridge to peek at
747  * \param user Optional Caller
748  *
749  * \note if caller is NULL, the announcment will be sent to all participants in the conference.
750  * \return Returns 0 on success, -1 if the user hung up
751  */
752 static int announce_user_count(struct confbridge_conference *conference, struct confbridge_user *user)
753 {
754         const char *other_in_party = conf_get_sound(CONF_SOUND_OTHER_IN_PARTY, conference->b_profile.sounds);
755         const char *only_one = conf_get_sound(CONF_SOUND_ONLY_ONE, conference->b_profile.sounds);
756         const char *there_are = conf_get_sound(CONF_SOUND_THERE_ARE, conference->b_profile.sounds);
757
758         if (conference->activeusers <= 1) {
759                 /* Awww we are the only person in the conference bridge OR we only have waitmarked users */
760                 return 0;
761         } else if (conference->activeusers == 2) {
762                 if (user) {
763                         /* Eep, there is one other person */
764                         if (ast_stream_and_wait(user->chan,
765                                 only_one,
766                                 "")) {
767                                 return -1;
768                         }
769                 } else {
770                         play_sound_file(conference, only_one);
771                 }
772         } else {
773                 /* Alas multiple others in here */
774                 if (user) {
775                         if (ast_stream_and_wait(user->chan,
776                                 there_are,
777                                 "")) {
778                                 return -1;
779                         }
780                         if (ast_say_number(user->chan, conference->activeusers - 1, "", ast_channel_language(user->chan), NULL)) {
781                                 return -1;
782                         }
783                         if (ast_stream_and_wait(user->chan,
784                                 other_in_party,
785                                 "")) {
786                                 return -1;
787                         }
788                 } else if (sound_file_exists(there_are) && sound_file_exists(other_in_party)) {
789                         play_sound_file(conference, there_are);
790                         play_sound_number(conference, conference->activeusers - 1);
791                         play_sound_file(conference, other_in_party);
792                 }
793         }
794         return 0;
795 }
796
797 /*!
798  * \brief Play back an audio file to a channel
799  *
800  * \param user User to play audio prompt to
801  * \param filename Prompt to play
802  *
803  * \return Returns 0 on success, -1 if the user hung up
804  * \note Generally this should be called when the conference is unlocked to avoid blocking
805  * the entire conference while the sound is played. But don't unlock the conference bridge
806  * in the middle of a state transition.
807  */
808 static int play_prompt_to_user(struct confbridge_user *user, const char *filename)
809 {
810         return ast_stream_and_wait(user->chan, filename, "");
811 }
812
813 static void handle_video_on_join(struct confbridge_conference *conference, struct ast_channel *chan, int marked)
814 {
815         /* Right now, only marked users are automatically set as the single src of video.*/
816         if (!marked) {
817                 return;
818         }
819
820         if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED)) {
821                 int set = 1;
822                 struct confbridge_user *user = NULL;
823
824                 ao2_lock(conference);
825                 /* see if anyone is already the video src */
826                 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
827                         if (user->chan == chan) {
828                                 continue;
829                         }
830                         if (ast_bridge_is_video_src(conference->bridge, user->chan)) {
831                                 set = 0;
832                                 break;
833                         }
834                 }
835                 ao2_unlock(conference);
836                 if (set) {
837                         ast_bridge_set_single_src_video_mode(conference->bridge, chan);
838                 }
839         } else if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
840                 /* we joined and are video capable, we override anyone else that may have already been the video feed */
841                 ast_bridge_set_single_src_video_mode(conference->bridge, chan);
842         }
843 }
844
845 static void handle_video_on_exit(struct confbridge_conference *conference, struct ast_channel *chan)
846 {
847         struct confbridge_user *user = NULL;
848
849         /* if this isn't a video source, nothing to update */
850         if (!ast_bridge_is_video_src(conference->bridge, chan)) {
851                 return;
852         }
853
854         ast_bridge_remove_video_src(conference->bridge, chan);
855
856         /* If in follow talker mode, make sure to restore this mode on the
857          * bridge when a source is removed.  It is possible this channel was
858          * only set temporarily as a video source by an AMI or DTMF action. */
859         if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
860                 ast_bridge_set_talker_src_video_mode(conference->bridge);
861         }
862
863         /* if the video_mode isn't set to automatically pick the video source, do nothing on exit. */
864         if (!ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FIRST_MARKED) &&
865                 !ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_LAST_MARKED)) {
866                 return;
867         }
868
869         /* Make the next available marked user the video src.  */
870         ao2_lock(conference);
871         AST_LIST_TRAVERSE(&conference->active_list, user, list) {
872                 if (user->chan == chan) {
873                         continue;
874                 }
875                 if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
876                         ast_bridge_set_single_src_video_mode(conference->bridge, user->chan);
877                         break;
878                 }
879         }
880         ao2_unlock(conference);
881 }
882
883 /*!
884  * \brief Destroy a conference bridge
885  *
886  * \param obj The conference bridge object
887  *
888  * \return Returns nothing
889  */
890 static void destroy_conference_bridge(void *obj)
891 {
892         struct confbridge_conference *conference = obj;
893
894         ast_debug(1, "Destroying conference bridge '%s'\n", conference->name);
895
896         if (conference->playback_chan) {
897                 conf_announce_channel_depart(conference->playback_chan);
898                 ast_hangup(conference->playback_chan);
899                 conference->playback_chan = NULL;
900         }
901
902         /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
903         if (conference->bridge) {
904                 ast_bridge_destroy(conference->bridge, 0);
905                 conference->bridge = NULL;
906         }
907
908         ast_channel_cleanup(conference->record_chan);
909         ast_free(conference->orig_rec_file);
910         ast_free(conference->record_filename);
911
912         conf_bridge_profile_destroy(&conference->b_profile);
913         ast_mutex_destroy(&conference->playback_lock);
914 }
915
916 /*! \brief Call the proper join event handler for the user for the conference bridge's current state
917  * \internal
918  * \param user The conference bridge user that is joining
919  * \retval 0 success
920  * \retval -1 failure
921  */
922 static int handle_conf_user_join(struct confbridge_user *user)
923 {
924         conference_event_fn handler;
925         if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
926                 handler = user->conference->state->join_marked;
927         } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
928                 handler = user->conference->state->join_waitmarked;
929         } else {
930                 handler = user->conference->state->join_unmarked;
931         }
932
933         ast_assert(handler != NULL);
934
935         if (!handler) {
936                 conf_invalid_event_fn(user);
937                 return -1;
938         }
939
940         handler(user);
941
942         return 0;
943 }
944
945 /*! \brief Call the proper leave event handler for the user for the conference bridge's current state
946  * \internal
947  * \param user The conference bridge user that is leaving
948  * \retval 0 success
949  * \retval -1 failure
950  */
951 static int handle_conf_user_leave(struct confbridge_user *user)
952 {
953         conference_event_fn handler;
954         if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
955                 handler = user->conference->state->leave_marked;
956         } else if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
957                 handler = user->conference->state->leave_waitmarked;
958         } else {
959                 handler = user->conference->state->leave_unmarked;
960         }
961
962         ast_assert(handler != NULL);
963
964         if (!handler) {
965                 /* This should never happen. If it does, though, it is bad. The user will not have been removed
966                  * from the appropriate list, so counts will be off and stuff. The conference won't be torn down, etc.
967                  * Shouldn't happen, though. */
968                 conf_invalid_event_fn(user);
969                 return -1;
970         }
971
972         handler(user);
973
974         return 0;
975 }
976
977 void conf_update_user_mute(struct confbridge_user *user)
978 {
979         int mute_user;
980         int mute_system;
981         int mute_effective;
982
983         /* User level mute request. */
984         mute_user = user->muted;
985
986         /* System level mute request. */
987         mute_system = user->playing_moh
988                 /*
989                  * Do not allow waitmarked users to talk to anyone unless there
990                  * is a marked user present.
991                  */
992                 || (!user->conference->markedusers
993                         && ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED));
994
995         mute_effective = mute_user || mute_system;
996
997         ast_debug(1, "User %s is %s: user:%d system:%d.\n",
998                 ast_channel_name(user->chan), mute_effective ? "muted" : "unmuted",
999                 mute_user, mute_system);
1000         user->features.mute = mute_effective;
1001         ast_test_suite_event_notify("CONF_MUTE_UPDATE",
1002                 "Mode: %s\r\n"
1003                 "Conference: %s\r\n"
1004                 "Channel: %s",
1005                 mute_effective ? "muted" : "unmuted",
1006                 user->b_profile.name,
1007                 ast_channel_name(user->chan));
1008 }
1009
1010 void conf_moh_stop(struct confbridge_user *user)
1011 {
1012         user->playing_moh = 0;
1013         if (!user->suspended_moh) {
1014                 int in_bridge;
1015
1016                 /*
1017                  * Locking the ast_bridge here is the only way to hold off the
1018                  * call to ast_bridge_join() in confbridge_exec() from
1019                  * interfering with the bridge and MOH operations here.
1020                  */
1021                 ast_bridge_lock(user->conference->bridge);
1022
1023                 /*
1024                  * Temporarily suspend the user from the bridge so we have
1025                  * control to stop MOH if needed.
1026                  */
1027                 in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1028                 ast_moh_stop(user->chan);
1029                 if (in_bridge) {
1030                         ast_bridge_unsuspend(user->conference->bridge, user->chan);
1031                 }
1032
1033                 ast_bridge_unlock(user->conference->bridge);
1034         }
1035 }
1036
1037 void conf_moh_start(struct confbridge_user *user)
1038 {
1039         user->playing_moh = 1;
1040         if (!user->suspended_moh) {
1041                 int in_bridge;
1042
1043                 /*
1044                  * Locking the ast_bridge here is the only way to hold off the
1045                  * call to ast_bridge_join() in confbridge_exec() from
1046                  * interfering with the bridge and MOH operations here.
1047                  */
1048                 ast_bridge_lock(user->conference->bridge);
1049
1050                 /*
1051                  * Temporarily suspend the user from the bridge so we have
1052                  * control to start MOH if needed.
1053                  */
1054                 in_bridge = !ast_bridge_suspend(user->conference->bridge, user->chan);
1055                 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1056                 if (in_bridge) {
1057                         ast_bridge_unsuspend(user->conference->bridge, user->chan);
1058                 }
1059
1060                 ast_bridge_unlock(user->conference->bridge);
1061         }
1062 }
1063
1064 /*!
1065  * \internal
1066  * \brief Unsuspend MOH for the conference user.
1067  *
1068  * \param user Conference user to unsuspend MOH on.
1069  *
1070  * \return Nothing
1071  */
1072 static void conf_moh_unsuspend(struct confbridge_user *user)
1073 {
1074         ao2_lock(user->conference);
1075         if (--user->suspended_moh == 0 && user->playing_moh) {
1076                 ast_moh_start(user->chan, user->u_profile.moh_class, NULL);
1077         }
1078         ao2_unlock(user->conference);
1079 }
1080
1081 /*!
1082  * \internal
1083  * \brief Suspend MOH for the conference user.
1084  *
1085  * \param user Conference user to suspend MOH on.
1086  *
1087  * \return Nothing
1088  */
1089 static void conf_moh_suspend(struct confbridge_user *user)
1090 {
1091         ao2_lock(user->conference);
1092         if (user->suspended_moh++ == 0 && user->playing_moh) {
1093                 ast_moh_stop(user->chan);
1094         }
1095         ao2_unlock(user->conference);
1096 }
1097
1098 int conf_handle_inactive_waitmarked(struct confbridge_user *user)
1099 {
1100         /* If we have not been quieted play back that they are waiting for the leader */
1101         if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET) && play_prompt_to_user(user,
1102                         conf_get_sound(CONF_SOUND_WAIT_FOR_LEADER, user->b_profile.sounds))) {
1103                 /* user hungup while the sound was playing */
1104                 return -1;
1105         }
1106         return 0;
1107 }
1108
1109 int conf_handle_only_unmarked(struct confbridge_user *user)
1110 {
1111         /* If audio prompts have not been quieted or this prompt quieted play it on out */
1112         if (!ast_test_flag(&user->u_profile, USER_OPT_QUIET | USER_OPT_NOONLYPERSON)) {
1113                 if (play_prompt_to_user(user,
1114                         conf_get_sound(CONF_SOUND_ONLY_PERSON, user->b_profile.sounds))) {
1115                         /* user hungup while the sound was playing */
1116                         return -1;
1117                 }
1118         }
1119         return 0;
1120 }
1121
1122 int conf_add_post_join_action(struct confbridge_user *user, int (*func)(struct confbridge_user *user))
1123 {
1124         struct post_join_action *action;
1125         if (!(action = ast_calloc(1, sizeof(*action)))) {
1126                 return -1;
1127         }
1128         action->func = func;
1129         AST_LIST_INSERT_TAIL(&user->post_join_list, action, list);
1130         return 0;
1131 }
1132
1133
1134 void conf_handle_first_join(struct confbridge_conference *conference)
1135 {
1136         ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "confbridge:%s", conference->name);
1137 }
1138
1139 void conf_handle_second_active(struct confbridge_conference *conference)
1140 {
1141         /* If we are the second participant we may need to stop music on hold on the first */
1142         struct confbridge_user *first_user = AST_LIST_FIRST(&conference->active_list);
1143
1144         if (ast_test_flag(&first_user->u_profile, USER_OPT_MUSICONHOLD)) {
1145                 conf_moh_stop(first_user);
1146         }
1147         conf_update_user_mute(first_user);
1148 }
1149
1150 void conf_ended(struct confbridge_conference *conference)
1151 {
1152         /* Called with a reference to conference */
1153         ao2_unlink(conference_bridges, conference);
1154         send_conf_end_event(conference);
1155         ao2_lock(conference);
1156         conf_stop_record(conference);
1157         ao2_unlock(conference);
1158 }
1159
1160 /*!
1161  * \brief Join a conference bridge
1162  *
1163  * \param conference_name The conference name
1164  * \param user Conference bridge user structure
1165  *
1166  * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
1167  */
1168 static struct confbridge_conference *join_conference_bridge(const char *conference_name, struct confbridge_user *user)
1169 {
1170         struct confbridge_conference *conference;
1171         struct post_join_action *action;
1172         int max_members_reached = 0;
1173
1174         /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
1175         ao2_lock(conference_bridges);
1176
1177         ast_debug(1, "Trying to find conference bridge '%s'\n", conference_name);
1178
1179         /* Attempt to find an existing conference bridge */
1180         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
1181         if (conference && conference->b_profile.max_members) {
1182                 max_members_reached = conference->b_profile.max_members > conference->activeusers ? 0 : 1;
1183         }
1184
1185         /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
1186         if (conference && (max_members_reached || conference->locked) && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
1187                 ao2_unlock(conference_bridges);
1188                 ao2_ref(conference, -1);
1189                 ast_debug(1, "Conference '%s' is locked and caller is not an admin\n", conference_name);
1190                 ast_stream_and_wait(user->chan,
1191                                 conf_get_sound(CONF_SOUND_LOCKED, user->b_profile.sounds),
1192                                 "");
1193                 return NULL;
1194         }
1195
1196         /* If no conference bridge was found see if we can create one */
1197         if (!conference) {
1198                 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
1199                 if (!(conference = ao2_alloc(sizeof(*conference), destroy_conference_bridge))) {
1200                         ao2_unlock(conference_bridges);
1201                         ast_log(LOG_ERROR, "Conference '%s' could not be created.\n", conference_name);
1202                         return NULL;
1203                 }
1204
1205                 /* Setup lock for playback channel */
1206                 ast_mutex_init(&conference->playback_lock);
1207
1208                 /* Setup for the record channel */
1209                 conference->record_filename = ast_str_create(RECORD_FILENAME_INITIAL_SPACE);
1210                 if (!conference->record_filename) {
1211                         ao2_ref(conference, -1);
1212                         ao2_unlock(conference_bridges);
1213                         return NULL;
1214                 }
1215
1216                 /* Setup conference bridge parameters */
1217                 ast_copy_string(conference->name, conference_name, sizeof(conference->name));
1218                 conf_bridge_profile_copy(&conference->b_profile, &user->b_profile);
1219
1220                 /* Create an actual bridge that will do the audio mixing */
1221                 conference->bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_MULTIMIX,
1222                         AST_BRIDGE_FLAG_MASQUERADE_ONLY | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY,
1223                         app, conference_name, NULL);
1224                 if (!conference->bridge) {
1225                         ao2_ref(conference, -1);
1226                         conference = NULL;
1227                         ao2_unlock(conference_bridges);
1228                         ast_log(LOG_ERROR, "Conference '%s' mixing bridge could not be created.\n", conference_name);
1229                         return NULL;
1230                 }
1231
1232                 /* Set the internal sample rate on the bridge from the bridge profile */
1233                 ast_bridge_set_internal_sample_rate(conference->bridge, conference->b_profile.internal_sample_rate);
1234                 /* Set the internal mixing interval on the bridge from the bridge profile */
1235                 ast_bridge_set_mixing_interval(conference->bridge, conference->b_profile.mix_interval);
1236
1237                 if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_VIDEO_SRC_FOLLOW_TALKER)) {
1238                         ast_bridge_set_talker_src_video_mode(conference->bridge);
1239                 }
1240
1241                 /* Link it into the conference bridges container */
1242                 if (!ao2_link(conference_bridges, conference)) {
1243                         ao2_ref(conference, -1);
1244                         conference = NULL;
1245                         ao2_unlock(conference_bridges);
1246                         ast_log(LOG_ERROR,
1247                                 "Conference '%s' could not be added to the conferences list.\n", conference_name);
1248                         return NULL;
1249                 }
1250
1251                 /* Set the initial state to EMPTY */
1252                 conference->state = CONF_STATE_EMPTY;
1253
1254                 if (ast_test_flag(&conference->b_profile, BRIDGE_OPT_RECORD_CONFERENCE)) {
1255                         ao2_lock(conference);
1256                         conf_start_record(conference);
1257                         ao2_unlock(conference);
1258                 }
1259
1260                 send_conf_start_event(conference);
1261                 ast_debug(1, "Created conference '%s' and linked to container.\n", conference_name);
1262         }
1263
1264         ao2_unlock(conference_bridges);
1265
1266         /* Setup conference bridge user parameters */
1267         user->conference = conference;
1268
1269         ao2_lock(conference);
1270
1271         /*
1272          * Suspend any MOH until the user actually joins the bridge of
1273          * the conference.  This way any pre-join file playback does not
1274          * need to worry about MOH.
1275          */
1276         user->suspended_moh = 1;
1277
1278         if (handle_conf_user_join(user)) {
1279                 /* Invalid event, nothing was done, so we don't want to process a leave. */
1280                 ao2_unlock(conference);
1281                 ao2_ref(conference, -1);
1282                 return NULL;
1283         }
1284
1285         if (ast_check_hangup(user->chan)) {
1286                 ao2_unlock(conference);
1287                 leave_conference(user);
1288                 return NULL;
1289         }
1290
1291         ao2_unlock(conference);
1292
1293         /* If an announcement is to be played play it */
1294         if (!ast_strlen_zero(user->u_profile.announcement)) {
1295                 if (play_prompt_to_user(user,
1296                         user->u_profile.announcement)) {
1297                         leave_conference(user);
1298                         return NULL;
1299                 }
1300         }
1301
1302         /* Announce number of users if need be */
1303         if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNT)) {
1304                 if (announce_user_count(conference, user)) {
1305                         leave_conference(user);
1306                         return NULL;
1307                 }
1308         }
1309
1310         if (ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCEUSERCOUNTALL) &&
1311                 (conference->activeusers > user->u_profile.announce_user_count_all_after)) {
1312                 int user_count_res;
1313
1314                 /*
1315                  * We have to autoservice the new user because he has not quite
1316                  * joined the conference yet.
1317                  */
1318                 ast_autoservice_start(user->chan);
1319                 user_count_res = announce_user_count(conference, NULL);
1320                 ast_autoservice_stop(user->chan);
1321                 if (user_count_res) {
1322                         leave_conference(user);
1323                         return NULL;
1324                 }
1325         }
1326
1327         /* Handle post-join actions */
1328         while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
1329                 action->func(user);
1330                 ast_free(action);
1331         }
1332
1333         return conference;
1334 }
1335
1336 /*!
1337  * \brief Leave a conference
1338  *
1339  * \param user The conference user
1340  */
1341 static void leave_conference(struct confbridge_user *user)
1342 {
1343         struct post_join_action *action;
1344
1345         ao2_lock(user->conference);
1346         handle_conf_user_leave(user);
1347         ao2_unlock(user->conference);
1348
1349         /* Discard any post-join actions */
1350         while ((action = AST_LIST_REMOVE_HEAD(&user->post_join_list, list))) {
1351                 ast_free(action);
1352         }
1353
1354         /* Done mucking with the conference, huzzah */
1355         ao2_ref(user->conference, -1);
1356         user->conference = NULL;
1357 }
1358
1359 /*!
1360  * \internal
1361  * \brief Allocate playback channel for a conference.
1362  * \pre expects conference to be locked before calling this function
1363  */
1364 static int alloc_playback_chan(struct confbridge_conference *conference)
1365 {
1366         struct ast_format_cap *cap;
1367
1368         cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
1369         if (!cap) {
1370                 return -1;
1371         }
1372         ast_format_cap_append(cap, ast_format_slin, 0);
1373         conference->playback_chan = ast_request("CBAnn", cap, NULL, NULL,
1374                 conference->name, NULL);
1375         ao2_ref(cap, -1);
1376         if (!conference->playback_chan) {
1377                 return -1;
1378         }
1379
1380         /* To make sure playback_chan has the same language of that profile */
1381         ast_channel_lock(conference->playback_chan);
1382         ast_channel_language_set(conference->playback_chan, conference->b_profile.language);
1383         ast_channel_unlock(conference->playback_chan);
1384
1385         ast_debug(1, "Created announcer channel '%s' to conference bridge '%s'\n",
1386                 ast_channel_name(conference->playback_chan), conference->name);
1387         return 0;
1388 }
1389
1390 static int play_sound_helper(struct confbridge_conference *conference, const char *filename, int say_number)
1391 {
1392         /* Do not waste resources trying to play files that do not exist */
1393         if (!ast_strlen_zero(filename) && !sound_file_exists(filename)) {
1394                 return 0;
1395         }
1396
1397         ast_mutex_lock(&conference->playback_lock);
1398         if (!conference->playback_chan && alloc_playback_chan(conference)) {
1399                 ast_mutex_unlock(&conference->playback_lock);
1400                 return -1;
1401         }
1402         if (conf_announce_channel_push(conference->playback_chan)) {
1403                 ast_mutex_unlock(&conference->playback_lock);
1404                 return -1;
1405         }
1406
1407         /* The channel is all under our control, in goes the prompt */
1408         if (!ast_strlen_zero(filename)) {
1409                 ast_stream_and_wait(conference->playback_chan, filename, "");
1410         } else if (say_number >= 0) {
1411                 ast_say_number(conference->playback_chan, say_number, "",
1412                         ast_channel_language(conference->playback_chan), NULL);
1413         }
1414
1415         ast_debug(1, "Departing announcer channel '%s' from conference bridge '%s'\n",
1416                 ast_channel_name(conference->playback_chan), conference->name);
1417         conf_announce_channel_depart(conference->playback_chan);
1418
1419         ast_mutex_unlock(&conference->playback_lock);
1420
1421         return 0;
1422 }
1423
1424 int play_sound_file(struct confbridge_conference *conference, const char *filename)
1425 {
1426         return play_sound_helper(conference, filename, -1);
1427 }
1428
1429 /*!
1430  * \brief Play number into the conference bridge
1431  *
1432  * \param conference The conference bridge to say the number into
1433  * \param say_number number to say
1434  *
1435  * \retval 0 success
1436  * \retval -1 failure
1437  */
1438 static int play_sound_number(struct confbridge_conference *conference, int say_number)
1439 {
1440         return play_sound_helper(conference, NULL, say_number);
1441 }
1442
1443 static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *hook_pvt, int talking)
1444 {
1445         const struct confbridge_user *user = hook_pvt;
1446         RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
1447         struct ast_json *talking_extras;
1448
1449         conference = ao2_find(conference_bridges, user->conference->name, OBJ_KEY);
1450         if (!conference) {
1451                 /* Remove the hook since the conference does not exist. */
1452                 return -1;
1453         }
1454
1455         talking_extras = ast_json_pack("{s: s, s: b}",
1456                 "talking_status", talking ? "on" : "off",
1457                 "admin", ast_test_flag(&user->u_profile, USER_OPT_ADMIN));
1458         if (!talking_extras) {
1459                 return 0;
1460         }
1461
1462         send_conf_stasis(conference, bridge_channel->chan, confbridge_talking_type(), talking_extras, 0);
1463         ast_json_unref(talking_extras);
1464         return 0;
1465 }
1466
1467 static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
1468 {
1469         char pin_guess[MAX_PIN+1] = { 0, };
1470         const char *pin = user->u_profile.pin;
1471         char *tmp = pin_guess;
1472         int i, res;
1473         unsigned int len = MAX_PIN ;
1474
1475         /* give them three tries to get the pin right */
1476         for (i = 0; i < 3; i++) {
1477                 if (ast_app_getdata(chan,
1478                         conf_get_sound(CONF_SOUND_GET_PIN, user->b_profile.sounds),
1479                         tmp, len, 0) >= 0) {
1480                         if (!strcasecmp(pin, pin_guess)) {
1481                                 return 0;
1482                         }
1483                 }
1484                 ast_streamfile(chan,
1485                         conf_get_sound(CONF_SOUND_INVALID_PIN, user->b_profile.sounds),
1486                         ast_channel_language(chan));
1487                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1488                 if (res > 0) {
1489                         /* Account for digit already read during ivalid pin playback
1490                          * resetting pin buf. */
1491                         pin_guess[0] = res;
1492                         pin_guess[1] = '\0';
1493                         tmp = pin_guess + 1;
1494                         len = MAX_PIN - 1;
1495                 } else {
1496                         /* reset pin buf as empty buffer. */
1497                         tmp = pin_guess;
1498                         len = MAX_PIN;
1499                 }
1500         }
1501         return -1;
1502 }
1503
1504 static int conf_rec_name(struct confbridge_user *user, const char *conf_name)
1505 {
1506         char destdir[PATH_MAX];
1507         int res;
1508         int duration = 20;
1509
1510         snprintf(destdir, sizeof(destdir), "%s/confbridge", ast_config_AST_SPOOL_DIR);
1511
1512         if (ast_mkdir(destdir, 0777) != 0) {
1513                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
1514                 return -1;
1515         }
1516         snprintf(user->name_rec_location, sizeof(user->name_rec_location),
1517                  "%s/confbridge-name-%s-%s", destdir,
1518                  conf_name, ast_channel_uniqueid(user->chan));
1519
1520         if (!(ast_test_flag(&user->u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW))) {
1521                 res = ast_play_and_record(user->chan,
1522                         "vm-rec-name",
1523                         user->name_rec_location,
1524                         10,
1525                         "sln",
1526                         &duration,
1527                         NULL,
1528                         ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE),
1529                         0,
1530                         NULL);
1531         } else {
1532                 res = ast_record_review(user->chan,
1533                         "vm-rec-name",
1534                         user->name_rec_location,
1535                         10,
1536                         "sln",
1537                         &duration,
1538                         NULL);
1539         }
1540
1541         if (res == -1) {
1542                 user->name_rec_location[0] = '\0';
1543                 return -1;
1544         }
1545         return 0;
1546 }
1547
1548 /*! \brief The ConfBridge application */
1549 static int confbridge_exec(struct ast_channel *chan, const char *data)
1550 {
1551         int res = 0, volume_adjustments[2];
1552         int quiet = 0;
1553         char *parse;
1554         const char *b_profile_name = NULL;
1555         const char *u_profile_name = NULL;
1556         const char *menu_profile_name = NULL;
1557         struct confbridge_conference *conference = NULL;
1558         struct confbridge_user user = {
1559                 .chan = chan,
1560                 .tech_args.talking_threshold = DEFAULT_TALKING_THRESHOLD,
1561                 .tech_args.silence_threshold = DEFAULT_SILENCE_THRESHOLD,
1562                 .tech_args.drop_silence = 0,
1563         };
1564         AST_DECLARE_APP_ARGS(args,
1565                 AST_APP_ARG(conf_name);
1566                 AST_APP_ARG(b_profile_name);
1567                 AST_APP_ARG(u_profile_name);
1568                 AST_APP_ARG(menu_profile_name);
1569         );
1570
1571         if (ast_channel_state(chan) != AST_STATE_UP) {
1572                 ast_answer(chan);
1573         }
1574
1575         if (ast_bridge_features_init(&user.features)) {
1576                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1577                 res = -1;
1578                 goto confbridge_cleanup;
1579         }
1580
1581         /* We need to make a copy of the input string if we are going to modify it! */
1582         parse = ast_strdupa(data);
1583
1584         AST_STANDARD_APP_ARGS(args, parse);
1585
1586         if (ast_strlen_zero(args.conf_name)) {
1587                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1588                 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
1589                 res = -1;
1590                 goto confbridge_cleanup;
1591         }
1592
1593         if (strlen(args.conf_name) >= MAX_CONF_NAME) {
1594                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1595                 ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1);
1596                 res = -1;
1597                 goto confbridge_cleanup;
1598         }
1599
1600         /* bridge profile name */
1601         if (args.argc > 1 && !ast_strlen_zero(args.b_profile_name)) {
1602                 b_profile_name = args.b_profile_name;
1603         }
1604         if (!conf_find_bridge_profile(chan, b_profile_name, &user.b_profile)) {
1605                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1606                 ast_log(LOG_WARNING, "Conference bridge profile %s does not exist\n", b_profile_name ?
1607                         b_profile_name : DEFAULT_BRIDGE_PROFILE);
1608                 res = -1;
1609                 goto confbridge_cleanup;
1610         }
1611
1612         /* user profile name */
1613         if (args.argc > 2 && !ast_strlen_zero(args.u_profile_name)) {
1614                 u_profile_name = args.u_profile_name;
1615         }
1616         if (!conf_find_user_profile(chan, u_profile_name, &user.u_profile)) {
1617                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1618                 ast_log(LOG_WARNING, "Conference user profile %s does not exist\n", u_profile_name ?
1619                         u_profile_name : DEFAULT_USER_PROFILE);
1620                 res = -1;
1621                 goto confbridge_cleanup;
1622         }
1623
1624         quiet = ast_test_flag(&user.u_profile, USER_OPT_QUIET);
1625
1626         /* ask for a PIN immediately after finding user profile.  This has to be
1627          * prompted for requardless of quiet setting. */
1628         if (!ast_strlen_zero(user.u_profile.pin)) {
1629                 if (conf_get_pin(chan, &user)) {
1630                         pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1631                         res = -1; /* invalid PIN */
1632                         goto confbridge_cleanup;
1633                 }
1634         }
1635
1636         /* See if we need them to record a intro name */
1637         if (!quiet &&
1638                 (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE) ||
1639                 (ast_test_flag(&user.u_profile, USER_OPT_ANNOUNCE_JOIN_LEAVE_REVIEW)))) {
1640                 conf_rec_name(&user, args.conf_name);
1641         }
1642
1643         /* menu name */
1644         if (args.argc > 3 && !ast_strlen_zero(args.menu_profile_name)) {
1645                 menu_profile_name = args.menu_profile_name;
1646         }
1647
1648         if (conf_set_menu_to_user(chan, &user, menu_profile_name)) {
1649                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1650                 ast_log(LOG_WARNING, "Conference menu profile %s does not exist\n", menu_profile_name ?
1651                         menu_profile_name : DEFAULT_MENU_PROFILE);
1652                 res = -1;
1653                 goto confbridge_cleanup;
1654         }
1655
1656         /* Set if DTMF should pass through for this user or not */
1657         if (ast_test_flag(&user.u_profile, USER_OPT_DTMF_PASS)) {
1658                 user.features.dtmf_passthrough = 1;
1659         } else {
1660                 user.features.dtmf_passthrough = 0;
1661         }
1662
1663         /* Set dsp threshold values if present */
1664         if (user.u_profile.talking_threshold) {
1665                 user.tech_args.talking_threshold = user.u_profile.talking_threshold;
1666         }
1667         if (user.u_profile.silence_threshold) {
1668                 user.tech_args.silence_threshold = user.u_profile.silence_threshold;
1669         }
1670
1671         /* Set a talker indicate call back if talking detection is requested */
1672         if (ast_test_flag(&user.u_profile, USER_OPT_TALKER_DETECT)) {
1673                 if (ast_bridge_talk_detector_hook(&user.features, conf_handle_talker_cb,
1674                         &user, NULL, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
1675                         pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1676                         res = -1;
1677                         goto confbridge_cleanup;
1678                 }
1679         }
1680
1681         /* If the caller should be joined already muted, set the flag before we join. */
1682         if (ast_test_flag(&user.u_profile, USER_OPT_STARTMUTED)) {
1683                 /* Set user level mute request. */
1684                 user.muted = 1;
1685         }
1686
1687         /* Look for a conference bridge matching the provided name */
1688         if (!(conference = join_conference_bridge(args.conf_name, &user))) {
1689                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "FAILED");
1690                 res = -1;
1691                 goto confbridge_cleanup;
1692         }
1693
1694         /* Keep a copy of volume adjustments so we can restore them later if need be */
1695         volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
1696         volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
1697
1698         if (ast_test_flag(&user.u_profile, USER_OPT_DROP_SILENCE)) {
1699                 user.tech_args.drop_silence = 1;
1700         }
1701
1702         if (ast_test_flag(&user.u_profile, USER_OPT_JITTERBUFFER)) {
1703                 char *func_jb;
1704                 if ((func_jb = ast_module_helper("", "func_jitterbuffer", 0, 0, 0, 0))) {
1705                         ast_free(func_jb);
1706                         ast_func_write(chan, "JITTERBUFFER(adaptive)", "default");
1707                 }
1708         }
1709
1710         if (ast_test_flag(&user.u_profile, USER_OPT_DENOISE)) {
1711                 char *mod_speex;
1712                 /* Reduce background noise from each participant */
1713                 if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
1714                         ast_free(mod_speex);
1715                         ast_func_write(chan, "DENOISE(rx)", "on");
1716                 }
1717         }
1718
1719         /* if this user has a intro, play it before entering */
1720         if (!ast_strlen_zero(user.name_rec_location)) {
1721                 ast_autoservice_start(chan);
1722                 play_sound_file(conference, user.name_rec_location);
1723                 play_sound_file(conference,
1724                         conf_get_sound(CONF_SOUND_HAS_JOINED, user.b_profile.sounds));
1725                 ast_autoservice_stop(chan);
1726         }
1727
1728         /* Play the Join sound to both the conference and the user entering. */
1729         if (!quiet) {
1730                 const char *join_sound = conf_get_sound(CONF_SOUND_JOIN, user.b_profile.sounds);
1731
1732                 ast_stream_and_wait(chan, join_sound, "");
1733                 ast_autoservice_start(chan);
1734                 play_sound_file(conference, join_sound);
1735                 ast_autoservice_stop(chan);
1736         }
1737
1738         /* See if we need to automatically set this user as a video source or not */
1739         handle_video_on_join(conference, user.chan, ast_test_flag(&user.u_profile, USER_OPT_MARKEDUSER));
1740
1741         conf_moh_unsuspend(&user);
1742
1743         /* Join our conference bridge for real */
1744         send_join_event(&user, conference);
1745         ast_bridge_join(conference->bridge,
1746                 chan,
1747                 NULL,
1748                 &user.features,
1749                 &user.tech_args,
1750                 0);
1751
1752         if (!user.kicked && ast_check_hangup(chan)) {
1753                 pbx_builtin_setvar_helper(chan, "CONFBRIDGE_RESULT", "HANGUP");
1754         }
1755
1756         send_leave_event(&user, conference);
1757
1758         /* if we're shutting down, don't attempt to do further processing */
1759         if (ast_shutting_down()) {
1760                 leave_conference(&user);
1761                 conference = NULL;
1762                 goto confbridge_cleanup;
1763         }
1764
1765         /* If this user was a video source, we need to clean up and possibly pick a new source. */
1766         handle_video_on_exit(conference, user.chan);
1767
1768         /* if this user has a intro, play it when leaving */
1769         if (!quiet && !ast_strlen_zero(user.name_rec_location)) {
1770                 ast_autoservice_start(chan);
1771                 play_sound_file(conference, user.name_rec_location);
1772                 play_sound_file(conference,
1773                         conf_get_sound(CONF_SOUND_HAS_LEFT, user.b_profile.sounds));
1774                 ast_autoservice_stop(chan);
1775         }
1776
1777         /* play the leave sound */
1778         if (!quiet) {
1779                 const char *leave_sound = conf_get_sound(CONF_SOUND_LEAVE, user.b_profile.sounds);
1780                 ast_autoservice_start(chan);
1781                 play_sound_file(conference, leave_sound);
1782                 ast_autoservice_stop(chan);
1783         }
1784
1785         /* Easy as pie, depart this channel from the conference bridge */
1786         leave_conference(&user);
1787         conference = NULL;
1788
1789         /* If the user was kicked from the conference play back the audio prompt for it */
1790         if (!quiet && user.kicked) {
1791                 res = ast_stream_and_wait(chan,
1792                         conf_get_sound(CONF_SOUND_KICKED, user.b_profile.sounds),
1793                         "");
1794         }
1795
1796         /* Restore volume adjustments to previous values in case they were changed */
1797         if (volume_adjustments[0]) {
1798                 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
1799         }
1800         if (volume_adjustments[1]) {
1801                 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
1802         }
1803
1804         if (!ast_strlen_zero(user.name_rec_location)) {
1805                 ast_filedelete(user.name_rec_location, NULL);
1806         }
1807
1808 confbridge_cleanup:
1809         ast_bridge_features_cleanup(&user.features);
1810         conf_bridge_profile_destroy(&user.b_profile);
1811         return res;
1812 }
1813
1814 static int action_toggle_mute(struct confbridge_conference *conference,
1815         struct confbridge_user *user)
1816 {
1817         int mute;
1818
1819         /* Toggle user level mute request. */
1820         mute = !user->muted;
1821         user->muted = mute;
1822
1823         conf_update_user_mute(user);
1824         ast_test_suite_event_notify("CONF_MUTE",
1825                 "Message: participant %s %s\r\n"
1826                 "Conference: %s\r\n"
1827                 "Channel: %s",
1828                 ast_channel_name(user->chan),
1829                 mute ? "muted" : "unmuted",
1830                 user->b_profile.name,
1831                 ast_channel_name(user->chan));
1832         if (mute) {
1833                 send_mute_event(user, conference);
1834         } else {
1835                 send_unmute_event(user, conference);
1836         }
1837
1838         return ast_stream_and_wait(user->chan, (mute ?
1839                 conf_get_sound(CONF_SOUND_MUTED, user->b_profile.sounds) :
1840                 conf_get_sound(CONF_SOUND_UNMUTED, user->b_profile.sounds)),
1841                 "");
1842 }
1843
1844 static int action_toggle_mute_participants(struct confbridge_conference *conference, struct confbridge_user *user)
1845 {
1846         struct confbridge_user *cur_user = NULL;
1847         const char *sound_to_play;
1848         int mute;
1849
1850         ao2_lock(conference);
1851
1852         /* Toggle bridge level mute request. */
1853         mute = !conference->muted;
1854         conference->muted = mute;
1855
1856         AST_LIST_TRAVERSE(&conference->active_list, cur_user, list) {
1857                 if (!ast_test_flag(&cur_user->u_profile, USER_OPT_ADMIN)) {
1858                         /* Set user level to bridge level mute request. */
1859                         cur_user->muted = mute;
1860                         conf_update_user_mute(cur_user);
1861                 }
1862         }
1863
1864         ao2_unlock(conference);
1865
1866         sound_to_play = conf_get_sound((mute ? CONF_SOUND_PARTICIPANTS_MUTED : CONF_SOUND_PARTICIPANTS_UNMUTED),
1867                 user->b_profile.sounds);
1868
1869         /* The host needs to hear it seperately, as they don't get the audio from play_sound_helper */
1870         ast_stream_and_wait(user->chan, sound_to_play, "");
1871
1872         /* Announce to the group that all participants are muted */
1873         ast_autoservice_start(user->chan);
1874         play_sound_helper(conference, sound_to_play, 0);
1875         ast_autoservice_stop(user->chan);
1876
1877         return 0;
1878 }
1879
1880 static int action_playback(struct ast_bridge_channel *bridge_channel, const char *playback_file)
1881 {
1882         char *file_copy = ast_strdupa(playback_file);
1883         char *file = NULL;
1884
1885         while ((file = strsep(&file_copy, "&"))) {
1886                 if (ast_stream_and_wait(bridge_channel->chan, file, "")) {
1887                         ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1888                         return -1;
1889                 }
1890         }
1891         return 0;
1892 }
1893
1894 static int action_playback_and_continue(struct confbridge_conference *conference,
1895         struct confbridge_user *user,
1896         struct ast_bridge_channel *bridge_channel,
1897         struct conf_menu *menu,
1898         const char *playback_file,
1899         const char *cur_dtmf,
1900         int *stop_prompts)
1901 {
1902         int i;
1903         int digit = 0;
1904         char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
1905         struct conf_menu_entry new_menu_entry = { { 0, }, };
1906         char *file_copy = ast_strdupa(playback_file);
1907         char *file = NULL;
1908
1909         while ((file = strsep(&file_copy, "&"))) {
1910                 if (ast_streamfile(bridge_channel->chan, file, ast_channel_language(bridge_channel->chan))) {
1911                         ast_log(LOG_WARNING, "Failed to playback file %s to channel\n", file);
1912                         return -1;
1913                 }
1914
1915                 /* now wait for more digits. */
1916                 if (!(digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY))) {
1917                         /* streaming finished and no DTMF was entered */
1918                         continue;
1919                 } else if (digit == -1) {
1920                         /* error */
1921                         return -1;
1922                 } else {
1923                         break; /* dtmf was entered */
1924                 }
1925         }
1926         if (!digit) {
1927                 /* streaming finished on all files and no DTMF was entered */
1928                 return -1;
1929         }
1930         ast_stopstream(bridge_channel->chan);
1931
1932         /* If we get here, then DTMF has been entered, This means no
1933          * additional prompts should be played for this menu entry */
1934         *stop_prompts = 1;
1935
1936         /* If a digit was pressed during the payback, update
1937          * the dtmf string and look for a new menu entry in the
1938          * menu structure */
1939         ast_copy_string(dtmf, cur_dtmf, sizeof(dtmf));
1940         for (i = 0; i < (MAXIMUM_DTMF_FEATURE_STRING - 1); i++) {
1941                 dtmf[i] = cur_dtmf[i];
1942                 if (!dtmf[i]) {
1943                         dtmf[i] = (char) digit;
1944                         dtmf[i + 1] = '\0';
1945                         i = -1;
1946                         break;
1947                 }
1948         }
1949         /* If i is not -1 then the new dtmf digit was _NOT_ added to the string.
1950          * If this is the case, no new DTMF sequence should be looked for. */
1951         if (i != -1) {
1952                 return 0;
1953         }
1954
1955         if (conf_find_menu_entry_by_sequence(dtmf, menu, &new_menu_entry)) {
1956                 execute_menu_entry(conference,
1957                         user,
1958                         bridge_channel,
1959                         &new_menu_entry, menu);
1960                 conf_menu_entry_destroy(&new_menu_entry);
1961         }
1962         return 0;
1963 }
1964
1965 static int action_kick_last(struct confbridge_conference *conference,
1966         struct ast_bridge_channel *bridge_channel,
1967         struct confbridge_user *user)
1968 {
1969         struct confbridge_user *last_user = NULL;
1970         int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
1971
1972         if (!isadmin) {
1973                 ast_stream_and_wait(bridge_channel->chan,
1974                         conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds),
1975                         "");
1976                 ast_log(LOG_WARNING, "Only admin users can use the kick_last menu action. Channel %s of conf %s is not an admin.\n",
1977                         ast_channel_name(bridge_channel->chan),
1978                         conference->name);
1979                 return -1;
1980         }
1981
1982         ao2_lock(conference);
1983         if (((last_user = AST_LIST_LAST(&conference->active_list)) == user)
1984                 || (ast_test_flag(&last_user->u_profile, USER_OPT_ADMIN))) {
1985                 ao2_unlock(conference);
1986                 ast_stream_and_wait(bridge_channel->chan,
1987                         conf_get_sound(CONF_SOUND_ERROR_MENU, user->b_profile.sounds),
1988                         "");
1989         } else if (last_user && !last_user->kicked) {
1990                 last_user->kicked = 1;
1991                 pbx_builtin_setvar_helper(last_user->chan, "CONFBRIDGE_RESULT", "KICKED");
1992                 ast_bridge_remove(conference->bridge, last_user->chan);
1993                 ao2_unlock(conference);
1994         }
1995         return 0;
1996 }
1997
1998 static int action_dialplan_exec(struct ast_bridge_channel *bridge_channel, struct conf_menu_action *menu_action)
1999 {
2000         struct ast_pbx_args args;
2001         struct ast_pbx *pbx;
2002         char *exten;
2003         char *context;
2004         int priority;
2005         int res;
2006
2007         memset(&args, 0, sizeof(args));
2008         args.no_hangup_chan = 1;
2009
2010         ast_channel_lock(bridge_channel->chan);
2011
2012         /*save off*/
2013         exten = ast_strdupa(ast_channel_exten(bridge_channel->chan));
2014         context = ast_strdupa(ast_channel_context(bridge_channel->chan));
2015         priority = ast_channel_priority(bridge_channel->chan);
2016         pbx = ast_channel_pbx(bridge_channel->chan);
2017         ast_channel_pbx_set(bridge_channel->chan, NULL);
2018
2019         /*set new*/
2020         ast_channel_exten_set(bridge_channel->chan, menu_action->data.dialplan_args.exten);
2021         ast_channel_context_set(bridge_channel->chan, menu_action->data.dialplan_args.context);
2022         ast_channel_priority_set(bridge_channel->chan, menu_action->data.dialplan_args.priority);
2023
2024         ast_channel_unlock(bridge_channel->chan);
2025
2026         /*execute*/
2027         res = ast_pbx_run_args(bridge_channel->chan, &args);
2028
2029         /*restore*/
2030         ast_channel_lock(bridge_channel->chan);
2031
2032         ast_channel_exten_set(bridge_channel->chan, exten);
2033         ast_channel_context_set(bridge_channel->chan, context);
2034         ast_channel_priority_set(bridge_channel->chan, priority);
2035         ast_channel_pbx_set(bridge_channel->chan, pbx);
2036
2037         ast_channel_unlock(bridge_channel->chan);
2038
2039         return res;
2040 }
2041
2042 static int execute_menu_entry(struct confbridge_conference *conference,
2043         struct confbridge_user *user,
2044         struct ast_bridge_channel *bridge_channel,
2045         struct conf_menu_entry *menu_entry,
2046         struct conf_menu *menu)
2047 {
2048         struct conf_menu_action *menu_action;
2049         int isadmin = ast_test_flag(&user->u_profile, USER_OPT_ADMIN);
2050         int stop_prompts = 0;
2051         int res = 0;
2052
2053         AST_LIST_TRAVERSE(&menu_entry->actions, menu_action, action) {
2054                 switch (menu_action->id) {
2055                 case MENU_ACTION_TOGGLE_MUTE:
2056                         res |= action_toggle_mute(conference, user);
2057                         break;
2058                 case MENU_ACTION_ADMIN_TOGGLE_MUTE_PARTICIPANTS:
2059                         if (!isadmin) {
2060                                 break;
2061                         }
2062                         action_toggle_mute_participants(conference, user);
2063                         break;
2064                 case MENU_ACTION_PARTICIPANT_COUNT:
2065                         announce_user_count(conference, user);
2066                         break;
2067                 case MENU_ACTION_PLAYBACK:
2068                         if (!stop_prompts) {
2069                                 res |= action_playback(bridge_channel, menu_action->data.playback_file);
2070                                 ast_test_suite_event_notify("CONF_MENU_PLAYBACK",
2071                                         "Message: %s\r\nChannel: %s",
2072                                         menu_action->data.playback_file, ast_channel_name(bridge_channel->chan));
2073                         }
2074                         break;
2075                 case MENU_ACTION_RESET_LISTENING:
2076                         ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 0);
2077                         break;
2078                 case MENU_ACTION_RESET_TALKING:
2079                         ast_audiohook_volume_set(user->chan, AST_AUDIOHOOK_DIRECTION_READ, 0);
2080                         break;
2081                 case MENU_ACTION_INCREASE_LISTENING:
2082                         ast_audiohook_volume_adjust(user->chan,
2083                                 AST_AUDIOHOOK_DIRECTION_WRITE, 1);
2084                         break;
2085                 case MENU_ACTION_DECREASE_LISTENING:
2086                         ast_audiohook_volume_adjust(user->chan,
2087                                 AST_AUDIOHOOK_DIRECTION_WRITE, -1);
2088                         break;
2089                 case MENU_ACTION_INCREASE_TALKING:
2090                         ast_audiohook_volume_adjust(user->chan,
2091                                 AST_AUDIOHOOK_DIRECTION_READ, 1);
2092                         break;
2093                 case MENU_ACTION_DECREASE_TALKING:
2094                         ast_audiohook_volume_adjust(user->chan,
2095                                 AST_AUDIOHOOK_DIRECTION_READ, -1);
2096                         break;
2097                 case MENU_ACTION_PLAYBACK_AND_CONTINUE:
2098                         if (!(stop_prompts)) {
2099                                 res |= action_playback_and_continue(conference,
2100                                         user,
2101                                         bridge_channel,
2102                                         menu,
2103                                         menu_action->data.playback_file,
2104                                         menu_entry->dtmf,
2105                                         &stop_prompts);
2106                         }
2107                         break;
2108                 case MENU_ACTION_DIALPLAN_EXEC:
2109                         res |= action_dialplan_exec(bridge_channel, menu_action);
2110                         break;
2111                 case MENU_ACTION_ADMIN_TOGGLE_LOCK:
2112                         if (!isadmin) {
2113                                 break;
2114                         }
2115                         conference->locked = (!conference->locked ? 1 : 0);
2116                         res |= ast_stream_and_wait(bridge_channel->chan,
2117                                 (conference->locked ?
2118                                 conf_get_sound(CONF_SOUND_LOCKED_NOW, user->b_profile.sounds) :
2119                                 conf_get_sound(CONF_SOUND_UNLOCKED_NOW, user->b_profile.sounds)),
2120                                 "");
2121
2122                         break;
2123                 case MENU_ACTION_ADMIN_KICK_LAST:
2124                         res |= action_kick_last(conference, bridge_channel, user);
2125                         break;
2126                 case MENU_ACTION_LEAVE:
2127                         pbx_builtin_setvar_helper(bridge_channel->chan, "CONFBRIDGE_RESULT", "DTMF");
2128                         ao2_lock(conference);
2129                         ast_bridge_remove(conference->bridge, bridge_channel->chan);
2130                         ast_test_suite_event_notify("CONF_MENU_LEAVE",
2131                                 "Channel: %s",
2132                                 ast_channel_name(bridge_channel->chan));
2133                         ao2_unlock(conference);
2134                         break;
2135                 case MENU_ACTION_NOOP:
2136                         break;
2137                 case MENU_ACTION_SET_SINGLE_VIDEO_SRC:
2138                         ao2_lock(conference);
2139                         ast_bridge_set_single_src_video_mode(conference->bridge, bridge_channel->chan);
2140                         ao2_unlock(conference);
2141                         break;
2142                 case MENU_ACTION_RELEASE_SINGLE_VIDEO_SRC:
2143                         handle_video_on_exit(conference, bridge_channel->chan);
2144                         break;
2145                 }
2146         }
2147         return res;
2148 }
2149
2150 int conf_handle_dtmf(struct ast_bridge_channel *bridge_channel,
2151         struct confbridge_user *user,
2152         struct conf_menu_entry *menu_entry,
2153         struct conf_menu *menu)
2154 {
2155         /* See if music on hold is playing */
2156         conf_moh_suspend(user);
2157
2158         /* execute the list of actions associated with this menu entry */
2159         execute_menu_entry(user->conference, user, bridge_channel, menu_entry, menu);
2160
2161         /* See if music on hold needs to be started back up again */
2162         conf_moh_unsuspend(user);
2163
2164         return 0;
2165 }
2166
2167 static int kick_conference_participant(struct confbridge_conference *conference,
2168         const char *channel)
2169 {
2170         int res = -1;
2171         int match;
2172         struct confbridge_user *user = NULL;
2173         int all = !strcasecmp("all", channel);
2174         int participants = !strcasecmp("participants", channel);
2175
2176         SCOPED_AO2LOCK(bridge_lock, conference);
2177
2178         AST_LIST_TRAVERSE(&conference->active_list, user, list) {
2179                 if (user->kicked) {
2180                         continue;
2181                 }
2182                 match = !strcasecmp(channel, ast_channel_name(user->chan));
2183                 if (match || all
2184                                 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
2185                         user->kicked = 1;
2186                         pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
2187                         ast_bridge_remove(conference->bridge, user->chan);
2188                         res = 0;
2189                         if (match) {
2190                                 return res;
2191                         }
2192                 }
2193         }
2194         AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
2195                 if (user->kicked) {
2196                         continue;
2197                 }
2198                 match = !strcasecmp(channel, ast_channel_name(user->chan));
2199                 if (match || all
2200                                 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
2201                         user->kicked = 1;
2202                         pbx_builtin_setvar_helper(user->chan, "CONFBRIDGE_RESULT", "KICKED");
2203                         ast_bridge_remove(conference->bridge, user->chan);
2204                         res = 0;
2205                         if (match) {
2206                                 return res;
2207                         }
2208                 }
2209         }
2210
2211         return res;
2212 }
2213
2214 static char *complete_confbridge_name(const char *line, const char *word, int pos, int state)
2215 {
2216         int which = 0;
2217         struct confbridge_conference *conference;
2218         char *res = NULL;
2219         int wordlen = strlen(word);
2220         struct ao2_iterator iter;
2221
2222         iter = ao2_iterator_init(conference_bridges, 0);
2223         while ((conference = ao2_iterator_next(&iter))) {
2224                 if (!strncasecmp(conference->name, word, wordlen) && ++which > state) {
2225                         res = ast_strdup(conference->name);
2226                         ao2_ref(conference, -1);
2227                         break;
2228                 }
2229                 ao2_ref(conference, -1);
2230         }
2231         ao2_iterator_destroy(&iter);
2232
2233         return res;
2234 }
2235
2236 static char *complete_confbridge_participant(const char *conference_name, const char *line, const char *word, int pos, int state)
2237 {
2238         int which = 0;
2239         RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
2240         struct confbridge_user *user;
2241         char *res = NULL;
2242         int wordlen = strlen(word);
2243
2244         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
2245         if (!conference) {
2246                 return NULL;
2247         }
2248
2249         if (!strncasecmp("all", word, wordlen) && ++which > state) {
2250                 return ast_strdup("all");
2251         }
2252
2253         if (!strncasecmp("participants", word, wordlen) && ++which > state) {
2254                 return ast_strdup("participants");
2255         }
2256
2257         {
2258                 SCOPED_AO2LOCK(bridge_lock, conference);
2259                 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
2260                         if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
2261                                 res = ast_strdup(ast_channel_name(user->chan));
2262                                 return res;
2263                         }
2264                 }
2265                 AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
2266                         if (!strncasecmp(ast_channel_name(user->chan), word, wordlen) && ++which > state) {
2267                                 res = ast_strdup(ast_channel_name(user->chan));
2268                                 return res;
2269                         }
2270                 }
2271         }
2272
2273         return NULL;
2274 }
2275
2276 static char *handle_cli_confbridge_kick(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2277 {
2278         struct confbridge_conference *conference;
2279         int not_found;
2280
2281         switch (cmd) {
2282         case CLI_INIT:
2283                 e->command = "confbridge kick";
2284                 e->usage =
2285                         "Usage: confbridge kick <conference> <channel>\n"
2286                         "       Kicks a channel out of the conference bridge.\n"
2287                         "             (all to kick everyone, participants to kick non-admins).\n";
2288                 return NULL;
2289         case CLI_GENERATE:
2290                 if (a->pos == 2) {
2291                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2292                 }
2293                 if (a->pos == 3) {
2294                         return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
2295                 }
2296                 return NULL;
2297         }
2298
2299         if (a->argc != 4) {
2300                 return CLI_SHOWUSAGE;
2301         }
2302
2303         conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
2304         if (!conference) {
2305                 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2306                 return CLI_SUCCESS;
2307         }
2308         not_found = kick_conference_participant(conference, a->argv[3]);
2309         ao2_ref(conference, -1);
2310         if (not_found) {
2311                 if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
2312                         ast_cli(a->fd, "No participants found!\n");
2313                 } else {
2314                         ast_cli(a->fd, "No participant named '%s' found!\n", a->argv[3]);
2315                 }
2316                 return CLI_SUCCESS;
2317         }
2318         ast_cli(a->fd, "Kicked '%s' out of conference '%s'\n", a->argv[3], a->argv[2]);
2319         return CLI_SUCCESS;
2320 }
2321
2322 static void handle_cli_confbridge_list_item(struct ast_cli_args *a, struct confbridge_user *user, int waiting)
2323 {
2324         char flag_str[6 + 1];/* Max flags + terminator */
2325         int pos = 0;
2326
2327         /* Build flags column string. */
2328         if (ast_test_flag(&user->u_profile, USER_OPT_ADMIN)) {
2329                 flag_str[pos++] = 'A';
2330         }
2331         if (ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER)) {
2332                 flag_str[pos++] = 'M';
2333         }
2334         if (ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED)) {
2335                 flag_str[pos++] = 'W';
2336         }
2337         if (ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED)) {
2338                 flag_str[pos++] = 'E';
2339         }
2340         if (user->muted) {
2341                 flag_str[pos++] = 'm';
2342         }
2343         if (waiting) {
2344                 flag_str[pos++] = 'w';
2345         }
2346         flag_str[pos] = '\0';
2347
2348         ast_cli(a->fd, "%-30s %-6s %-16s %-16s %-16s %s\n",
2349                 ast_channel_name(user->chan),
2350                 flag_str,
2351                 user->u_profile.name,
2352                 user->b_profile.name,
2353                 user->menu_name,
2354                 S_COR(ast_channel_caller(user->chan)->id.number.valid,
2355                         ast_channel_caller(user->chan)->id.number.str, "<unknown>"));
2356 }
2357
2358 static char *handle_cli_confbridge_list(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2359 {
2360         struct confbridge_conference *conference;
2361
2362         switch (cmd) {
2363         case CLI_INIT:
2364                 e->command = "confbridge list";
2365                 e->usage =
2366                         "Usage: confbridge list [<name>]\n"
2367                         "       Lists all currently active conference bridges or a specific conference bridge.\n"
2368                         "\n"
2369                         "       When a conference bridge name is provided, flags may be shown for users. Below\n"
2370                         "       are the flags and what they represent.\n"
2371                         "\n"
2372                         "       Flags:\n"
2373                         "         A - The user is an admin\n"
2374                         "         M - The user is a marked user\n"
2375                         "         W - The user must wait for a marked user to join\n"
2376                         "         E - The user will be kicked after the last marked user leaves the conference\n"
2377                         "         m - The user is muted\n"
2378                         "         w - The user is waiting for a marked user to join\n";
2379                 return NULL;
2380         case CLI_GENERATE:
2381                 if (a->pos == 2) {
2382                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2383                 }
2384                 return NULL;
2385         }
2386
2387         if (a->argc == 2) {
2388                 struct ao2_iterator iter;
2389
2390                 ast_cli(a->fd, "Conference Bridge Name           Users  Marked Locked?\n");
2391                 ast_cli(a->fd, "================================ ====== ====== ========\n");
2392                 iter = ao2_iterator_init(conference_bridges, 0);
2393                 while ((conference = ao2_iterator_next(&iter))) {
2394                         ast_cli(a->fd, "%-32s %6u %6u %s\n", conference->name, conference->activeusers + conference->waitingusers, conference->markedusers, (conference->locked ? "locked" : "unlocked"));
2395                         ao2_ref(conference, -1);
2396                 }
2397                 ao2_iterator_destroy(&iter);
2398                 return CLI_SUCCESS;
2399         }
2400
2401         if (a->argc == 3) {
2402                 struct confbridge_user *user;
2403
2404                 conference = ao2_find(conference_bridges, a->argv[2], OBJ_KEY);
2405                 if (!conference) {
2406                         ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2407                         return CLI_SUCCESS;
2408                 }
2409                 ast_cli(a->fd, "Channel                        Flags  User Profile     Bridge Profile   Menu             CallerID\n");
2410                 ast_cli(a->fd, "============================== ====== ================ ================ ================ ================\n");
2411                 ao2_lock(conference);
2412                 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
2413                         handle_cli_confbridge_list_item(a, user, 0);
2414                 }
2415                 AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
2416                         handle_cli_confbridge_list_item(a, user, 1);
2417                 }
2418                 ao2_unlock(conference);
2419                 ao2_ref(conference, -1);
2420                 return CLI_SUCCESS;
2421         }
2422
2423         return CLI_SHOWUSAGE;
2424 }
2425
2426 /* \internal
2427  * \brief finds a conference by name and locks/unlocks.
2428  *
2429  * \retval 0 success
2430  * \retval -1 conference not found
2431  */
2432 static int generic_lock_unlock_helper(int lock, const char *conference_name)
2433 {
2434         struct confbridge_conference *conference;
2435         int res = 0;
2436
2437         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
2438         if (!conference) {
2439                 return -1;
2440         }
2441         ao2_lock(conference);
2442         conference->locked = lock;
2443         ast_test_suite_event_notify("CONF_LOCK", "Message: conference %s\r\nConference: %s", conference->locked ? "locked" : "unlocked", conference->b_profile.name);
2444         ao2_unlock(conference);
2445         ao2_ref(conference, -1);
2446
2447         return res;
2448 }
2449
2450 /* \internal
2451  * \brief Mute/unmute a single user.
2452  */
2453 static void generic_mute_unmute_user(struct confbridge_conference *conference, struct confbridge_user *user, int mute)
2454 {
2455         /* Set user level mute request. */
2456         user->muted = mute ? 1 : 0;
2457
2458         conf_update_user_mute(user);
2459         ast_test_suite_event_notify("CONF_MUTE",
2460                 "Message: participant %s %s\r\n"
2461                 "Conference: %s\r\n"
2462                 "Channel: %s",
2463                 ast_channel_name(user->chan),
2464                 mute ? "muted" : "unmuted",
2465                 conference->b_profile.name,
2466                 ast_channel_name(user->chan));
2467         if (mute) {
2468                 send_mute_event(user, conference);
2469         } else {
2470                 send_unmute_event(user, conference);
2471         }
2472 }
2473
2474 /* \internal
2475  * \brief finds a conference user by channel name and mutes/unmutes them.
2476  *
2477  * \retval 0 success
2478  * \retval -1 conference not found
2479  * \retval -2 user not found
2480  */
2481 static int generic_mute_unmute_helper(int mute, const char *conference_name,
2482         const char *chan_name)
2483 {
2484         RAII_VAR(struct confbridge_conference *, conference, NULL, ao2_cleanup);
2485         struct confbridge_user *user;
2486         int all = !strcasecmp("all", chan_name);
2487         int participants = !strcasecmp("participants", chan_name);
2488         int res = -2;
2489
2490         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
2491         if (!conference) {
2492                 return -1;
2493         }
2494
2495         {
2496                 SCOPED_AO2LOCK(bridge_lock, conference);
2497                 AST_LIST_TRAVERSE(&conference->active_list, user, list) {
2498                         int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
2499                                 strlen(chan_name));
2500                         if (match || all
2501                                 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
2502                                 generic_mute_unmute_user(conference, user, mute);
2503                                 res = 0;
2504                                 if (match) {
2505                                         return res;
2506                                 }
2507                         }
2508                 }
2509
2510                 AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
2511                         int match = !strncasecmp(chan_name, ast_channel_name(user->chan),
2512                                 strlen(chan_name));
2513                         if (match || all
2514                                 || (participants && !ast_test_flag(&user->u_profile, USER_OPT_ADMIN))) {
2515                                 generic_mute_unmute_user(conference, user, mute);
2516                                 res = 0;
2517                                 if (match) {
2518                                         return res;
2519                                 }
2520                         }
2521                 }
2522         }
2523
2524         return res;
2525 }
2526
2527 static int cli_mute_unmute_helper(int mute, struct ast_cli_args *a)
2528 {
2529         int res = generic_mute_unmute_helper(mute, a->argv[2], a->argv[3]);
2530
2531         if (res == -1) {
2532                 ast_cli(a->fd, "No conference bridge named '%s' found!\n", a->argv[2]);
2533                 return -1;
2534         } else if (res == -2) {
2535                 if (!strcasecmp("all", a->argv[3]) || !strcasecmp("participants", a->argv[3])) {
2536                         ast_cli(a->fd, "No participants found in conference %s\n", a->argv[2]);
2537                 } else {
2538                         ast_cli(a->fd, "No channel named '%s' found in conference %s\n", a->argv[3], a->argv[2]);
2539                 }
2540                 return -1;
2541         }
2542         ast_cli(a->fd, "%s %s from confbridge %s\n", mute ? "Muting" : "Unmuting", a->argv[3], a->argv[2]);
2543         return 0;
2544 }
2545
2546 static char *handle_cli_confbridge_mute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2547 {
2548         switch (cmd) {
2549         case CLI_INIT:
2550                 e->command = "confbridge mute";
2551                 e->usage =
2552                         "Usage: confbridge mute <conference> <channel>\n"
2553                         "       Mute a channel in a conference.\n"
2554                         "              (all to mute everyone, participants to mute non-admins)\n"
2555                         "       If the specified channel is a prefix,\n"
2556                         "       the action will be taken on the first\n"
2557                         "       matching channel.\n";
2558                 return NULL;
2559         case CLI_GENERATE:
2560                 if (a->pos == 2) {
2561                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2562                 }
2563                 if (a->pos == 3) {
2564                         return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
2565                 }
2566                 return NULL;
2567         }
2568         if (a->argc != 4) {
2569                 return CLI_SHOWUSAGE;
2570         }
2571
2572         cli_mute_unmute_helper(1, a);
2573
2574         return CLI_SUCCESS;
2575 }
2576
2577 static char *handle_cli_confbridge_unmute(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2578 {
2579         switch (cmd) {
2580         case CLI_INIT:
2581                 e->command = "confbridge unmute";
2582                 e->usage =
2583                         "Usage: confbridge unmute <conference> <channel>\n"
2584                         "       Unmute a channel in a conference.\n"
2585                         "              (all to unmute everyone, participants to unmute non-admins)\n"
2586                         "       If the specified channel is a prefix,\n"
2587                         "       the action will be taken on the first\n"
2588                         "       matching channel.\n";
2589                 return NULL;
2590         case CLI_GENERATE:
2591                 if (a->pos == 2) {
2592                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2593                 }
2594                 if (a->pos == 3) {
2595                         return complete_confbridge_participant(a->argv[2], a->line, a->word, a->pos, a->n);
2596                 }
2597                 return NULL;
2598         }
2599         if (a->argc != 4) {
2600                 return CLI_SHOWUSAGE;
2601         }
2602
2603         cli_mute_unmute_helper(0, a);
2604
2605         return CLI_SUCCESS;
2606 }
2607
2608 static char *handle_cli_confbridge_lock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2609 {
2610         switch (cmd) {
2611         case CLI_INIT:
2612                 e->command = "confbridge lock";
2613                 e->usage =
2614                         "Usage: confbridge lock <conference>\n"
2615                         "       Lock a conference. While locked, no new non-admins\n"
2616                         "       may join the conference.\n";
2617                 return NULL;
2618         case CLI_GENERATE:
2619                 if (a->pos == 2) {
2620                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2621                 }
2622                 return NULL;
2623         }
2624         if (a->argc != 3) {
2625                 return CLI_SHOWUSAGE;
2626         }
2627         if (generic_lock_unlock_helper(1, a->argv[2])) {
2628                 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2629         } else {
2630                 ast_cli(a->fd, "Conference %s is locked.\n", a->argv[2]);
2631         }
2632         return CLI_SUCCESS;
2633 }
2634
2635 static char *handle_cli_confbridge_unlock(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2636 {
2637         switch (cmd) {
2638         case CLI_INIT:
2639                 e->command = "confbridge unlock";
2640                 e->usage =
2641                         "Usage: confbridge unlock <conference>\n"
2642                         "       Unlock a previously locked conference.\n";
2643                 return NULL;
2644         case CLI_GENERATE:
2645                 if (a->pos == 2) {
2646                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2647                 }
2648                 return NULL;
2649         }
2650         if (a->argc != 3) {
2651                 return CLI_SHOWUSAGE;
2652         }
2653         if (generic_lock_unlock_helper(0, a->argv[2])) {
2654                 ast_cli(a->fd, "Conference %s is not found\n", a->argv[2]);
2655         } else {
2656                 ast_cli(a->fd, "Conference %s is unlocked.\n", a->argv[2]);
2657         }
2658         return CLI_SUCCESS;
2659 }
2660
2661 static char *handle_cli_confbridge_start_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2662 {
2663         const char *rec_file = NULL;
2664         struct confbridge_conference *conference;
2665
2666         switch (cmd) {
2667         case CLI_INIT:
2668                 e->command = "confbridge record start";
2669                 e->usage =
2670                         "Usage: confbridge record start <conference> <file>\n"
2671                         "       <file> is optional, Otherwise the bridge profile\n"
2672                         "       record file will be used.  If the bridge profile\n"
2673                         "       has no record file specified, a file will automatically\n"
2674                         "       be generated in the monitor directory\n";
2675                 return NULL;
2676         case CLI_GENERATE:
2677                 if (a->pos == 3) {
2678                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2679                 }
2680                 return NULL;
2681         }
2682         if (a->argc < 4) {
2683                 return CLI_SHOWUSAGE;
2684         }
2685         if (a->argc == 5) {
2686                 rec_file = a->argv[4];
2687         }
2688
2689         conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
2690         if (!conference) {
2691                 ast_cli(a->fd, "Conference not found.\n");
2692                 return CLI_FAILURE;
2693         }
2694         ao2_lock(conference);
2695         if (conf_is_recording(conference)) {
2696                 ast_cli(a->fd, "Conference is already being recorded.\n");
2697                 ao2_unlock(conference);
2698                 ao2_ref(conference, -1);
2699                 return CLI_SUCCESS;
2700         }
2701         if (!ast_strlen_zero(rec_file)) {
2702                 ast_copy_string(conference->b_profile.rec_file, rec_file, sizeof(conference->b_profile.rec_file));
2703         }
2704
2705         if (conf_start_record(conference)) {
2706                 ast_cli(a->fd, "Could not start recording due to internal error.\n");
2707                 ao2_unlock(conference);
2708                 ao2_ref(conference, -1);
2709                 return CLI_FAILURE;
2710         }
2711         ao2_unlock(conference);
2712
2713         ast_cli(a->fd, "Recording started\n");
2714         ao2_ref(conference, -1);
2715         return CLI_SUCCESS;
2716 }
2717
2718 static char *handle_cli_confbridge_stop_record(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
2719 {
2720         struct confbridge_conference *conference;
2721         int ret;
2722
2723         switch (cmd) {
2724         case CLI_INIT:
2725                 e->command = "confbridge record stop";
2726                 e->usage =
2727                         "Usage: confbridge record stop <conference>\n"
2728                         "       Stop a previously started recording.\n";
2729                 return NULL;
2730         case CLI_GENERATE:
2731                 if (a->pos == 3) {
2732                         return complete_confbridge_name(a->line, a->word, a->pos, a->n);
2733                 }
2734                 return NULL;
2735         }
2736         if (a->argc != 4) {
2737                 return CLI_SHOWUSAGE;
2738         }
2739
2740         conference = ao2_find(conference_bridges, a->argv[3], OBJ_KEY);
2741         if (!conference) {
2742                 ast_cli(a->fd, "Conference not found.\n");
2743                 return CLI_SUCCESS;
2744         }
2745         ao2_lock(conference);
2746         ret = conf_stop_record(conference);
2747         ao2_unlock(conference);
2748         ast_cli(a->fd, "Recording %sstopped.\n", ret ? "could not be " : "");
2749         ao2_ref(conference, -1);
2750         return CLI_SUCCESS;
2751 }
2752
2753 static struct ast_cli_entry cli_confbridge[] = {
2754         AST_CLI_DEFINE(handle_cli_confbridge_list, "List conference bridges and participants."),
2755         AST_CLI_DEFINE(handle_cli_confbridge_kick, "Kick participants out of conference bridges."),
2756         AST_CLI_DEFINE(handle_cli_confbridge_mute, "Mute participants."),
2757         AST_CLI_DEFINE(handle_cli_confbridge_unmute, "Unmute participants."),
2758         AST_CLI_DEFINE(handle_cli_confbridge_lock, "Lock a conference."),
2759         AST_CLI_DEFINE(handle_cli_confbridge_unlock, "Unlock a conference."),
2760         AST_CLI_DEFINE(handle_cli_confbridge_start_record, "Start recording a conference"),
2761         AST_CLI_DEFINE(handle_cli_confbridge_stop_record, "Stop recording a conference."),
2762 };
2763 static struct ast_custom_function confbridge_function = {
2764         .name = "CONFBRIDGE",
2765         .write = func_confbridge_helper,
2766 };
2767
2768 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
2769 static struct ast_custom_function confbridge_info_function = {
2770         .name = "CONFBRIDGE_INFO",
2771         .read = func_confbridge_info,
2772 };
2773
2774 static void action_confbridgelist_item(struct mansession *s, const char *id_text, struct confbridge_conference *conference, struct confbridge_user *user, int waiting)
2775 {
2776         astman_append(s,
2777                 "Event: ConfbridgeList\r\n"
2778                 "%s"
2779                 "Conference: %s\r\n"
2780                 "CallerIDNum: %s\r\n"
2781                 "CallerIDName: %s\r\n"
2782                 "Channel: %s\r\n"
2783                 "Admin: %s\r\n"
2784                 "MarkedUser: %s\r\n"
2785                 "WaitMarked: %s\r\n"
2786                 "EndMarked: %s\r\n"
2787                 "Waiting: %s\r\n"
2788                 "Muted: %s\r\n"
2789                 "AnsweredTime: %d\r\n"
2790                 "\r\n",
2791                 id_text,
2792                 conference->name,
2793                 S_COR(ast_channel_caller(user->chan)->id.number.valid, ast_channel_caller(user->chan)->id.number.str, "<unknown>"),
2794                 S_COR(ast_channel_caller(user->chan)->id.name.valid, ast_channel_caller(user->chan)->id.name.str, "<no name>"),
2795                 ast_channel_name(user->chan),
2796                 ast_test_flag(&user->u_profile, USER_OPT_ADMIN) ? "Yes" : "No",
2797                 ast_test_flag(&user->u_profile, USER_OPT_MARKEDUSER) ? "Yes" : "No",
2798                 ast_test_flag(&user->u_profile, USER_OPT_WAITMARKED) ? "Yes" : "No",
2799                 ast_test_flag(&user->u_profile, USER_OPT_ENDMARKED) ? "Yes" : "No",
2800                 waiting ? "Yes" : "No",
2801                 user->muted ? "Yes" : "No",
2802                 ast_channel_get_up_time(user->chan));
2803 }
2804
2805 static int action_confbridgelist(struct mansession *s, const struct message *m)
2806 {
2807         const char *actionid = astman_get_header(m, "ActionID");
2808         const char *conference_name = astman_get_header(m, "Conference");
2809         struct confbridge_user *user;
2810         struct confbridge_conference *conference;
2811         char id_text[80];
2812         int total = 0;
2813
2814         id_text[0] = '\0';
2815         if (!ast_strlen_zero(actionid)) {
2816                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2817         }
2818         if (ast_strlen_zero(conference_name)) {
2819                 astman_send_error(s, m, "No Conference name provided.");
2820                 return 0;
2821         }
2822         if (!ao2_container_count(conference_bridges)) {
2823                 astman_send_error(s, m, "No active conferences.");
2824                 return 0;
2825         }
2826         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
2827         if (!conference) {
2828                 astman_send_error(s, m, "No Conference by that name found.");
2829                 return 0;
2830         }
2831
2832         astman_send_listack(s, m, "Confbridge user list will follow", "start");
2833
2834         ao2_lock(conference);
2835         AST_LIST_TRAVERSE(&conference->active_list, user, list) {
2836                 total++;
2837                 action_confbridgelist_item(s, id_text, conference, user, 0);
2838         }
2839         AST_LIST_TRAVERSE(&conference->waiting_list, user, list) {
2840                 total++;
2841                 action_confbridgelist_item(s, id_text, conference, user, 1);
2842         }
2843         ao2_unlock(conference);
2844         ao2_ref(conference, -1);
2845
2846         astman_send_list_complete_start(s, m, "ConfbridgeListComplete", total);
2847         astman_send_list_complete_end(s);
2848
2849         return 0;
2850 }
2851
2852 static int action_confbridgelistrooms(struct mansession *s, const struct message *m)
2853 {
2854         const char *actionid = astman_get_header(m, "ActionID");
2855         struct confbridge_conference *conference;
2856         struct ao2_iterator iter;
2857         char id_text[512] = "";
2858         int totalitems = 0;
2859
2860         if (!ast_strlen_zero(actionid)) {
2861                 snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", actionid);
2862         }
2863
2864         if (!ao2_container_count(conference_bridges)) {
2865                 astman_send_error(s, m, "No active conferences.");
2866                 return 0;
2867         }
2868
2869         astman_send_listack(s, m, "Confbridge conferences will follow", "start");
2870
2871         /* Traverse the conference list */
2872         iter = ao2_iterator_init(conference_bridges, 0);
2873         while ((conference = ao2_iterator_next(&iter))) {
2874                 totalitems++;
2875
2876                 ao2_lock(conference);
2877                 astman_append(s,
2878                 "Event: ConfbridgeListRooms\r\n"
2879                 "%s"
2880                 "Conference: %s\r\n"
2881                 "Parties: %u\r\n"
2882                 "Marked: %u\r\n"
2883                 "Locked: %s\r\n"
2884                 "\r\n",
2885                 id_text,
2886                 conference->name,
2887                 conference->activeusers + conference->waitingusers,
2888                 conference->markedusers,
2889                 conference->locked ? "Yes" : "No");
2890                 ao2_unlock(conference);
2891
2892                 ao2_ref(conference, -1);
2893         }
2894         ao2_iterator_destroy(&iter);
2895
2896         /* Send final confirmation */
2897         astman_send_list_complete_start(s, m, "ConfbridgeListRoomsComplete", totalitems);
2898         astman_send_list_complete_end(s);
2899         return 0;
2900 }
2901
2902 static int action_mute_unmute_helper(struct mansession *s, const struct message *m, int mute)
2903 {
2904         const char *conference_name = astman_get_header(m, "Conference");
2905         const char *channel_name = astman_get_header(m, "Channel");
2906         int res = 0;
2907
2908         if (ast_strlen_zero(conference_name)) {
2909                 astman_send_error(s, m, "No Conference name provided.");
2910                 return 0;
2911         }
2912         if (ast_strlen_zero(channel_name)) {
2913                 astman_send_error(s, m, "No channel name provided.");
2914                 return 0;
2915         }
2916         if (!ao2_container_count(conference_bridges)) {
2917                 astman_send_error(s, m, "No active conferences.");
2918                 return 0;
2919         }
2920
2921         res = generic_mute_unmute_helper(mute, conference_name, channel_name);
2922
2923         if (res == -1) {
2924                 astman_send_error(s, m, "No Conference by that name found.");
2925                 return 0;
2926         } else if (res == -2) {
2927                 astman_send_error(s, m, "No Channel by that name found in Conference.");
2928                 return 0;
2929         }
2930
2931         astman_send_ack(s, m, mute ? "User muted" : "User unmuted");
2932         return 0;
2933 }
2934
2935 static int action_confbridgeunmute(struct mansession *s, const struct message *m)
2936 {
2937         return action_mute_unmute_helper(s, m, 0);
2938 }
2939 static int action_confbridgemute(struct mansession *s, const struct message *m)
2940 {
2941         return action_mute_unmute_helper(s, m, 1);
2942 }
2943
2944 static int action_lock_unlock_helper(struct mansession *s, const struct message *m, int lock)
2945 {
2946         const char *conference_name = astman_get_header(m, "Conference");
2947         int res = 0;
2948
2949         if (ast_strlen_zero(conference_name)) {
2950                 astman_send_error(s, m, "No Conference name provided.");
2951                 return 0;
2952         }
2953         if (!ao2_container_count(conference_bridges)) {
2954                 astman_send_error(s, m, "No active conferences.");
2955                 return 0;
2956         }
2957         if ((res = generic_lock_unlock_helper(lock, conference_name))) {
2958                 astman_send_error(s, m, "No Conference by that name found.");
2959                 return 0;
2960         }
2961         astman_send_ack(s, m, lock ? "Conference locked" : "Conference unlocked");
2962         return 0;
2963 }
2964 static int action_confbridgeunlock(struct mansession *s, const struct message *m)
2965 {
2966         return action_lock_unlock_helper(s, m, 0);
2967 }
2968 static int action_confbridgelock(struct mansession *s, const struct message *m)
2969 {
2970         return action_lock_unlock_helper(s, m, 1);
2971 }
2972
2973 static int action_confbridgekick(struct mansession *s, const struct message *m)
2974 {
2975         const char *conference_name = astman_get_header(m, "Conference");
2976         const char *channel = astman_get_header(m, "Channel");
2977         struct confbridge_conference *conference;
2978         int found;
2979
2980         if (ast_strlen_zero(conference_name)) {
2981                 astman_send_error(s, m, "No Conference name provided.");
2982                 return 0;
2983         }
2984         if (!ao2_container_count(conference_bridges)) {
2985                 astman_send_error(s, m, "No active conferences.");
2986                 return 0;
2987         }
2988
2989         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
2990         if (!conference) {
2991                 astman_send_error(s, m, "No Conference by that name found.");
2992                 return 0;
2993         }
2994
2995         found = !kick_conference_participant(conference, channel);
2996         ao2_ref(conference, -1);
2997
2998         if (found) {
2999                 astman_send_ack(s, m, !strcmp("all", channel) ? "All participants kicked" : "User kicked");
3000         } else {
3001                 astman_send_error(s, m, "No Channel by that name found in Conference.");
3002         }
3003         return 0;
3004 }
3005
3006 static int action_confbridgestartrecord(struct mansession *s, const struct message *m)
3007 {
3008         const char *conference_name = astman_get_header(m, "Conference");
3009         const char *recordfile = astman_get_header(m, "RecordFile");
3010         struct confbridge_conference *conference;
3011
3012         if (ast_strlen_zero(conference_name)) {
3013                 astman_send_error(s, m, "No Conference name provided.");
3014                 return 0;
3015         }
3016         if (!ao2_container_count(conference_bridges)) {
3017                 astman_send_error(s, m, "No active conferences.");
3018                 return 0;
3019         }
3020
3021         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3022         if (!conference) {
3023                 astman_send_error(s, m, "No Conference by that name found.");
3024                 return 0;
3025         }
3026
3027         ao2_lock(conference);
3028         if (conf_is_recording(conference)) {
3029                 astman_send_error(s, m, "Conference is already being recorded.");
3030                 ao2_unlock(conference);
3031                 ao2_ref(conference, -1);
3032                 return 0;
3033         }
3034
3035         if (!ast_strlen_zero(recordfile)) {
3036                 ast_copy_string(conference->b_profile.rec_file, recordfile, sizeof(conference->b_profile.rec_file));
3037         }
3038
3039         if (conf_start_record(conference)) {
3040                 astman_send_error(s, m, "Internal error starting conference recording.");
3041                 ao2_unlock(conference);
3042                 ao2_ref(conference, -1);
3043                 return 0;
3044         }
3045         ao2_unlock(conference);
3046
3047         ao2_ref(conference, -1);
3048         astman_send_ack(s, m, "Conference Recording Started.");
3049         return 0;
3050 }
3051 static int action_confbridgestoprecord(struct mansession *s, const struct message *m)
3052 {
3053         const char *conference_name = astman_get_header(m, "Conference");
3054         struct confbridge_conference *conference;
3055
3056         if (ast_strlen_zero(conference_name)) {
3057                 astman_send_error(s, m, "No Conference name provided.");
3058                 return 0;
3059         }
3060         if (!ao2_container_count(conference_bridges)) {
3061                 astman_send_error(s, m, "No active conferences.");
3062                 return 0;
3063         }
3064
3065         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3066         if (!conference) {
3067                 astman_send_error(s, m, "No Conference by that name found.");
3068                 return 0;
3069         }
3070
3071         ao2_lock(conference);
3072         if (conf_stop_record(conference)) {
3073                 ao2_unlock(conference);
3074                 astman_send_error(s, m, "Internal error while stopping recording.");
3075                 ao2_ref(conference, -1);
3076                 return 0;
3077         }
3078         ao2_unlock(conference);
3079
3080         ao2_ref(conference, -1);
3081         astman_send_ack(s, m, "Conference Recording Stopped.");
3082         return 0;
3083 }
3084
3085 static int action_confbridgesetsinglevideosrc(struct mansession *s, const struct message *m)
3086 {
3087         const char *conference_name = astman_get_header(m, "Conference");
3088         const char *channel = astman_get_header(m, "Channel");
3089         struct confbridge_user *user;
3090         struct confbridge_conference *conference;
3091
3092         if (ast_strlen_zero(conference_name)) {
3093                 astman_send_error(s, m, "No Conference name provided.");
3094                 return 0;
3095         }
3096         if (ast_strlen_zero(channel)) {
3097                 astman_send_error(s, m, "No channel name provided.");
3098                 return 0;
3099         }
3100         if (!ao2_container_count(conference_bridges)) {
3101                 astman_send_error(s, m, "No active conferences.");
3102                 return 0;
3103         }
3104
3105         conference = ao2_find(conference_bridges, conference_name, OBJ_KEY);
3106         if (!conference) {
3107                 astman_send_error(s, m, "No Conference by that name found.");
3108                 return 0;
3109         }
3110
3111         /* find channel and set as video src. */
3112         ao2_lock(conference);
3113         AST_LIST_TRAVERSE(&conference->active_list, user, list) {
3114                 if (!strncmp(channel, ast_channel_name(user->chan), strlen(channel))) {
3115                         ast_bridge_set_single_src_video_mode(conference->bridge, user->chan);
3116                         break;
3117                 }
3118         }
3119         ao2_unlock(conference);
3120         ao2_ref(conference, -1);
3121
3122         /* do not access user after conference unlock.  We are just
3123          * using this check to see if it was found or not */
3124         if (!user) {
3125                 astman_send_error(s, m, "No channel by that name found in conference.");
3126                 return 0;
3127         }
3128         astman_send_ack(s, m, "Conference single video source set.");
3129         return 0;
3130 }
3131
3132 static int func_confbridge_info(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
3133 {
3134         char *parse;
3135         struct confbridge_conference *conference;
3136         struct confbridge_user *user;
3137         int count = 0;
3138         AST_DECLARE_APP_ARGS(args,
3139                 AST_APP_ARG(type);
3140                 AST_APP_ARG(confno);
3141         );
3142
3143    &