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