1d30b2ca7956137f4065aa1bc05fca645ba4e4d2
[asterisk/asterisk.git] / apps / app_meetme.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2007, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * SLA Implementation by:
9  * Russell Bryant <russell@digium.com>
10  *
11  * See http://www.asterisk.org for more information about
12  * the Asterisk project. Please do not directly contact
13  * any of the maintainers of this project for assistance;
14  * the project provides a web site, mailing lists and IRC
15  * channels for your use.
16  *
17  * This program is free software, distributed under the terms of
18  * the GNU General Public License Version 2. See the LICENSE file
19  * at the top of the source tree.
20  */
21
22 /*! \file
23  *
24  * \brief Meet me conference bridge and Shared Line Appearances
25  *
26  * \author Mark Spencer <markster@digium.com>
27  * \author (SLA) Russell Bryant <russell@digium.com>
28  * 
29  * \ingroup applications
30  */
31
32 /*** MODULEINFO
33         <depend>dahdi</depend>
34  ***/
35
36 #include "asterisk.h"
37
38 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
39
40 #include <dahdi/user.h>
41
42 #include "asterisk/lock.h"
43 #include "asterisk/file.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/module.h"
47 #include "asterisk/config.h"
48 #include "asterisk/app.h"
49 #include "asterisk/dsp.h"
50 #include "asterisk/musiconhold.h"
51 #include "asterisk/manager.h"
52 #include "asterisk/cli.h"
53 #include "asterisk/say.h"
54 #include "asterisk/utils.h"
55 #include "asterisk/translate.h"
56 #include "asterisk/ulaw.h"
57 #include "asterisk/astobj2.h"
58 #include "asterisk/devicestate.h"
59 #include "asterisk/dial.h"
60 #include "asterisk/causes.h"
61 #include "asterisk/paths.h"
62 #include "asterisk/data.h"
63 #include "asterisk/test.h"
64
65 #include "enter.h"
66 #include "leave.h"
67
68 /*** DOCUMENTATION
69         <application name="MeetMe" language="en_US">
70                 <synopsis>
71                         MeetMe conference bridge.
72                 </synopsis>
73                 <syntax>
74                         <parameter name="confno">
75                                 <para>The conference number</para>
76                         </parameter>
77                         <parameter name="options">
78                                 <optionlist>
79                                         <option name="a">
80                                                 <para>Set admin mode.</para>
81                                         </option>
82                                         <option name="A">
83                                                 <para>Set marked mode.</para>
84                                         </option>
85                                         <option name="b">
86                                                 <para>Run AGI script specified in <variable>MEETME_AGI_BACKGROUND</variable>
87                                                 Default: <literal>conf-background.agi</literal>.</para>
88                                                 <note><para>This does not work with non-DAHDI channels in the same
89                                                 conference).</para></note>
90                                         </option>
91                                         <option name="c">
92                                                 <para>Announce user(s) count on joining a conference.</para>
93                                         </option>
94                                         <option name="C">
95                                                 <para>Continue in dialplan when kicked out of conference.</para>
96                                         </option>
97                                         <option name="d">
98                                                 <para>Dynamically add conference.</para>
99                                         </option>
100                                         <option name="D">
101                                                 <para>Dynamically add conference, prompting for a PIN.</para>
102                                         </option>
103                                         <option name="e">
104                                                 <para>Select an empty conference.</para>
105                                         </option>
106                                         <option name="E">
107                                                 <para>Select an empty pinless conference.</para>
108                                         </option>
109                                         <option name="F">
110                                                 <para>Pass DTMF through the conference.</para>
111                                         </option>
112                                         <option name="G">
113                                                 <argument name="x" required="true">
114                                                         <para>The file to playback</para>
115                                                 </argument>
116                                                 <para>Play an intro announcement in conference.</para>
117                                         </option>
118                                         <option name="i">
119                                                 <para>Announce user join/leave with review.</para>
120                                         </option>
121                                         <option name="I">
122                                                 <para>Announce user join/leave without review.</para>
123                                         </option>
124                                         <option name="l">
125                                                 <para>Set listen only mode (Listen only, no talking).</para>
126                                         </option>
127                                         <option name="m">
128                                                 <para>Set initially muted.</para>
129                                         </option>
130                                         <option name="M" hasparams="optional">
131                                                 <para>Enable music on hold when the conference has a single caller. Optionally,
132                                                 specify a musiconhold class to use. If one is not provided, it will use the
133                                                 channel's currently set music class, or <literal>default</literal>.</para>
134                                                 <argument name="class" required="true" />
135                                         </option>
136                                         <option name="o">
137                                                 <para>Set talker optimization - treats talkers who aren't speaking as
138                                                 being muted, meaning (a) No encode is done on transmission and (b)
139                                                 Received audio that is not registered as talking is omitted causing no
140                                                 buildup in background noise.</para>
141                                         </option>
142                                         <option name="p" hasparams="optional">
143                                                 <para>Allow user to exit the conference by pressing <literal>#</literal> (default)
144                                                 or any of the defined keys. If keys contain <literal>*</literal> this will override
145                                                 option <literal>s</literal>. The key used is set to channel variable
146                                                 <variable>MEETME_EXIT_KEY</variable>.</para>
147                                                 <argument name="keys" required="true" />
148                                         </option>
149                                         <option name="P">
150                                                 <para>Always prompt for the pin even if it is specified.</para>
151                                         </option>
152                                         <option name="q">
153                                                 <para>Quiet mode (don't play enter/leave sounds).</para>
154                                         </option>
155                                         <option name="r">
156                                                 <para>Record conference (records as <variable>MEETME_RECORDINGFILE</variable>
157                                                 using format <variable>MEETME_RECORDINGFORMAT</variable>. Default filename is
158                                                 <literal>meetme-conf-rec-${CONFNO}-${UNIQUEID}</literal> and the default format is
159                                                 wav.</para>
160                                         </option>
161                                         <option name="s">
162                                                 <para>Present menu (user or admin) when <literal>*</literal> is received
163                                                 (send to menu).</para>
164                                         </option>
165                                         <option name="t">
166                                                 <para>Set talk only mode. (Talk only, no listening).</para>
167                                         </option>
168                                         <option name="T">
169                                                 <para>Set talker detection (sent to manager interface and meetme list).</para>
170                                         </option>
171                                         <option name="v" hasparams="optional">
172                                                 <para>Announce when a user is joining or leaving the conference.  Use the voicemail greeting as the announcement.
173                                                  If the i or I options are set, the application will fall back to them if no voicemail greeting can be found.</para>
174                                                 <argument name="mailbox@[context]" required="true">
175                                                         <para>The mailbox and voicemail context to play from.  If no context provided, assumed context is default.</para>
176                                                 </argument>
177                                         </option>
178                                         <option name="w" hasparams="optional">
179                                                 <para>Wait until the marked user enters the conference.</para>
180                                                 <argument name="secs" required="true" />
181                                         </option>
182                                         <option name="x">
183                                                 <para>Close the conference when last marked user exits</para>
184                                         </option>
185                                         <option name="X">
186                                                 <para>Allow user to exit the conference by entering a valid single digit
187                                                 extension <variable>MEETME_EXIT_CONTEXT</variable> or the current context
188                                                 if that variable is not defined.</para>
189                                         </option>
190                                         <option name="1">
191                                                 <para>Do not play message when first person enters</para>
192                                         </option>
193                                         <option name="S">
194                                                 <para>Kick the user <replaceable>x</replaceable> seconds <emphasis>after</emphasis> he entered into
195                                                 the conference.</para>
196                                                 <argument name="x" required="true" />
197                                         </option>
198                                         <option name="L" argsep=":">
199                                                 <para>Limit the conference to <replaceable>x</replaceable> ms. Play a warning when
200                                                 <replaceable>y</replaceable> ms are left. Repeat the warning every <replaceable>z</replaceable> ms.
201                                                 The following special variables can be used with this option:</para>
202                                                 <variablelist>
203                                                         <variable name="CONF_LIMIT_TIMEOUT_FILE">
204                                                                 <para>File to play when time is up.</para>
205                                                         </variable>
206                                                         <variable name="CONF_LIMIT_WARNING_FILE">
207                                                                 <para>File to play as warning if <replaceable>y</replaceable> is defined. The
208                                                                 default is to say the time remaining.</para>
209                                                         </variable>
210                                                 </variablelist>
211                                                 <argument name="x" />
212                                                 <argument name="y" />
213                                                 <argument name="z" />
214                                         </option>
215                                 </optionlist>
216                         </parameter>
217                         <parameter name="pin" />
218                 </syntax>
219                 <description>
220                         <para>Enters the user into a specified MeetMe conference.  If the <replaceable>confno</replaceable>
221                         is omitted, the user will be prompted to enter one.  User can exit the conference by hangup, or
222                         if the <literal>p</literal> option is specified, by pressing <literal>#</literal>.</para>
223                         <note><para>The DAHDI kernel modules and at least one hardware driver (or dahdi_dummy)
224                         must be present for conferencing to operate properly. In addition, the chan_dahdi channel driver
225                         must be loaded for the <literal>i</literal> and <literal>r</literal> options to operate at
226                         all.</para></note>
227                 </description>
228                 <see-also>
229                         <ref type="application">MeetMeCount</ref>
230                         <ref type="application">MeetMeAdmin</ref>
231                         <ref type="application">MeetMeChannelAdmin</ref>
232                 </see-also>
233         </application>
234         <application name="MeetMeCount" language="en_US">
235                 <synopsis>
236                         MeetMe participant count.
237                 </synopsis>
238                 <syntax>
239                         <parameter name="confno" required="true">
240                                 <para>Conference number.</para>
241                         </parameter>
242                         <parameter name="var" />
243                 </syntax>
244                 <description>
245                         <para>Plays back the number of users in the specified MeetMe conference.
246                         If <replaceable>var</replaceable> is specified, playback will be skipped and the value
247                         will be returned in the variable. Upon application completion, MeetMeCount will hangup
248                         the channel, unless priority <literal>n+1</literal> exists, in which case priority progress will
249                         continue.</para>
250                 </description>
251                 <see-also>
252                         <ref type="application">MeetMe</ref>
253                 </see-also>
254         </application>
255         <application name="MeetMeAdmin" language="en_US">
256                 <synopsis>
257                         MeetMe conference administration.
258                 </synopsis>
259                 <syntax>
260                         <parameter name="confno" required="true" />
261                         <parameter name="command" required="true">
262                                 <optionlist>
263                                         <option name="e">
264                                                 <para>Eject last user that joined.</para>
265                                         </option>
266                                         <option name="E">
267                                                 <para>Extend conference end time, if scheduled.</para>
268                                         </option>
269                                         <option name="k">
270                                                 <para>Kick one user out of conference.</para>
271                                         </option>
272                                         <option name="K">
273                                                 <para>Kick all users out of conference.</para>
274                                         </option>
275                                         <option name="l">
276                                                 <para>Unlock conference.</para>
277                                         </option>
278                                         <option name="L">
279                                                 <para>Lock conference.</para>
280                                         </option>
281                                         <option name="m">
282                                                 <para>Unmute one user.</para>
283                                         </option>
284                                         <option name="M">
285                                                 <para>Mute one user.</para>
286                                         </option>
287                                         <option name="n">
288                                                 <para>Unmute all users in the conference.</para>
289                                         </option>
290                                         <option name="N">
291                                                 <para>Mute all non-admin users in the conference.</para>
292                                         </option>
293                                         <option name="r">
294                                                 <para>Reset one user's volume settings.</para>
295                                         </option>
296                                         <option name="R">
297                                                 <para>Reset all users volume settings.</para>
298                                         </option>
299                                         <option name="s">
300                                                 <para>Lower entire conference speaking volume.</para>
301                                         </option>
302                                         <option name="S">
303                                                 <para>Raise entire conference speaking volume.</para>
304                                         </option>
305                                         <option name="t">
306                                                 <para>Lower one user's talk volume.</para>
307                                         </option>
308                                         <option name="T">
309                                                 <para>Raise one user's talk volume.</para>
310                                         </option>
311                                         <option name="u">
312                                                 <para>Lower one user's listen volume.</para>
313                                         </option>
314                                         <option name="U">
315                                                 <para>Raise one user's listen volume.</para>
316                                         </option>
317                                         <option name="v">
318                                                 <para>Lower entire conference listening volume.</para>
319                                         </option>
320                                         <option name="V">
321                                                 <para>Raise entire conference listening volume.</para>
322                                         </option>
323                                 </optionlist>
324                         </parameter>
325                         <parameter name="user" />
326                 </syntax>
327                 <description>
328                         <para>Run admin <replaceable>command</replaceable> for conference <replaceable>confno</replaceable>.</para>
329                         <para>Will additionally set the variable <variable>MEETMEADMINSTATUS</variable> with one of
330                         the following values:</para>
331                         <variablelist>
332                                 <variable name="MEETMEADMINSTATUS">
333                                         <value name="NOPARSE">
334                                                 Invalid arguments.
335                                         </value>
336                                         <value name="NOTFOUND">
337                                                 User specified was not found.
338                                         </value>
339                                         <value name="FAILED">
340                                                 Another failure occurred.
341                                         </value>
342                                         <value name="OK">
343                                                 The operation was completed successfully.
344                                         </value>
345                                 </variable>
346                         </variablelist>
347                 </description>
348                 <see-also>
349                         <ref type="application">MeetMe</ref>
350                 </see-also>
351         </application>
352         <application name="MeetMeChannelAdmin" language="en_US">
353                 <synopsis>
354                         MeetMe conference Administration (channel specific).
355                 </synopsis>
356                 <syntax>
357                         <parameter name="channel" required="true" />
358                         <parameter name="command" required="true">
359                                 <optionlist>
360                                         <option name="k">
361                                                 <para>Kick the specified user out of the conference he is in.</para>
362                                         </option>
363                                         <option name="m">
364                                                 <para>Unmute the specified user.</para>
365                                         </option>
366                                         <option name="M">
367                                                 <para>Mute the specified user.</para>
368                                         </option>
369                                 </optionlist>
370                         </parameter>
371                 </syntax>
372                 <description>
373                         <para>Run admin <replaceable>command</replaceable> for a specific
374                         <replaceable>channel</replaceable> in any coference.</para>
375                 </description>
376         </application>
377         <application name="SLAStation" language="en_US">
378                 <synopsis>
379                         Shared Line Appearance Station.
380                 </synopsis>
381                 <syntax>
382                         <parameter name="station" required="true">
383                                 <para>Station name</para>
384                         </parameter>
385                 </syntax>
386                 <description>
387                         <para>This application should be executed by an SLA station. The argument depends
388                         on how the call was initiated. If the phone was just taken off hook, then the argument
389                         <replaceable>station</replaceable> should be just the station name. If the call was
390                         initiated by pressing a line key, then the station name should be preceded by an underscore
391                         and the trunk name associated with that line button.</para>
392                         <para>For example: <literal>station1_line1</literal></para>
393                         <para>On exit, this application will set the variable <variable>SLASTATION_STATUS</variable> to
394                         one of the following values:</para>
395                         <variablelist>
396                                 <variable name="SLASTATION_STATUS">
397                                         <value name="FAILURE" />
398                                         <value name="CONGESTION" />
399                                         <value name="SUCCESS" />
400                                 </variable>
401                         </variablelist>
402                 </description>
403         </application>
404         <application name="SLATrunk" language="en_US">
405                 <synopsis>
406                         Shared Line Appearance Trunk.
407                 </synopsis>
408                 <syntax>
409                         <parameter name="trunk" required="true">
410                                 <para>Trunk name</para>
411                         </parameter>
412                         <parameter name="options">
413                                 <optionlist>
414                                         <option name="M" hasparams="optional">
415                                                 <para>Play back the specified MOH <replaceable>class</replaceable>
416                                                 instead of ringing</para>
417                                                 <argument name="class" required="true" />
418                                         </option>
419                                 </optionlist>
420                         </parameter>
421                 </syntax>
422                 <description>
423                         <para>This application should be executed by an SLA trunk on an inbound call. The channel calling
424                         this application should correspond to the SLA trunk with the name <replaceable>trunk</replaceable>
425                         that is being passed as an argument.</para>
426                         <para>On exit, this application will set the variable <variable>SLATRUNK_STATUS</variable> to
427                         one of the following values:</para>
428                         <variablelist>
429                                 <variable name="SLATRUNK_STATUS">
430                                         <value name="FAILURE" />
431                                         <value name="SUCCESS" />
432                                         <value name="UNANSWERED" />
433                                         <value name="RINGTIMEOUT" />
434                                 </variable>
435                         </variablelist>
436                 </description>
437         </application>
438         <function name="MEETME_INFO" language="en_US">
439                 <synopsis>
440                         Query a given conference of various properties.
441                 </synopsis>
442                 <syntax>
443                         <parameter name="keyword" required="true">
444                                 <para>Options:</para>
445                                 <enumlist>
446                                         <enum name="lock">
447                                                 <para>Boolean of whether the corresponding conference is locked.</para>
448                                         </enum>
449                                         <enum name="parties">
450                                                 <para>Number of parties in a given conference</para>
451                                         </enum>
452                                         <enum name="activity">
453                                                 <para>Duration of conference in seconds.</para>
454                                         </enum>
455                                         <enum name="dynamic">
456                                                 <para>Boolean of whether the corresponding conference is dynamic.</para>
457                                         </enum>
458                                 </enumlist>
459                         </parameter>
460                         <parameter name="confno" required="true">
461                                 <para>Conference number to retrieve information from.</para>
462                         </parameter>
463                 </syntax>
464                 <description />
465                 <see-also>
466                         <ref type="application">MeetMe</ref>
467                         <ref type="application">MeetMeCount</ref>
468                         <ref type="application">MeetMeAdmin</ref>
469                         <ref type="application">MeetMeChannelAdmin</ref>
470                 </see-also>
471         </function>
472         <manager name="MeetmeMute" language="en_US">
473                 <synopsis>
474                         Mute a Meetme user.
475                 </synopsis>
476                 <syntax>
477                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
478                         <parameter name="Meetme" required="true" />
479                         <parameter name="Usernum" required="true" />
480                 </syntax>
481                 <description>
482                 </description>
483         </manager>
484         <manager name="MeetmeUnmute" language="en_US">
485                 <synopsis>
486                         Unmute a Meetme user.
487                 </synopsis>
488                 <syntax>
489                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
490                         <parameter name="Meetme" required="true" />
491                         <parameter name="Usernum" required="true" />
492                 </syntax>
493                 <description>
494                 </description>
495         </manager>
496         <manager name="MeetmeList" language="en_US">
497                 <synopsis>
498                         List participants in a conference.
499                 </synopsis>
500                 <syntax>
501                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
502                         <parameter name="Conference" required="false">
503                                 <para>Conference number.</para>
504                         </parameter>
505                 </syntax>
506                 <description>
507                         <para>Lists all users in a particular MeetMe conference.
508                         MeetmeList will follow as separate events, followed by a final event called
509                         MeetmeListComplete.</para>
510                 </description>
511         </manager>
512         <manager name="MeetmeListRooms" language="en_US">
513                 <synopsis>
514                         List active conferences.
515                 </synopsis>
516                 <syntax>
517                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
518                 </syntax>
519                 <description>
520                         <para>Lists data about all active conferences.
521                                 MeetmeListRooms will follow as separate events, followed by a final event called
522                                 MeetmeListRoomsComplete.</para>
523                 </description>
524         </manager>
525  ***/
526
527 #define CONFIG_FILE_NAME "meetme.conf"
528 #define SLA_CONFIG_FILE  "sla.conf"
529
530 /*! each buffer is 20ms, so this is 640ms total */
531 #define DEFAULT_AUDIO_BUFFERS  32
532
533 /*! String format for scheduled conferences */
534 #define DATE_FORMAT "%Y-%m-%d %H:%M:%S"
535
536 enum {
537         ADMINFLAG_MUTED =     (1 << 1), /*!< User is muted */
538         ADMINFLAG_SELFMUTED = (1 << 2), /*!< User muted self */
539         ADMINFLAG_KICKME =    (1 << 3),  /*!< User has been kicked */
540         /*! User has requested to speak */
541         ADMINFLAG_T_REQUEST = (1 << 4),
542 };
543
544 #define MEETME_DELAYDETECTTALK     300
545 #define MEETME_DELAYDETECTENDTALK  1000
546
547 #define AST_FRAME_BITS  32
548
549 enum volume_action {
550         VOL_UP,
551         VOL_DOWN
552 };
553
554 enum entrance_sound {
555         ENTER,
556         LEAVE
557 };
558
559 enum recording_state {
560         MEETME_RECORD_OFF,
561         MEETME_RECORD_STARTED,
562         MEETME_RECORD_ACTIVE,
563         MEETME_RECORD_TERMINATE
564 };
565
566 #define CONF_SIZE  320
567
568 enum {
569         /*! user has admin access on the conference */
570         CONFFLAG_ADMIN = (1 << 0),
571         /*! If set the user can only receive audio from the conference */
572         CONFFLAG_MONITOR = (1 << 1),
573         /*! If set asterisk will exit conference when key defined in p() option is pressed */
574         CONFFLAG_KEYEXIT = (1 << 2),
575         /*! If set asterisk will provide a menu to the user when '*' is pressed */
576         CONFFLAG_STARMENU = (1 << 3),
577         /*! If set the use can only send audio to the conference */
578         CONFFLAG_TALKER = (1 << 4),
579         /*! If set there will be no enter or leave sounds */
580         CONFFLAG_QUIET = (1 << 5),
581         /*! If set, when user joins the conference, they will be told the number 
582          *  of users that are already in */
583         CONFFLAG_ANNOUNCEUSERCOUNT = (1 << 6),
584         /*! Set to run AGI Script in Background */
585         CONFFLAG_AGI = (1 << 7),
586         /*! Set to have music on hold when user is alone in conference */
587         CONFFLAG_MOH = (1 << 8),
588         /*! If set the MeetMe will return if all marked with this flag left */
589         CONFFLAG_MARKEDEXIT = (1 << 9),
590         /*! If set, the MeetMe will wait until a marked user enters */
591         CONFFLAG_WAITMARKED = (1 << 10),
592         /*! If set, the MeetMe will exit to the specified context */
593         CONFFLAG_EXIT_CONTEXT = (1 << 11),
594         /*! If set, the user will be marked */
595         CONFFLAG_MARKEDUSER = (1 << 12),
596         /*! If set, user will be ask record name on entry of conference */
597         CONFFLAG_INTROUSER = (1 << 13),
598         /*! If set, the MeetMe will be recorded */
599         CONFFLAG_RECORDCONF = (1<< 14),
600         /*! If set, the user will be monitored if the user is talking or not */
601         CONFFLAG_MONITORTALKER = (1 << 15),
602         CONFFLAG_DYNAMIC = (1 << 16),
603         CONFFLAG_DYNAMICPIN = (1 << 17),
604         CONFFLAG_EMPTY = (1 << 18),
605         CONFFLAG_EMPTYNOPIN = (1 << 19),
606         CONFFLAG_ALWAYSPROMPT = (1 << 20),
607         /*! If set, treat talking users as muted users */
608         CONFFLAG_OPTIMIZETALKER = (1 << 21),
609         /*! If set, won't speak the extra prompt when the first person 
610          *  enters the conference */
611         CONFFLAG_NOONLYPERSON = (1 << 22),
612         /*! If set, user will be asked to record name on entry of conference 
613          *  without review */
614         CONFFLAG_INTROUSERNOREVIEW = (1 << 23),
615         /*! If set, the user will be initially self-muted */
616         CONFFLAG_STARTMUTED = (1 << 24),
617         /*! Pass DTMF through the conference */
618         CONFFLAG_PASS_DTMF = (1 << 25),
619         CONFFLAG_SLA_STATION = (1 << 26),
620         CONFFLAG_SLA_TRUNK = (1 << 27),
621         /*! If set, the user should continue in the dialplan if kicked out */
622         CONFFLAG_KICK_CONTINUE = (1 << 28),
623         CONFFLAG_DURATION_STOP = (1 << 29),
624         CONFFLAG_DURATION_LIMIT = (1 << 30),
625         /*! Do not write any audio to this channel until the state is up. */
626         CONFFLAG_NO_AUDIO_UNTIL_UP = (1 << 31),
627 };
628
629 /* !If set play an intro announcement at start of conference */
630 #define CONFFLAG_INTROMSG ((uint64_t)1 << 32)
631 #define CONFFLAG_INTROUSER_VMREC ((uint64_t)1 << 33)
632
633 enum {
634         OPT_ARG_WAITMARKED = 0,
635         OPT_ARG_EXITKEYS   = 1,
636         OPT_ARG_DURATION_STOP = 2,
637         OPT_ARG_DURATION_LIMIT = 3,
638         OPT_ARG_MOH_CLASS = 4,
639         OPT_ARG_INTROMSG = 5,
640         OPT_ARG_INTROUSER_VMREC = 6,
641         OPT_ARG_ARRAY_SIZE = 7,
642 };
643
644 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
645         AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
646         AST_APP_OPTION('a', CONFFLAG_ADMIN ),
647         AST_APP_OPTION('b', CONFFLAG_AGI ),
648         AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
649         AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
650         AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
651         AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
652         AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
653         AST_APP_OPTION('e', CONFFLAG_EMPTY ),
654         AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
655         AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
656         AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
657         AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
658         AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
659         AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
660         AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
661         AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
662         AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
663         AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
664         AST_APP_OPTION('q', CONFFLAG_QUIET ),
665         AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
666         AST_APP_OPTION('s', CONFFLAG_STARMENU ),
667         AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
668         AST_APP_OPTION('l', CONFFLAG_MONITOR ),
669         AST_APP_OPTION('t', CONFFLAG_TALKER ),
670         AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
671         AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
672         AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
673         AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
674         AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
675         AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
676 END_OPTIONS );
677
678 static const char * const app = "MeetMe";
679 static const char * const app2 = "MeetMeCount";
680 static const char * const app3 = "MeetMeAdmin";
681 static const char * const app4 = "MeetMeChannelAdmin";
682 static const char * const slastation_app = "SLAStation";
683 static const char * const slatrunk_app = "SLATrunk";
684
685 /* Lookup RealTime conferences based on confno and current time */
686 static int rt_schedule;
687 static int fuzzystart;
688 static int earlyalert;
689 static int endalert;
690 static int extendby;
691
692 /*! Log participant count to the RealTime backend */
693 static int rt_log_members;
694
695 #define MAX_CONFNUM 80
696 #define MAX_PIN     80
697 #define OPTIONS_LEN 100
698
699 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
700 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
701
702 enum announcetypes {
703         CONF_HASJOIN,
704         CONF_HASLEFT
705 };
706
707 struct announce_listitem {
708         AST_LIST_ENTRY(announce_listitem) entry;
709         char namerecloc[PATH_MAX];                              /*!< Name Recorded file Location */
710         char language[MAX_LANGUAGE];
711         struct ast_channel *confchan;
712         int confusers;
713         int vmrec;
714         enum announcetypes announcetype;
715 };
716
717 /*! \brief The MeetMe Conference object */
718 struct ast_conference {
719         ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
720         ast_mutex_t listenlock;                 /*!< Conference specific lock (listeners) */
721         char confno[MAX_CONFNUM];               /*!< Conference */
722         struct ast_channel *chan;               /*!< Announcements channel */
723         struct ast_channel *lchan;              /*!< Listen/Record channel */
724         int fd;                                 /*!< Announcements fd */
725         int dahdiconf;                            /*!< DAHDI Conf # */
726         int users;                              /*!< Number of active users */
727         int markedusers;                        /*!< Number of marked users */
728         int maxusers;                           /*!< Participant limit if scheduled */
729         int endalert;                           /*!< When to play conf ending message */
730         time_t start;                           /*!< Start time (s) */
731         int refcount;                           /*!< reference count of usage */
732         enum recording_state recording:2;       /*!< recording status */
733         unsigned int isdynamic:1;               /*!< Created on the fly? */
734         unsigned int locked:1;                  /*!< Is the conference locked? */
735         unsigned int gmuted:1;                  /*!< Is the conference globally muted? (all non-admins) */
736         pthread_t recordthread;                 /*!< thread for recording */
737         ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
738         pthread_attr_t attr;                    /*!< thread attribute */
739         char *recordingfilename;                /*!< Filename to record the Conference into */
740         char *recordingformat;                  /*!< Format to record the Conference in */
741         char pin[MAX_PIN];                      /*!< If protected by a PIN */
742         char pinadmin[MAX_PIN];                 /*!< If protected by a admin PIN */
743         char uniqueid[32];
744         long endtime;                           /*!< When to end the conf if scheduled */
745         const char *useropts;                   /*!< RealTime user flags */
746         const char *adminopts;                  /*!< RealTime moderator flags */
747         const char *bookid;                     /*!< RealTime conference id */
748         struct ast_frame *transframe[32];
749         struct ast_frame *origframe;
750         struct ast_trans_pvt *transpath[32];
751         struct ao2_container *usercontainer;
752         AST_LIST_ENTRY(ast_conference) list;
753         /* announce_thread related data */
754         pthread_t announcethread;
755         ast_mutex_t announcethreadlock;
756         unsigned int announcethread_stop:1;
757         ast_cond_t announcelist_addition;
758         AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
759         ast_mutex_t announcelistlock;
760 };
761
762 static AST_LIST_HEAD_STATIC(confs, ast_conference);
763
764 static unsigned int conf_map[1024] = {0, };
765
766 struct volume {
767         int desired;                            /*!< Desired volume adjustment */
768         int actual;                             /*!< Actual volume adjustment (for channels that can't adjust) */
769 };
770
771 /*! \brief The MeetMe User object */
772 struct ast_conf_user {
773         int user_no;                            /*!< User Number */
774         struct ast_flags64 userflags;           /*!< Flags as set in the conference */
775         int adminflags;                         /*!< Flags set by the Admin */
776         struct ast_channel *chan;               /*!< Connected channel */
777         int talking;                            /*!< Is user talking */
778         int dahdichannel;                       /*!< Is a DAHDI channel */
779         char usrvalue[50];                      /*!< Custom User Value */
780         char namerecloc[PATH_MAX];              /*!< Name Recorded file Location */
781         time_t jointime;                        /*!< Time the user joined the conference */
782         time_t kicktime;                        /*!< Time the user will be kicked from the conference */
783         struct timeval start_time;              /*!< Time the user entered into the conference */
784         long timelimit;                         /*!< Time limit for the user to be in the conference L(x:y:z) */
785         long play_warning;                      /*!< Play a warning when 'y' ms are left */
786         long warning_freq;                      /*!< Repeat the warning every 'z' ms */
787         const char *warning_sound;              /*!< File to play as warning if 'y' is defined */
788         const char *end_sound;                  /*!< File to play when time is up. */
789         struct volume talk;
790         struct volume listen;
791         AST_LIST_ENTRY(ast_conf_user) list;
792 };
793
794 enum sla_which_trunk_refs {
795         ALL_TRUNK_REFS,
796         INACTIVE_TRUNK_REFS,
797 };
798
799 enum sla_trunk_state {
800         SLA_TRUNK_STATE_IDLE,
801         SLA_TRUNK_STATE_RINGING,
802         SLA_TRUNK_STATE_UP,
803         SLA_TRUNK_STATE_ONHOLD,
804         SLA_TRUNK_STATE_ONHOLD_BYME,
805 };
806
807 enum sla_hold_access {
808         /*! This means that any station can put it on hold, and any station
809          * can retrieve the call from hold. */
810         SLA_HOLD_OPEN,
811         /*! This means that only the station that put the call on hold may
812          * retrieve it from hold. */
813         SLA_HOLD_PRIVATE,
814 };
815
816 struct sla_trunk_ref;
817
818 struct sla_station {
819         AST_RWLIST_ENTRY(sla_station) entry;
820         AST_DECLARE_STRING_FIELDS(
821                 AST_STRING_FIELD(name); 
822                 AST_STRING_FIELD(device);       
823                 AST_STRING_FIELD(autocontext);  
824         );
825         AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
826         struct ast_dial *dial;
827         /*! Ring timeout for this station, for any trunk.  If a ring timeout
828          *  is set for a specific trunk on this station, that will take
829          *  priority over this value. */
830         unsigned int ring_timeout;
831         /*! Ring delay for this station, for any trunk.  If a ring delay
832          *  is set for a specific trunk on this station, that will take
833          *  priority over this value. */
834         unsigned int ring_delay;
835         /*! This option uses the values in the sla_hold_access enum and sets the
836          * access control type for hold on this station. */
837         unsigned int hold_access:1;
838         /*! Use count for inside sla_station_exec */
839         unsigned int ref_count;
840 };
841
842 struct sla_station_ref {
843         AST_LIST_ENTRY(sla_station_ref) entry;
844         struct sla_station *station;
845 };
846
847 struct sla_trunk {
848         AST_RWLIST_ENTRY(sla_trunk) entry;
849         AST_DECLARE_STRING_FIELDS(
850                 AST_STRING_FIELD(name);
851                 AST_STRING_FIELD(device);
852                 AST_STRING_FIELD(autocontext);  
853         );
854         AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
855         /*! Number of stations that use this trunk */
856         unsigned int num_stations;
857         /*! Number of stations currently on a call with this trunk */
858         unsigned int active_stations;
859         /*! Number of stations that have this trunk on hold. */
860         unsigned int hold_stations;
861         struct ast_channel *chan;
862         unsigned int ring_timeout;
863         /*! If set to 1, no station will be able to join an active call with
864          *  this trunk. */
865         unsigned int barge_disabled:1;
866         /*! This option uses the values in the sla_hold_access enum and sets the
867          * access control type for hold on this trunk. */
868         unsigned int hold_access:1;
869         /*! Whether this trunk is currently on hold, meaning that once a station
870          *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
871         unsigned int on_hold:1;
872         /*! Use count for inside sla_trunk_exec */
873         unsigned int ref_count;
874 };
875
876 struct sla_trunk_ref {
877         AST_LIST_ENTRY(sla_trunk_ref) entry;
878         struct sla_trunk *trunk;
879         enum sla_trunk_state state;
880         struct ast_channel *chan;
881         /*! Ring timeout to use when this trunk is ringing on this specific
882          *  station.  This takes higher priority than a ring timeout set at
883          *  the station level. */
884         unsigned int ring_timeout;
885         /*! Ring delay to use when this trunk is ringing on this specific
886          *  station.  This takes higher priority than a ring delay set at
887          *  the station level. */
888         unsigned int ring_delay;
889 };
890
891 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
892 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
893
894 static const char sla_registrar[] = "SLA";
895
896 /*! \brief Event types that can be queued up for the SLA thread */
897 enum sla_event_type {
898         /*! A station has put the call on hold */
899         SLA_EVENT_HOLD,
900         /*! The state of a dial has changed */
901         SLA_EVENT_DIAL_STATE,
902         /*! The state of a ringing trunk has changed */
903         SLA_EVENT_RINGING_TRUNK,
904         /*! A reload of configuration has been requested */
905         SLA_EVENT_RELOAD,
906         /*! Poke the SLA thread so it can check if it can perform a reload */
907         SLA_EVENT_CHECK_RELOAD,
908 };
909
910 struct sla_event {
911         enum sla_event_type type;
912         struct sla_station *station;
913         struct sla_trunk_ref *trunk_ref;
914         AST_LIST_ENTRY(sla_event) entry;
915 };
916
917 /*! \brief A station that failed to be dialed 
918  * \note Only used by the SLA thread. */
919 struct sla_failed_station {
920         struct sla_station *station;
921         struct timeval last_try;
922         AST_LIST_ENTRY(sla_failed_station) entry;
923 };
924
925 /*! \brief A trunk that is ringing */
926 struct sla_ringing_trunk {
927         struct sla_trunk *trunk;
928         /*! The time that this trunk started ringing */
929         struct timeval ring_begin;
930         AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
931         AST_LIST_ENTRY(sla_ringing_trunk) entry;
932 };
933
934 enum sla_station_hangup {
935         SLA_STATION_HANGUP_NORMAL,
936         SLA_STATION_HANGUP_TIMEOUT,
937 };
938
939 /*! \brief A station that is ringing */
940 struct sla_ringing_station {
941         struct sla_station *station;
942         /*! The time that this station started ringing */
943         struct timeval ring_begin;
944         AST_LIST_ENTRY(sla_ringing_station) entry;
945 };
946
947 /*!
948  * \brief A structure for data used by the sla thread
949  */
950 static struct {
951         /*! The SLA thread ID */
952         pthread_t thread;
953         ast_cond_t cond;
954         ast_mutex_t lock;
955         AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
956         AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
957         AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
958         AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
959         unsigned int stop:1;
960         /*! Attempt to handle CallerID, even though it is known not to work
961          *  properly in some situations. */
962         unsigned int attempt_callerid:1;
963         /*! A reload has been requested */
964         unsigned int reload:1;
965 } sla = {
966         .thread = AST_PTHREADT_NULL,
967 };
968
969 /*! \brief The number of audio buffers to be allocated on pseudo channels
970  *  when in a conference */
971 static int audio_buffers;
972
973 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB) 
974  *    settings for channel drivers.
975  *
976  *  \note these are not a straight linear-to-dB
977  *  conversion... the numbers have been modified
978  *  to give the user a better level of adjustability.
979  */
980 static const char gain_map[] = {
981         -15,
982         -13,
983         -10,
984         -6,
985         0,
986         0,
987         0,
988         6,
989         10,
990         13,
991         15,
992 };
993
994
995 static int admin_exec(struct ast_channel *chan, const char *data);
996 static void *recordthread(void *args);
997
998 static const char *istalking(int x)
999 {
1000         if (x > 0)
1001                 return "(talking)";
1002         else if (x < 0)
1003                 return "(unmonitored)";
1004         else 
1005                 return "(not talking)";
1006 }
1007
1008 static int careful_write(int fd, unsigned char *data, int len, int block)
1009 {
1010         int res;
1011         int x;
1012
1013         while (len) {
1014                 if (block) {
1015                         x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1016                         res = ioctl(fd, DAHDI_IOMUX, &x);
1017                 } else
1018                         res = 0;
1019                 if (res >= 0)
1020                         res = write(fd, data, len);
1021                 if (res < 1) {
1022                         if (errno != EAGAIN) {
1023                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1024                                 return -1;
1025                         } else
1026                                 return 0;
1027                 }
1028                 len -= res;
1029                 data += res;
1030         }
1031
1032         return 0;
1033 }
1034
1035 static int set_talk_volume(struct ast_conf_user *user, int volume)
1036 {
1037         char gain_adjust;
1038
1039         /* attempt to make the adjustment in the channel driver;
1040            if successful, don't adjust in the frame reading routine
1041         */
1042         gain_adjust = gain_map[volume + 5];
1043
1044         return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1045 }
1046
1047 static int set_listen_volume(struct ast_conf_user *user, int volume)
1048 {
1049         char gain_adjust;
1050
1051         /* attempt to make the adjustment in the channel driver;
1052            if successful, don't adjust in the frame reading routine
1053         */
1054         gain_adjust = gain_map[volume + 5];
1055
1056         return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1057 }
1058
1059 static void tweak_volume(struct volume *vol, enum volume_action action)
1060 {
1061         switch (action) {
1062         case VOL_UP:
1063                 switch (vol->desired) { 
1064                 case 5:
1065                         break;
1066                 case 0:
1067                         vol->desired = 2;
1068                         break;
1069                 case -2:
1070                         vol->desired = 0;
1071                         break;
1072                 default:
1073                         vol->desired++;
1074                         break;
1075                 }
1076                 break;
1077         case VOL_DOWN:
1078                 switch (vol->desired) {
1079                 case -5:
1080                         break;
1081                 case 2:
1082                         vol->desired = 0;
1083                         break;
1084                 case 0:
1085                         vol->desired = -2;
1086                         break;
1087                 default:
1088                         vol->desired--;
1089                         break;
1090                 }
1091         }
1092 }
1093
1094 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1095 {
1096         tweak_volume(&user->talk, action);
1097         /* attempt to make the adjustment in the channel driver;
1098            if successful, don't adjust in the frame reading routine
1099         */
1100         if (!set_talk_volume(user, user->talk.desired))
1101                 user->talk.actual = 0;
1102         else
1103                 user->talk.actual = user->talk.desired;
1104 }
1105
1106 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1107 {
1108         tweak_volume(&user->listen, action);
1109         /* attempt to make the adjustment in the channel driver;
1110            if successful, don't adjust in the frame reading routine
1111         */
1112         if (!set_listen_volume(user, user->listen.desired))
1113                 user->listen.actual = 0;
1114         else
1115                 user->listen.actual = user->listen.desired;
1116 }
1117
1118 static void reset_volumes(struct ast_conf_user *user)
1119 {
1120         signed char zero_volume = 0;
1121
1122         ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1123         ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1124 }
1125
1126 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1127 {
1128         unsigned char *data;
1129         int len;
1130         int res = -1;
1131
1132         if (!ast_check_hangup(chan))
1133                 res = ast_autoservice_start(chan);
1134
1135         AST_LIST_LOCK(&confs);
1136
1137         switch(sound) {
1138         case ENTER:
1139                 data = enter;
1140                 len = sizeof(enter);
1141                 break;
1142         case LEAVE:
1143                 data = leave;
1144                 len = sizeof(leave);
1145                 break;
1146         default:
1147                 data = NULL;
1148                 len = 0;
1149         }
1150         if (data) {
1151                 careful_write(conf->fd, data, len, 1);
1152         }
1153
1154         AST_LIST_UNLOCK(&confs);
1155
1156         if (!res) 
1157                 ast_autoservice_stop(chan);
1158 }
1159
1160 static int user_no_cmp(void *obj, void *arg, int flags)
1161 {
1162         struct ast_conf_user *user = obj;
1163         int *user_no = arg;
1164
1165         if (user->user_no == *user_no) {
1166                 return (CMP_MATCH | CMP_STOP);
1167         }
1168
1169         return 0;
1170 }
1171
1172 static int user_max_cmp(void *obj, void *arg, int flags)
1173 {
1174         struct ast_conf_user *user = obj;
1175         int *max_no = arg;
1176
1177         if (user->user_no > *max_no) {
1178                 *max_no = user->user_no;
1179         }
1180
1181         return 0;
1182 }
1183
1184 /*!
1185  * \brief Find or create a conference
1186  *
1187  * \param confno The conference name/number
1188  * \param pin The regular user pin
1189  * \param pinadmin The admin pin
1190  * \param make Make the conf if it doesn't exist
1191  * \param dynamic Mark the newly created conference as dynamic
1192  * \param refcount How many references to mark on the conference
1193  * \param chan The asterisk channel
1194  *
1195  * \return A pointer to the conference struct, or NULL if it wasn't found and
1196  *         make or dynamic were not set.
1197  */
1198 static struct ast_conference *build_conf(const char *confno, const char *pin,
1199         const char *pinadmin, int make, int dynamic, int refcount,
1200         const struct ast_channel *chan, struct ast_test *test)
1201 {
1202         struct ast_conference *cnf;
1203         struct dahdi_confinfo dahdic = { 0, };
1204         int confno_int = 0;
1205         struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
1206         struct ast_format tmp_fmt;
1207
1208         AST_LIST_LOCK(&confs);
1209
1210         AST_LIST_TRAVERSE(&confs, cnf, list) {
1211                 if (!strcmp(confno, cnf->confno)) 
1212                         break;
1213         }
1214
1215         if (cnf || (!make && !dynamic) || !cap_slin)
1216                 goto cnfout;
1217
1218         ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
1219         /* Make a new one */
1220         if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
1221                 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
1222                 goto cnfout;
1223         }
1224
1225         ast_mutex_init(&cnf->playlock);
1226         ast_mutex_init(&cnf->listenlock);
1227         cnf->recordthread = AST_PTHREADT_NULL;
1228         ast_mutex_init(&cnf->recordthreadlock);
1229         cnf->announcethread = AST_PTHREADT_NULL;
1230         ast_mutex_init(&cnf->announcethreadlock);
1231         ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1232         ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1233         ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1234         ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1235
1236         /* Setup a new dahdi conference */
1237         dahdic.confno = -1;
1238         dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1239         cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1240         if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1241                 if (test) {
1242                         /* if we are creating a conference for a unit test, it is not neccesary
1243                          * to open a pseudo channel, so, if we fail continue creating
1244                          * the conference. */
1245                         ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1246                 } else {
1247                         ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1248                         if (cnf->fd >= 0)
1249                                 close(cnf->fd);
1250                         ao2_ref(cnf->usercontainer, -1);
1251                         ast_mutex_destroy(&cnf->playlock);
1252                         ast_mutex_destroy(&cnf->listenlock);
1253                         ast_mutex_destroy(&cnf->recordthreadlock);
1254                         ast_mutex_destroy(&cnf->announcethreadlock);
1255                         ast_free(cnf);
1256                         cnf = NULL;
1257                         goto cnfout;
1258                 }
1259         }
1260
1261         cnf->dahdiconf = dahdic.confno;
1262
1263         /* Setup a new channel for playback of audio files */
1264         cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
1265         if (cnf->chan) {
1266                 ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1267                 ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1268                 dahdic.chan = 0;
1269                 dahdic.confno = cnf->dahdiconf;
1270                 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1271                 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1272                         if (test) {
1273                                 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1274                         }
1275                         ast_log(LOG_WARNING, "Error setting conference\n");
1276                         if (cnf->chan)
1277                                 ast_hangup(cnf->chan);
1278                         else
1279                                 close(cnf->fd);
1280                         ao2_ref(cnf->usercontainer, -1);
1281                         ast_mutex_destroy(&cnf->playlock);
1282                         ast_mutex_destroy(&cnf->listenlock);
1283                         ast_mutex_destroy(&cnf->recordthreadlock);
1284                         ast_mutex_destroy(&cnf->announcethreadlock);
1285                         ast_free(cnf);
1286                         cnf = NULL;
1287                         goto cnfout;
1288                 }
1289         }
1290
1291         /* Fill the conference struct */
1292         cnf->start = time(NULL);
1293         cnf->maxusers = 0x7fffffff;
1294         cnf->isdynamic = dynamic ? 1 : 0;
1295         ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1296         AST_LIST_INSERT_HEAD(&confs, cnf, list);
1297
1298         /* Reserve conference number in map */
1299         if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1300                 conf_map[confno_int] = 1;
1301         
1302 cnfout:
1303         cap_slin = ast_format_cap_destroy(cap_slin);
1304         if (cnf)
1305                 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1306
1307         AST_LIST_UNLOCK(&confs);
1308
1309         return cnf;
1310 }
1311
1312 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1313 {
1314         static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1315
1316         int len = strlen(word);
1317         int which = 0;
1318         struct ast_conference *cnf = NULL;
1319         struct ast_conf_user *usr = NULL;
1320         char *confno = NULL;
1321         char usrno[50] = "";
1322         char *myline, *ret = NULL;
1323         
1324         if (pos == 1) {         /* Command */
1325                 return ast_cli_complete(word, cmds, state);
1326         } else if (pos == 2) {  /* Conference Number */
1327                 AST_LIST_LOCK(&confs);
1328                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1329                         if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1330                                 ret = cnf->confno;
1331                                 break;
1332                         }
1333                 }
1334                 ret = ast_strdup(ret); /* dup before releasing the lock */
1335                 AST_LIST_UNLOCK(&confs);
1336                 return ret;
1337         } else if (pos == 3) {
1338                 /* User Number || Conf Command option*/
1339                 if (strstr(line, "mute") || strstr(line, "kick")) {
1340                         if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1341                                 return ast_strdup("all");
1342                         which++;
1343                         AST_LIST_LOCK(&confs);
1344
1345                         /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1346                         myline = ast_strdupa(line);
1347                         if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1348                                 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1349                                         ;
1350                         }
1351                         
1352                         AST_LIST_TRAVERSE(&confs, cnf, list) {
1353                                 if (!strcmp(confno, cnf->confno))
1354                                     break;
1355                         }
1356
1357                         if (cnf) {
1358                                 struct ao2_iterator user_iter;
1359                                 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1360
1361                                 while((usr = ao2_iterator_next(&user_iter))) {
1362                                         snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1363                                         if (!strncasecmp(word, usrno, len) && ++which > state) {
1364                                                 ao2_ref(usr, -1);
1365                                                 break;
1366                                         }
1367                                         ao2_ref(usr, -1);
1368                                 }
1369                                 ao2_iterator_destroy(&user_iter);
1370                                 AST_LIST_UNLOCK(&confs);
1371                                 return usr ? ast_strdup(usrno) : NULL;
1372                         }
1373                 }
1374         }
1375
1376         return NULL;
1377 }
1378
1379 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1380 {
1381         /* Process the command */
1382         struct ast_conf_user *user;
1383         struct ast_conference *cnf;
1384         int hr, min, sec;
1385         int i = 0, total = 0;
1386         time_t now;
1387         struct ast_str *cmdline = NULL;
1388 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
1389 #define MC_DATA_FORMAT "%-12.12s   %4.4d              %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"
1390
1391         switch (cmd) {
1392         case CLI_INIT:
1393                 e->command = "meetme list [concise]";
1394                 e->usage =
1395                         "Usage: meetme list [concise] <confno> \n"
1396                         "       List all or a specific conference.\n";
1397                 return NULL;
1398         case CLI_GENERATE:
1399                 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1400         }
1401
1402         /* Check for length so no buffer will overflow... */
1403         for (i = 0; i < a->argc; i++) {
1404                 if (strlen(a->argv[i]) > 100)
1405                         ast_cli(a->fd, "Invalid Arguments.\n");
1406         }
1407
1408         /* Max confno length */
1409         if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1410                 return CLI_FAILURE;
1411         }
1412
1413         if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1414                 /* List all the conferences */  
1415                 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1416                 now = time(NULL);
1417                 AST_LIST_LOCK(&confs);
1418                 if (AST_LIST_EMPTY(&confs)) {
1419                         if (!concise) {
1420                                 ast_cli(a->fd, "No active MeetMe conferences.\n");
1421                         }
1422                         AST_LIST_UNLOCK(&confs);
1423                         ast_free(cmdline);
1424                         return CLI_SUCCESS;
1425                 }
1426                 if (!concise) {
1427                         ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1428                 }
1429                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1430                         if (cnf->markedusers == 0) {
1431                                 ast_str_set(&cmdline, 0, "N/A ");
1432                         } else {
1433                                 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1434                         }
1435                         hr = (now - cnf->start) / 3600;
1436                         min = ((now - cnf->start) % 3600) / 60;
1437                         sec = (now - cnf->start) % 60;
1438                         if (!concise) {
1439                                 ast_cli(a->fd, MC_DATA_FORMAT, cnf->confno, cnf->users, ast_str_buffer(cmdline), hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static", cnf->locked ? "Yes" : "No");
1440                         } else {
1441                                 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1442                                         cnf->confno,
1443                                         cnf->users,
1444                                         cnf->markedusers,
1445                                         hr, min, sec,
1446                                         cnf->isdynamic,
1447                                         cnf->locked);
1448                         }
1449
1450                         total += cnf->users;
1451                 }
1452                 AST_LIST_UNLOCK(&confs);
1453                 if (!concise) {
1454                         ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1455                 }
1456                 ast_free(cmdline);
1457                 return CLI_SUCCESS;
1458         } else if (strcmp(a->argv[1], "list") == 0) {
1459                 struct ao2_iterator user_iter;
1460                 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1461                 /* List all the users in a conference */
1462                 if (AST_LIST_EMPTY(&confs)) {
1463                         if (!concise) {
1464                                 ast_cli(a->fd, "No active MeetMe conferences.\n");
1465                         }
1466                         ast_free(cmdline);
1467                         return CLI_SUCCESS;     
1468                 }
1469                 /* Find the right conference */
1470                 AST_LIST_LOCK(&confs);
1471                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1472                         if (strcmp(cnf->confno, a->argv[2]) == 0) {
1473                                 break;
1474                         }
1475                 }
1476                 if (!cnf) {
1477                         if (!concise)
1478                                 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1479                         AST_LIST_UNLOCK(&confs);
1480                         ast_free(cmdline);
1481                         return CLI_SUCCESS;
1482                 }
1483                 /* Show all the users */
1484                 time(&now);
1485                 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1486                 while((user = ao2_iterator_next(&user_iter))) {
1487                         hr = (now - user->jointime) / 3600;
1488                         min = ((now - user->jointime) % 3600) / 60;
1489                         sec = (now - user->jointime) % 60;
1490                         if (!concise) {
1491                                 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1492                                         user->user_no,
1493                                         S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
1494                                         S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
1495                                         user->chan->name,
1496                                         ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1497                                         ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1498                                         user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1499                                         user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1500                                         istalking(user->talking), hr, min, sec); 
1501                         } else {
1502                                 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1503                                         user->user_no,
1504                                         S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, ""),
1505                                         S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, ""),
1506                                         user->chan->name,
1507                                         ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1508                                         ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1509                                         user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1510                                         user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1511                                         user->talking, hr, min, sec);
1512                         }
1513                         ao2_ref(user, -1);
1514                 }
1515                 ao2_iterator_destroy(&user_iter);
1516                 if (!concise) {
1517                         ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1518                 }
1519                 AST_LIST_UNLOCK(&confs);
1520                 ast_free(cmdline);
1521                 return CLI_SUCCESS;
1522         }
1523         if (a->argc < 2) {
1524                 ast_free(cmdline);
1525                 return CLI_SHOWUSAGE;
1526         }
1527
1528         ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1529
1530         admin_exec(NULL, ast_str_buffer(cmdline));
1531         ast_free(cmdline);
1532
1533         return CLI_SUCCESS;
1534 }
1535
1536
1537 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1538 {
1539         /* Process the command */
1540         struct ast_str *cmdline = NULL;
1541         int i = 0;
1542
1543         switch (cmd) {
1544         case CLI_INIT:
1545                 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1546                 e->usage =
1547                         "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1548                         "       Executes a command for the conference or on a conferee\n";
1549                 return NULL;
1550         case CLI_GENERATE:
1551                 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1552         }
1553
1554         if (a->argc > 8)
1555                 ast_cli(a->fd, "Invalid Arguments.\n");
1556         /* Check for length so no buffer will overflow... */
1557         for (i = 0; i < a->argc; i++) {
1558                 if (strlen(a->argv[i]) > 100)
1559                         ast_cli(a->fd, "Invalid Arguments.\n");
1560         }
1561
1562         /* Max confno length */
1563         if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1564                 return CLI_FAILURE;
1565         }
1566
1567         if (a->argc < 1) {
1568                 ast_free(cmdline);
1569                 return CLI_SHOWUSAGE;
1570         }
1571
1572         ast_str_set(&cmdline, 0, "%s", a->argv[2]);     /* Argv 2: conference number */
1573         if (strstr(a->argv[1], "lock")) {
1574                 if (strcmp(a->argv[1], "lock") == 0) {
1575                         /* Lock */
1576                         ast_str_append(&cmdline, 0, ",L");
1577                 } else {
1578                         /* Unlock */
1579                         ast_str_append(&cmdline, 0, ",l");
1580                 }
1581         } else if (strstr(a->argv[1], "mute")) { 
1582                 if (a->argc < 4) {
1583                         ast_free(cmdline);
1584                         return CLI_SHOWUSAGE;
1585                 }
1586                 if (strcmp(a->argv[1], "mute") == 0) {
1587                         /* Mute */
1588                         if (strcmp(a->argv[3], "all") == 0) {
1589                                 ast_str_append(&cmdline, 0, ",N");
1590                         } else {
1591                                 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);       
1592                         }
1593                 } else {
1594                         /* Unmute */
1595                         if (strcmp(a->argv[3], "all") == 0) {
1596                                 ast_str_append(&cmdline, 0, ",n");
1597                         } else {
1598                                 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1599                         }
1600                 }
1601         } else if (strcmp(a->argv[1], "kick") == 0) {
1602                 if (a->argc < 4) {
1603                         ast_free(cmdline);
1604                         return CLI_SHOWUSAGE;
1605                 }
1606                 if (strcmp(a->argv[3], "all") == 0) {
1607                         /* Kick all */
1608                         ast_str_append(&cmdline, 0, ",K");
1609                 } else {
1610                         /* Kick a single user */
1611                         ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1612                 }
1613         } else {
1614                 ast_free(cmdline);
1615                 return CLI_SHOWUSAGE;
1616         }
1617
1618         ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1619
1620         admin_exec(NULL, ast_str_buffer(cmdline));
1621         ast_free(cmdline);
1622
1623         return CLI_SUCCESS;
1624 }
1625
1626 static const char *sla_hold_str(unsigned int hold_access)
1627 {
1628         const char *hold = "Unknown";
1629
1630         switch (hold_access) {
1631         case SLA_HOLD_OPEN:
1632                 hold = "Open";
1633                 break;
1634         case SLA_HOLD_PRIVATE:
1635                 hold = "Private";
1636         default:
1637                 break;
1638         }
1639
1640         return hold;
1641 }
1642
1643 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1644 {
1645         const struct sla_trunk *trunk;
1646
1647         switch (cmd) {
1648         case CLI_INIT:
1649                 e->command = "sla show trunks";
1650                 e->usage =
1651                         "Usage: sla show trunks\n"
1652                         "       This will list all trunks defined in sla.conf\n";
1653                 return NULL;
1654         case CLI_GENERATE:
1655                 return NULL;
1656         }
1657
1658         ast_cli(a->fd, "\n"
1659                     "=============================================================\n"
1660                     "=== Configured SLA Trunks ===================================\n"
1661                     "=============================================================\n"
1662                     "===\n");
1663         AST_RWLIST_RDLOCK(&sla_trunks);
1664         AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1665                 struct sla_station_ref *station_ref;
1666                 char ring_timeout[16] = "(none)";
1667                 if (trunk->ring_timeout)
1668                         snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1669                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1670                             "=== Trunk Name:       %s\n"
1671                             "=== ==> Device:       %s\n"
1672                             "=== ==> AutoContext:  %s\n"
1673                             "=== ==> RingTimeout:  %s\n"
1674                             "=== ==> BargeAllowed: %s\n"
1675                             "=== ==> HoldAccess:   %s\n"
1676                             "=== ==> Stations ...\n",
1677                             trunk->name, trunk->device, 
1678                             S_OR(trunk->autocontext, "(none)"), 
1679                             ring_timeout,
1680                             trunk->barge_disabled ? "No" : "Yes",
1681                             sla_hold_str(trunk->hold_access));
1682                 AST_RWLIST_RDLOCK(&sla_stations);
1683                 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1684                         ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
1685                 AST_RWLIST_UNLOCK(&sla_stations);
1686                 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1687         }
1688         AST_RWLIST_UNLOCK(&sla_trunks);
1689         ast_cli(a->fd, "=============================================================\n\n");
1690
1691         return CLI_SUCCESS;
1692 }
1693
1694 static const char *trunkstate2str(enum sla_trunk_state state)
1695 {
1696 #define S(e) case e: return # e;
1697         switch (state) {
1698         S(SLA_TRUNK_STATE_IDLE)
1699         S(SLA_TRUNK_STATE_RINGING)
1700         S(SLA_TRUNK_STATE_UP)
1701         S(SLA_TRUNK_STATE_ONHOLD)
1702         S(SLA_TRUNK_STATE_ONHOLD_BYME)
1703         }
1704         return "Uknown State";
1705 #undef S
1706 }
1707
1708 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1709 {
1710         const struct sla_station *station;
1711
1712         switch (cmd) {
1713         case CLI_INIT:
1714                 e->command = "sla show stations";
1715                 e->usage =
1716                         "Usage: sla show stations\n"
1717                         "       This will list all stations defined in sla.conf\n";
1718                 return NULL;
1719         case CLI_GENERATE:
1720                 return NULL;
1721         }
1722
1723         ast_cli(a->fd, "\n" 
1724                     "=============================================================\n"
1725                     "=== Configured SLA Stations =================================\n"
1726                     "=============================================================\n"
1727                     "===\n");
1728         AST_RWLIST_RDLOCK(&sla_stations);
1729         AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1730                 struct sla_trunk_ref *trunk_ref;
1731                 char ring_timeout[16] = "(none)";
1732                 char ring_delay[16] = "(none)";
1733                 if (station->ring_timeout) {
1734                         snprintf(ring_timeout, sizeof(ring_timeout), 
1735                                 "%u", station->ring_timeout);
1736                 }
1737                 if (station->ring_delay) {
1738                         snprintf(ring_delay, sizeof(ring_delay), 
1739                                 "%u", station->ring_delay);
1740                 }
1741                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1742                             "=== Station Name:    %s\n"
1743                             "=== ==> Device:      %s\n"
1744                             "=== ==> AutoContext: %s\n"
1745                             "=== ==> RingTimeout: %s\n"
1746                             "=== ==> RingDelay:   %s\n"
1747                             "=== ==> HoldAccess:  %s\n"
1748                             "=== ==> Trunks ...\n",
1749                             station->name, station->device,
1750                             S_OR(station->autocontext, "(none)"), 
1751                             ring_timeout, ring_delay,
1752                             sla_hold_str(station->hold_access));
1753                 AST_RWLIST_RDLOCK(&sla_trunks);
1754                 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1755                         if (trunk_ref->ring_timeout) {
1756                                 snprintf(ring_timeout, sizeof(ring_timeout),
1757                                         "%u", trunk_ref->ring_timeout);
1758                         } else
1759                                 strcpy(ring_timeout, "(none)");
1760                         if (trunk_ref->ring_delay) {
1761                                 snprintf(ring_delay, sizeof(ring_delay),
1762                                         "%u", trunk_ref->ring_delay);
1763                         } else
1764                                 strcpy(ring_delay, "(none)");
1765                                 ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
1766                                     "===       ==> State:       %s\n"
1767                                     "===       ==> RingTimeout: %s\n"
1768                                     "===       ==> RingDelay:   %s\n",
1769                                     trunk_ref->trunk->name,
1770                                     trunkstate2str(trunk_ref->state),
1771                                     ring_timeout, ring_delay);
1772                 }
1773                 AST_RWLIST_UNLOCK(&sla_trunks);
1774                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1775                             "===\n");
1776         }
1777         AST_RWLIST_UNLOCK(&sla_stations);
1778         ast_cli(a->fd, "============================================================\n"
1779                     "\n");
1780
1781         return CLI_SUCCESS;
1782 }
1783
1784 static struct ast_cli_entry cli_meetme[] = {
1785         AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1786         AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1787         AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1788         AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1789 };
1790
1791 static void conf_flush(int fd, struct ast_channel *chan)
1792 {
1793         int x;
1794
1795         /* read any frames that may be waiting on the channel
1796            and throw them away
1797         */
1798         if (chan) {
1799                 struct ast_frame *f;
1800
1801                 /* when no frames are available, this will wait
1802                    for 1 millisecond maximum
1803                 */
1804                 while (ast_waitfor(chan, 1)) {
1805                         f = ast_read(chan);
1806                         if (f)
1807                                 ast_frfree(f);
1808                         else /* channel was hung up or something else happened */
1809                                 break;
1810                 }
1811         }
1812
1813         /* flush any data sitting in the pseudo channel */
1814         x = DAHDI_FLUSH_ALL;
1815         if (ioctl(fd, DAHDI_FLUSH, &x))
1816                 ast_log(LOG_WARNING, "Error flushing channel\n");
1817
1818 }
1819
1820 /*! \brief Remove the conference from the list and free it.
1821
1822    We assume that this was called while holding conflock. */
1823 static int conf_free(struct ast_conference *conf)
1824 {
1825         int x;
1826         struct announce_listitem *item;
1827         
1828         AST_LIST_REMOVE(&confs, conf, list);
1829         manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1830
1831         if (conf->recording == MEETME_RECORD_ACTIVE) {
1832                 conf->recording = MEETME_RECORD_TERMINATE;
1833                 AST_LIST_UNLOCK(&confs);
1834                 while (1) {
1835                         usleep(1);
1836                         AST_LIST_LOCK(&confs);
1837                         if (conf->recording == MEETME_RECORD_OFF)
1838                                 break;
1839                         AST_LIST_UNLOCK(&confs);
1840                 }
1841         }
1842
1843         for (x = 0; x < AST_FRAME_BITS; x++) {
1844                 if (conf->transframe[x])
1845                         ast_frfree(conf->transframe[x]);
1846                 if (conf->transpath[x])
1847                         ast_translator_free_path(conf->transpath[x]);
1848         }
1849         if (conf->announcethread != AST_PTHREADT_NULL) {
1850                 ast_mutex_lock(&conf->announcelistlock);
1851                 conf->announcethread_stop = 1;
1852                 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1853                 ast_cond_signal(&conf->announcelist_addition);
1854                 ast_mutex_unlock(&conf->announcelistlock);
1855                 pthread_join(conf->announcethread, NULL);
1856         
1857                 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1858                         /* If it's a voicemail greeting file we don't want to remove it */
1859                         if (!item->vmrec){
1860                                 ast_filedelete(item->namerecloc, NULL);
1861                         }
1862                         ao2_ref(item, -1);
1863                 }
1864                 ast_mutex_destroy(&conf->announcelistlock);
1865         }
1866
1867         if (conf->origframe)
1868                 ast_frfree(conf->origframe);
1869         if (conf->lchan)
1870                 ast_hangup(conf->lchan);
1871         if (conf->chan)
1872                 ast_hangup(conf->chan);
1873         if (conf->fd >= 0)
1874                 close(conf->fd);
1875         if (conf->recordingfilename) {
1876                 ast_free(conf->recordingfilename);
1877         }
1878         if (conf->usercontainer) {
1879                 ao2_ref(conf->usercontainer, -1);
1880         }
1881         if (conf->recordingformat) {
1882                 ast_free(conf->recordingformat);
1883         }
1884         ast_mutex_destroy(&conf->playlock);
1885         ast_mutex_destroy(&conf->listenlock);
1886         ast_mutex_destroy(&conf->recordthreadlock);
1887         ast_mutex_destroy(&conf->announcethreadlock);
1888         ast_free(conf);
1889
1890         return 0;
1891 }
1892
1893 static void conf_queue_dtmf(const struct ast_conference *conf,
1894         const struct ast_conf_user *sender, struct ast_frame *f)
1895 {
1896         struct ast_conf_user *user;
1897         struct ao2_iterator user_iter;
1898
1899         user_iter = ao2_iterator_init(conf->usercontainer, 0);
1900         while ((user = ao2_iterator_next(&user_iter))) {
1901                 if (user == sender) {
1902                         ao2_ref(user, -1);
1903                         continue;
1904                 }
1905                 if (ast_write(user->chan, f) < 0)
1906                         ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1907                 ao2_ref(user, -1);
1908         }
1909         ao2_iterator_destroy(&user_iter);
1910 }
1911
1912 static void sla_queue_event_full(enum sla_event_type type, 
1913         struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1914 {
1915         struct sla_event *event;
1916
1917         if (sla.thread == AST_PTHREADT_NULL) {
1918                 return;
1919         }
1920
1921         if (!(event = ast_calloc(1, sizeof(*event))))
1922                 return;
1923
1924         event->type = type;
1925         event->trunk_ref = trunk_ref;
1926         event->station = station;
1927
1928         if (!lock) {
1929                 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1930                 return;
1931         }
1932
1933         ast_mutex_lock(&sla.lock);
1934         AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1935         ast_cond_signal(&sla.cond);
1936         ast_mutex_unlock(&sla.lock);
1937 }
1938
1939 static void sla_queue_event_nolock(enum sla_event_type type)
1940 {
1941         sla_queue_event_full(type, NULL, NULL, 0);
1942 }
1943
1944 static void sla_queue_event(enum sla_event_type type)
1945 {
1946         sla_queue_event_full(type, NULL, NULL, 1);
1947 }
1948
1949 /*! \brief Queue a SLA event from the conference */
1950 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1951         struct ast_conference *conf)
1952 {
1953         struct sla_station *station;
1954         struct sla_trunk_ref *trunk_ref = NULL;
1955         char *trunk_name;
1956
1957         trunk_name = ast_strdupa(conf->confno);
1958         strsep(&trunk_name, "_");
1959         if (ast_strlen_zero(trunk_name)) {
1960                 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1961                 return;
1962         }
1963
1964         AST_RWLIST_RDLOCK(&sla_stations);
1965         AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1966                 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1967                         if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1968                                 break;
1969                 }
1970                 if (trunk_ref)
1971                         break;
1972         }
1973         AST_RWLIST_UNLOCK(&sla_stations);
1974
1975         if (!trunk_ref) {
1976                 ast_debug(1, "Trunk not found for event!\n");
1977                 return;
1978         }
1979
1980         sla_queue_event_full(type, trunk_ref, station, 1);
1981 }
1982
1983 /*! \brief Decrement reference counts, as incremented by find_conf() */
1984 static int dispose_conf(struct ast_conference *conf)
1985 {
1986         int res = 0;
1987         int confno_int = 0;
1988
1989         AST_LIST_LOCK(&confs);
1990         if (ast_atomic_dec_and_test(&conf->refcount)) {
1991                 /* Take the conference room number out of an inuse state */
1992                 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1993                         conf_map[confno_int] = 0;
1994                 }
1995                 conf_free(conf);
1996                 res = 1;
1997         }
1998         AST_LIST_UNLOCK(&confs);
1999
2000         return res;
2001 }
2002
2003 static int rt_extend_conf(const char *confno)
2004 {
2005         char currenttime[32];
2006         char endtime[32];
2007         struct timeval now;
2008         struct ast_tm tm;
2009         struct ast_variable *var, *orig_var;
2010         char bookid[51];
2011
2012         if (!extendby) {
2013                 return 0;
2014         }
2015
2016         now = ast_tvnow();
2017
2018         ast_localtime(&now, &tm, NULL);
2019         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2020
2021         var = ast_load_realtime("meetme", "confno",
2022                 confno, "startTime<= ", currenttime,
2023                 "endtime>= ", currenttime, NULL);
2024
2025         orig_var = var;
2026
2027         /* Identify the specific RealTime conference */
2028         while (var) {
2029                 if (!strcasecmp(var->name, "bookid")) {
2030                         ast_copy_string(bookid, var->value, sizeof(bookid));
2031                 }
2032                 if (!strcasecmp(var->name, "endtime")) {
2033                         ast_copy_string(endtime, var->value, sizeof(endtime));
2034                 }
2035
2036                 var = var->next;
2037         }
2038         ast_variables_destroy(orig_var);
2039
2040         ast_strptime(endtime, DATE_FORMAT, &tm);
2041         now = ast_mktime(&tm, NULL);
2042
2043         now.tv_sec += extendby;
2044
2045         ast_localtime(&now, &tm, NULL);
2046         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2047         strcat(currenttime, "0"); /* Seconds needs to be 00 */
2048
2049         var = ast_load_realtime("meetme", "confno",
2050                 confno, "startTime<= ", currenttime,
2051                 "endtime>= ", currenttime, NULL);
2052
2053         /* If there is no conflict with extending the conference, update the DB */
2054         if (!var) {
2055                 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2056                 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2057                 return 0;
2058
2059         }
2060
2061         ast_variables_destroy(var);
2062         return -1;
2063 }
2064
2065 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2066 {
2067         char *original_moh;
2068
2069         ast_channel_lock(chan);
2070         original_moh = ast_strdupa(chan->musicclass);
2071         ast_string_field_set(chan, musicclass, musicclass);
2072         ast_channel_unlock(chan);
2073
2074         ast_moh_start(chan, original_moh, NULL);
2075
2076         ast_channel_lock(chan);
2077         ast_string_field_set(chan, musicclass, original_moh);
2078         ast_channel_unlock(chan);
2079 }
2080
2081 static const char *get_announce_filename(enum announcetypes type)
2082 {
2083         switch (type) {
2084         case CONF_HASLEFT:
2085                 return "conf-hasleft";
2086                 break;
2087         case CONF_HASJOIN:
2088                 return "conf-hasjoin";
2089                 break;
2090         default:
2091                 return "";
2092         }
2093 }
2094
2095 static void *announce_thread(void *data)
2096 {
2097         struct announce_listitem *current;
2098         struct ast_conference *conf = data;
2099         int res;
2100         char filename[PATH_MAX] = "";
2101         AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2102         AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2103
2104         while (!conf->announcethread_stop) {
2105                 ast_mutex_lock(&conf->announcelistlock);
2106                 if (conf->announcethread_stop) {
2107                         ast_mutex_unlock(&conf->announcelistlock);
2108                         break;
2109                 }
2110                 if (AST_LIST_EMPTY(&conf->announcelist))
2111                         ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2112
2113                 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2114                 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2115
2116                 ast_mutex_unlock(&conf->announcelistlock);
2117                 if (conf->announcethread_stop) {
2118                         break;
2119                 }
2120
2121                 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2122                         ast_debug(1, "About to play %s\n", current->namerecloc);
2123                         if (!ast_fileexists(current->namerecloc, NULL, NULL))
2124                                 continue;
2125                         if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2126                                 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2127                                         res = ast_waitstream(current->confchan, "");
2128                                 if (!res) {
2129                                         ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2130                                         if (!ast_streamfile(current->confchan, filename, current->language))
2131                                                 ast_waitstream(current->confchan, "");
2132                                 }
2133                         }
2134                         if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2135                                 /* only remove it if it isn't a VM recording file */
2136                                 ast_filedelete(current->namerecloc, NULL);
2137                         }
2138                 }
2139         }
2140
2141         /* thread marked to stop, clean up */
2142         while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2143                 /* only delete if it's a vm rec */
2144                 if (!current->vmrec) {
2145                         ast_filedelete(current->namerecloc, NULL);
2146                 }
2147                 ao2_ref(current, -1);
2148         }
2149         return NULL;
2150 }
2151
2152 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2153 {
2154         if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2155                 return 1;
2156         }
2157
2158         return (chan->_state == AST_STATE_UP);
2159 }
2160
2161 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2162 {
2163         ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2164               "Channel: %s\r\n"
2165               "Uniqueid: %s\r\n"
2166               "Meetme: %s\r\n"
2167               "Usernum: %d\r\n"
2168               "Status: %s\r\n",
2169               chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
2170 }
2171
2172 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2173 {
2174         int last_talking = user->talking;
2175         if (last_talking == talking)
2176                 return;
2177
2178         user->talking = talking;
2179
2180         if (monitor) {
2181                 /* Check if talking state changed. Take care of -1 which means unmonitored */
2182                 int was_talking = (last_talking > 0);
2183                 int now_talking = (talking > 0);
2184                 if (was_talking != now_talking) {
2185                         send_talking_event(chan, conf, user, now_talking);
2186                 }
2187         }
2188 }
2189
2190 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2191 {
2192         struct ast_conf_user *user = obj;
2193         /* actual pointer contents of check_admin_arg is irrelevant */
2194
2195         if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2196                 user->adminflags |= ADMINFLAG_KICKME;
2197         }
2198         return 0;
2199 }
2200
2201 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2202 {
2203         struct ast_conf_user *user = obj;
2204         /* actual pointer contents of check_admin_arg is irrelevant */
2205
2206         if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2207                 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
2208         }
2209         return 0;
2210 }
2211
2212 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2213 {
2214         struct ast_conf_user *user = obj;
2215         /* actual pointer contents of check_admin_arg is irrelevant */
2216
2217         if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2218                 user->adminflags |= ADMINFLAG_MUTED;
2219         }
2220         return 0;
2221 }
2222
2223 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2224 {
2225         struct ast_conf_user *user = NULL;
2226         int fd;
2227         struct dahdi_confinfo dahdic, dahdic_empty;
2228         struct ast_frame *f;
2229         struct ast_channel *c;
2230         struct ast_frame fr;
2231         int outfd;
2232         int ms;
2233         int nfds;
2234         int res;
2235         int retrydahdi;
2236         int origfd;
2237         int musiconhold = 0, mohtempstopped = 0;
2238         int firstpass = 0;
2239         int lastmarked = 0;
2240         int currentmarked = 0;
2241         int ret = -1;
2242         int x;
2243         int menu_active = 0;
2244         int menu8_active = 0;
2245         int talkreq_manager = 0;
2246         int using_pseudo = 0;
2247         int duration = 20;
2248         int hr, min, sec;
2249         int sent_event = 0;
2250         int checked = 0;
2251         int announcement_played = 0;
2252         struct timeval now;
2253         struct ast_dsp *dsp = NULL;
2254         struct ast_app *agi_app;
2255         char *agifile, *mod_speex;
2256         const char *agifiledefault = "conf-background.agi", *tmpvar;
2257         char meetmesecs[30] = "";
2258         char exitcontext[AST_MAX_CONTEXT] = "";
2259         char recordingtmp[AST_MAX_EXTENSION] = "";
2260         char members[10] = "";
2261         int dtmf, opt_waitmarked_timeout = 0;
2262         time_t timeout = 0;
2263         struct dahdi_bufferinfo bi;
2264         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
2265         char *buf = __buf + AST_FRIENDLY_OFFSET;
2266         char *exitkeys = NULL;
2267         unsigned int calldurationlimit = 0;
2268         long timelimit = 0;
2269         long play_warning = 0;
2270         long warning_freq = 0;
2271         const char *warning_sound = NULL;
2272         const char *end_sound = NULL;
2273         char *parse;
2274         long time_left_ms = 0;
2275         struct timeval nexteventts = { 0, };
2276         int to;
2277         int setusercount = 0;
2278         int confsilence = 0, totalsilence = 0;
2279         char *mailbox, *context;
2280         struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
2281         struct ast_format tmpfmt;
2282
2283         if (!cap_slin) {
2284                 goto conf_run_cleanup;
2285         }
2286         ast_format_cap_add(cap_slin, ast_format_set(&tmpfmt, AST_FORMAT_SLINEAR, 0));
2287
2288         if (!(user = ao2_alloc(sizeof(*user), NULL))) {
2289                 goto conf_run_cleanup;
2290         }
2291
2292         /* Possible timeout waiting for marked user */
2293         if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) &&
2294                 !ast_strlen_zero(optargs[OPT_ARG_WAITMARKED]) &&
2295                 (sscanf(optargs[OPT_ARG_WAITMARKED], "%30d", &opt_waitmarked_timeout) == 1) &&
2296                 (opt_waitmarked_timeout > 0)) {
2297                 timeout = time(NULL) + opt_waitmarked_timeout;
2298         }
2299
2300         if (ast_test_flag64(confflags, CONFFLAG_DURATION_STOP) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_STOP])) {
2301                 calldurationlimit = atoi(optargs[OPT_ARG_DURATION_STOP]);
2302                 ast_verb(3, "Setting call duration limit to %d seconds.\n", calldurationlimit);
2303         }
2304
2305         if (ast_test_flag64(confflags, CONFFLAG_DURATION_LIMIT) && !ast_strlen_zero(optargs[OPT_ARG_DURATION_LIMIT])) {
2306                 char *limit_str, *warning_str, *warnfreq_str;
2307                 const char *var;
2308
2309                 parse = optargs[OPT_ARG_DURATION_LIMIT];
2310                 limit_str = strsep(&parse, ":");
2311                 warning_str = strsep(&parse, ":");
2312                 warnfreq_str = parse;
2313
2314                 timelimit = atol(limit_str);
2315                 if (warning_str)
2316                         play_warning = atol(warning_str);
2317                 if (warnfreq_str)
2318                         warning_freq = atol(warnfreq_str);
2319
2320                 if (!timelimit) {
2321                         timelimit = play_warning = warning_freq = 0;
2322                         warning_sound = NULL;
2323                 } else if (play_warning > timelimit) {
2324                         if (!warning_freq) {
2325                                 play_warning = 0;
2326                         } else {
2327                                 while (play_warning > timelimit)
2328                                         play_warning -= warning_freq;
2329                                 if (play_warning < 1)
2330                                         play_warning = warning_freq = 0;
2331                         }
2332                 }
2333
2334                 ast_channel_lock(chan);
2335                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2336                         var = ast_strdupa(var);
2337                 }
2338                 ast_channel_unlock(chan);
2339
2340                 warning_sound = var ? var : "timeleft";
2341
2342                 ast_channel_lock(chan);
2343                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2344                         var = ast_strdupa(var);
2345                 }
2346                 ast_channel_unlock(chan);
2347
2348                 end_sound = var ? var : NULL;
2349
2350                 /* undo effect of S(x) in case they are both used */
2351                 calldurationlimit = 0;
2352                 /* more efficient do it like S(x) does since no advanced opts */
2353                 if (!play_warning && !end_sound && timelimit) {
2354                         calldurationlimit = timelimit / 1000;
2355                         timelimit = play_warning = warning_freq = 0;
2356                 } else {
2357                         ast_debug(2, "Limit Data for this call:\n");
2358                         ast_debug(2, "- timelimit     = %ld\n", timelimit);
2359                         ast_debug(2, "- play_warning  = %ld\n", play_warning);
2360                         ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
2361                         ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2362                         ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
2363                 }
2364         }
2365
2366         /* Get exit keys */
2367         if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2368                 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2369                         exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2370                 else
2371                         exitkeys = ast_strdupa("#"); /* Default */
2372         }
2373         
2374         if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2375                 if (!conf->recordingfilename) {
2376                         const char *var;
2377                         ast_channel_lock(chan);
2378                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2379                                 conf->recordingfilename = ast_strdup(var);
2380                         }
2381                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2382                                 conf->recordingformat = ast_strdup(var);
2383                         }
2384                         ast_channel_unlock(chan);
2385                         if (!conf->recordingfilename) {
2386                                 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2387                                 conf->recordingfilename = ast_strdup(recordingtmp);
2388                         }
2389                         if (!conf->recordingformat) {
2390                                 conf->recordingformat = ast_strdup("wav");
2391                         }
2392                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2393                                     conf->confno, conf->recordingfilename, conf->recordingformat);
2394                 }
2395         }
2396
2397         ast_mutex_lock(&conf->recordthreadlock);
2398         if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2399                 ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
2400                 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2401                 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2402                 dahdic.chan = 0;
2403                 dahdic.confno = conf->dahdiconf;
2404                 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2405                 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2406                         ast_log(LOG_WARNING, "Error starting listen channel\n");
2407                         ast_hangup(conf->lchan);
2408                         conf->lchan = NULL;
2409                 } else {
2410                         ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2411                 }
2412         }
2413         ast_mutex_unlock(&conf->recordthreadlock);
2414
2415         ast_mutex_lock(&conf->announcethreadlock);
2416         if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2417                 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
2418                 ast_mutex_init(&conf->announcelistlock);
2419                 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2420                 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2421         }
2422         ast_mutex_unlock(&conf->announcethreadlock);
2423
2424         time(&user->jointime);
2425         
2426         user->timelimit = timelimit;
2427         user->play_warning = play_warning;
2428         user->warning_freq = warning_freq;
2429         user->warning_sound = warning_sound;
2430         user->end_sound = end_sound;    
2431         
2432         if (calldurationlimit > 0) {
2433                 time(&user->kicktime);
2434                 user->kicktime = user->kicktime + calldurationlimit;
2435         }
2436         
2437         if (ast_tvzero(user->start_time))
2438                 user->start_time = ast_tvnow();
2439         time_left_ms = user->timelimit;
2440         
2441         if (user->timelimit) {
2442                 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2443                 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2444         }
2445
2446         if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2447                 /* Sorry, but this conference is locked! */     
2448                 if (!ast_streamfile(chan, "conf-locked", chan->language))
2449                         ast_waitstream(chan, "");
2450                 goto outrun;
2451         }
2452
2453         ast_mutex_lock(&conf->playlock);
2454
2455         if (rt_schedule && conf->maxusers) {
2456                 if (conf->users >= conf->maxusers) {
2457                         /* Sorry, but this confernce has reached the participant limit! */      
2458                         if (!ast_streamfile(chan, "conf-full", chan->language))
2459                                 ast_waitstream(chan, "");
2460                         ast_mutex_unlock(&conf->playlock);
2461                         goto outrun;
2462                 }
2463         }
2464
2465         ao2_lock(conf->usercontainer);
2466         ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
2467         user->user_no++;
2468         ao2_link(conf->usercontainer, user);
2469         ao2_unlock(conf->usercontainer);
2470
2471         user->chan = chan;
2472         user->userflags = *confflags;
2473         user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2474         user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2475         user->talking = -1;
2476
2477         ast_mutex_unlock(&conf->playlock);
2478
2479         if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
2480                 char destdir[PATH_MAX];
2481
2482                 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2483
2484                 if (ast_mkdir(destdir, 0777) != 0) {
2485                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2486                         goto outrun;
2487                 }
2488
2489                 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2490                         context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
2491                         mailbox = strsep(&context, "@");
2492
2493                         if (ast_strlen_zero(mailbox)) {
2494                                 /* invalid input, clear the v flag*/
2495                                 ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
2496                                 ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
2497                         } else {
2498                                 if (ast_strlen_zero(context)) {
2499                                     context = "default";
2500                                 }
2501                                 /* if there is no mailbox we don't need to do this logic  */
2502                                 snprintf(user->namerecloc, sizeof(user->namerecloc),
2503                                          "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
2504
2505                                 /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
2506                                 if (!ast_fileexists(user->namerecloc, NULL, NULL)){
2507                                         snprintf(user->namerecloc, sizeof(user->namerecloc),
2508                                                  "%s/meetme-username-%s-%d", destdir,
2509                                                  conf->confno, user->user_no);
2510                                         ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
2511                                 }
2512                         }
2513                 } else {
2514                         snprintf(user->namerecloc, sizeof(user->namerecloc),
2515                                  "%s/meetme-username-%s-%d", destdir,
2516                                  conf->confno, user->user_no);
2517                 }
2518
2519                 res = 0;
2520                 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
2521                         res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2522                 else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
2523                         res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2524                 if (res == -1)
2525                         goto outrun;
2526
2527         }
2528
2529         ast_mutex_lock(&conf->playlock);
2530
2531         if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2532                 conf->markedusers++;
2533         conf->users++;
2534         if (rt_log_members) {
2535                 /* Update table */
2536                 snprintf(members, sizeof(members), "%d", conf->users);
2537                 ast_realtime_require_field("meetme",
2538                         "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2539                         "members", RQ_UINTEGER1, strlen(members),
2540                         NULL);
2541                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2542         }
2543         setusercount = 1;
2544
2545         /* This device changed state now - if this is the first user */
2546         if (conf->users == 1)
2547                 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2548
2549         ast_mutex_unlock(&conf->playlock);
2550
2551         /* return the unique ID of the conference */
2552         pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2553
2554         if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2555                 ast_channel_lock(chan);
2556                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2557                         ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2558                 } else if (!ast_strlen_zero(chan->macrocontext)) {
2559                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2560                 } else {
2561                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2562                 }
2563                 ast_channel_unlock(chan);
2564         }
2565
2566         /* Play an arbitrary intro message */
2567         if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2568                         !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2569                 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
2570                         ast_waitstream(chan, "");
2571                 }
2572         }
2573
2574         if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2575                 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2576                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2577                                 ast_waitstream(chan, "");
2578                 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2579                         if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2580                                 ast_waitstream(chan, "");
2581         }
2582
2583         if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
2584                 conf->users > 1) {
2585                 int keepplaying = 1;
2586
2587                 if (conf->users == 2) { 
2588                         if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2589                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2590                                 ast_stopstream(chan);
2591                                 if (res > 0)
2592                                         keepplaying = 0;
2593                                 else if (res == -1)
2594                                         goto outrun;
2595                         }
2596                 } else { 
2597                         if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2598                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2599                                 ast_stopstream(chan);
2600                                 if (res > 0)
2601                                         keepplaying = 0;
2602                                 else if (res == -1)
2603                                         goto outrun;
2604                         }
2605                         if (keepplaying) {
2606                                 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2607                                 if (res > 0)
2608                                         keepplaying = 0;
2609                                 else if (res == -1)
2610                                         goto outrun;
2611                         }
2612                         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2613                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2614                                 ast_stopstream(chan);
2615                                 if (res > 0)
2616                                         keepplaying = 0;
2617                                 else if (res == -1) 
2618                                         goto outrun;
2619                         }
2620                 }
2621         }
2622
2623         if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2624                 /* We're leaving this alone until the state gets changed to up */
2625                 ast_indicate(chan, -1);
2626         }
2627
2628         if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2629                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2630                 goto outrun;
2631         }
2632
2633         if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2634                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2635                 goto outrun;
2636         }
2637
2638         /* Reduce background noise from each participant */
2639         if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2640                 ast_free(mod_speex);
2641                 ast_func_write(chan, "DENOISE(rx)", "on");
2642         }
2643
2644         retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2645         user->dahdichannel = !retrydahdi;
2646
2647  dahdiretry:
2648         origfd = chan->fds[0];
2649         if (retrydahdi) {
2650                 /* open pseudo in non-blocking mode */
2651                 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2652                 if (fd < 0) {
2653                         ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
2654                         goto outrun;
2655                 }
2656                 using_pseudo = 1;
2657                 /* Setup buffering information */
2658                 memset(&bi, 0, sizeof(bi));
2659                 bi.bufsize = CONF_SIZE / 2;
2660                 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2661                 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2662                 bi.numbufs = audio_buffers;
2663                 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2664                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2665                         close(fd);
2666                         goto outrun;
2667                 }
2668                 x = 1;
2669                 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2670                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2671                         close(fd);
2672                         goto outrun;
2673                 }
2674                 nfds = 1;
2675         } else {
2676                 /* XXX Make sure we're not running on a pseudo channel XXX */
2677                 fd = chan->fds[0];
2678                 nfds = 0;
2679         }
2680         memset(&dahdic, 0, sizeof(dahdic));
2681         memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2682         /* Check to see if we're in a conference... */
2683         dahdic.chan = 0;
2684         if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2685                 ast_log(LOG_WARNING, "Error getting conference\n");
2686                 close(fd);
2687                 goto outrun;
2688         }
2689         if (dahdic.confmode) {
2690                 /* Whoa, already in a conference...  Retry... */
2691                 if (!retrydahdi) {
2692                         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2693                         retrydahdi = 1;
2694                         goto dahdiretry;
2695                 }
2696         }
2697         memset(&dahdic, 0, sizeof(dahdic));
2698         /* Add us to the conference */
2699         dahdic.chan = 0;
2700         dahdic.confno = conf->dahdiconf;
2701
2702         if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2703                         ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
2704                 struct announce_listitem *item;
2705                 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2706                         goto outrun;
2707                 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2708                 ast_copy_string(item->language, chan->language, sizeof(item->language));
2709                 item->confchan = conf->chan;
2710                 item->confusers = conf->users;
2711                 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2712                         item->vmrec = 1;
2713                 }
2714                 item->announcetype = CONF_HASJOIN;
2715                 ast_mutex_lock(&conf->announcelistlock);
2716                 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2717                 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2718                 ast_cond_signal(&conf->announcelist_addition);
2719                 ast_mutex_unlock(&conf->announcelistlock);
2720
2721                 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2722                         ;
2723                 }
2724                 ao2_ref(item, -1);
2725         }
2726
2727         if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED && !conf->markedusers))
2728                 dahdic.confmode = DAHDI_CONF_CONF;
2729         else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2730                 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2731         else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2732                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2733         else
2734                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2735
2736         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2737                 ast_log(LOG_WARNING, "Error setting conference\n");
2738                 close(fd);
2739                 goto outrun;
2740         }
2741         ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2742
2743         if (!sent_event) {
2744                 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2745                         "Channel: %s\r\n"
2746                         "Uniqueid: %s\r\n"
2747                         "Meetme: %s\r\n"
2748                         "Usernum: %d\r\n"
2749                         "CallerIDnum: %s\r\n"
2750                         "CallerIDname: %s\r\n",
2751                         chan->name, chan->uniqueid, conf->confno,
2752                         user->user_no,
2753                         S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
2754                         S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>")
2755                         );
2756                 sent_event = 1;
2757         }
2758
2759         if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2760                 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2761                 firstpass = 1;
2762                 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2763                         if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2764                                 (conf->markedusers >= 1))) {
2765                                 conf_play(chan, conf, ENTER);
2766                         }
2767         }
2768
2769         conf_flush(fd, chan);
2770
2771         if (dsp)
2772                 ast_dsp_free(dsp);
2773
2774         if (!(dsp = ast_dsp_new())) {
2775                 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2776                 res = -1;
2777         }
2778
2779         if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2780                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2781                    or use default filename of conf-background.agi */
2782
2783                 ast_channel_lock(chan);
2784                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2785                         agifile = ast_strdupa(tmpvar);
2786                 } else {
2787                         agifile = ast_strdupa(agifiledefault);
2788                 }
2789                 ast_channel_unlock(chan);
2790                 
2791                 if (user->dahdichannel) {
2792                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2793                         x = 1;
2794                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2795                 }
2796                 /* Find a pointer to the agi app and execute the script */
2797                 agi_app = pbx_findapp("agi");
2798                 if (agi_app) {
2799                         ret = pbx_exec(chan, agi_app, agifile);
2800                 } else {
2801                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
2802                         ret = -2;
2803                 }
2804                 if (user->dahdichannel) {
2805                         /*  Remove CONFMUTE mode on DAHDI channel */
2806                         x = 0;
2807                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2808                 }
2809         } else {
2810                 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2811                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2812                         x = 1;
2813                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2814                 }       
2815                 for (;;) {
2816                         int menu_was_active = 0;
2817
2818                         outfd = -1;
2819                         ms = -1;
2820                         now = ast_tvnow();
2821
2822                         if (rt_schedule && conf->endtime) {
2823                                 char currenttime[32];
2824                                 long localendtime = 0;
2825                                 int extended = 0;
2826                                 struct ast_tm tm;
2827                                 struct ast_variable *var, *origvar;
2828                                 struct timeval tmp;
2829
2830                                 if (now.tv_sec % 60 == 0) {
2831                                         if (!checked) {
2832                                                 ast_localtime(&now, &tm, NULL);
2833                                                 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2834                                                 var = origvar = ast_load_realtime("meetme", "confno",
2835                                                         conf->confno, "starttime <=", currenttime,
2836                                                          "endtime >=", currenttime, NULL);
2837
2838                                                 for ( ; var; var = var->next) {
2839                                                         if (!strcasecmp(var->name, "endtime")) {
2840                                                                 struct ast_tm endtime_tm;
2841                                                                 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2842                                                                 tmp = ast_mktime(&endtime_tm, NULL);
2843                                                                 localendtime = tmp.tv_sec;
2844                                                         }
2845                                                 }
2846                                                 ast_variables_destroy(origvar);
2847
2848                                                 /* A conference can be extended from the
2849                                                    Admin/User menu or by an external source */
2850                                                 if (localendtime > conf->endtime){
2851                                                         conf->endtime = localendtime;
2852                                                         extended = 1;
2853                                                 }
2854
2855                                                 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2856                                                         ast_verbose("Quitting time...\n");
2857                                                         goto outrun;
2858                                                 }
2859
2860                                                 if (!announcement_played && conf->endalert) {
2861                                                         if (now.tv_sec + conf->endalert >= conf->endtime) {
2862                                                                 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2863                                                                         ast_waitstream(chan, "");
2864                                                                 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2865                                                                 if (!ast_streamfile(chan, "minutes", chan->language))
2866                                                                         ast_waitstream(chan, "");
2867                                                                 if (musiconhold) {
2868                                                                         conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2869                                                                 }
2870                                                                 announcement_played = 1;
2871                                                         }
2872                                                 }
2873
2874                                                 if (extended) {
2875                                                         announcement_played = 0;
2876                                                 }
2877
2878                                                 checked = 1;
2879                                         }
2880                                 } else {
2881                                         checked = 0;
2882                                 }
2883                         }
2884
2885                         if (user->kicktime && (user->kicktime <= now.tv_sec)) {
2886                                 if (ast_test_flag64(confflags, CONFFLAG_KICK_CONTINUE)) {
2887                                         ret = 0;