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