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