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