Merged revisions 317969 via svnmerge from
[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 /* These flags are defined separately because we ran out of bits that an enum can be used to represent. 
630    If you add new flags, be sure to do it in the same way that CONFFLAG_INTROMSG is. */
631 #define CONFFLAG_INTROMSG ((uint64_t)1 << 32)    /*!< If set play an intro announcement at start of conference */
632 #define CONFFLAG_INTROUSER_VMREC ((uint64_t)1 << 33)
633
634 enum {
635         OPT_ARG_WAITMARKED = 0,
636         OPT_ARG_EXITKEYS   = 1,
637         OPT_ARG_DURATION_STOP = 2,
638         OPT_ARG_DURATION_LIMIT = 3,
639         OPT_ARG_MOH_CLASS = 4,
640         OPT_ARG_INTROMSG = 5,
641         OPT_ARG_INTROUSER_VMREC = 6,
642         OPT_ARG_ARRAY_SIZE = 7,
643 };
644
645 AST_APP_OPTIONS(meetme_opts, BEGIN_OPTIONS
646         AST_APP_OPTION('A', CONFFLAG_MARKEDUSER ),
647         AST_APP_OPTION('a', CONFFLAG_ADMIN ),
648         AST_APP_OPTION('b', CONFFLAG_AGI ),
649         AST_APP_OPTION('c', CONFFLAG_ANNOUNCEUSERCOUNT ),
650         AST_APP_OPTION('C', CONFFLAG_KICK_CONTINUE),
651         AST_APP_OPTION('D', CONFFLAG_DYNAMICPIN ),
652         AST_APP_OPTION('d', CONFFLAG_DYNAMIC ),
653         AST_APP_OPTION('E', CONFFLAG_EMPTYNOPIN ),
654         AST_APP_OPTION('e', CONFFLAG_EMPTY ),
655         AST_APP_OPTION('F', CONFFLAG_PASS_DTMF ),
656         AST_APP_OPTION_ARG('G', CONFFLAG_INTROMSG, OPT_ARG_INTROMSG ),
657         AST_APP_OPTION_ARG('v', CONFFLAG_INTROUSER_VMREC , OPT_ARG_INTROUSER_VMREC),
658         AST_APP_OPTION('i', CONFFLAG_INTROUSER ),
659         AST_APP_OPTION('I', CONFFLAG_INTROUSERNOREVIEW ),
660         AST_APP_OPTION_ARG('M', CONFFLAG_MOH, OPT_ARG_MOH_CLASS ),
661         AST_APP_OPTION('m', CONFFLAG_STARTMUTED ),
662         AST_APP_OPTION('o', CONFFLAG_OPTIMIZETALKER ),
663         AST_APP_OPTION('P', CONFFLAG_ALWAYSPROMPT ),
664         AST_APP_OPTION_ARG('p', CONFFLAG_KEYEXIT, OPT_ARG_EXITKEYS ),
665         AST_APP_OPTION('q', CONFFLAG_QUIET ),
666         AST_APP_OPTION('r', CONFFLAG_RECORDCONF ),
667         AST_APP_OPTION('s', CONFFLAG_STARMENU ),
668         AST_APP_OPTION('T', CONFFLAG_MONITORTALKER ),
669         AST_APP_OPTION('l', CONFFLAG_MONITOR ),
670         AST_APP_OPTION('t', CONFFLAG_TALKER ),
671         AST_APP_OPTION_ARG('w', CONFFLAG_WAITMARKED, OPT_ARG_WAITMARKED ),
672         AST_APP_OPTION('X', CONFFLAG_EXIT_CONTEXT ),
673         AST_APP_OPTION('x', CONFFLAG_MARKEDEXIT ),
674         AST_APP_OPTION('1', CONFFLAG_NOONLYPERSON ),
675         AST_APP_OPTION_ARG('S', CONFFLAG_DURATION_STOP, OPT_ARG_DURATION_STOP),
676         AST_APP_OPTION_ARG('L', CONFFLAG_DURATION_LIMIT, OPT_ARG_DURATION_LIMIT),
677 END_OPTIONS );
678
679 static const char * const app = "MeetMe";
680 static const char * const app2 = "MeetMeCount";
681 static const char * const app3 = "MeetMeAdmin";
682 static const char * const app4 = "MeetMeChannelAdmin";
683 static const char * const slastation_app = "SLAStation";
684 static const char * const slatrunk_app = "SLATrunk";
685
686 /* Lookup RealTime conferences based on confno and current time */
687 static int rt_schedule;
688 static int fuzzystart;
689 static int earlyalert;
690 static int endalert;
691 static int extendby;
692
693 /*! Log participant count to the RealTime backend */
694 static int rt_log_members;
695
696 #define MAX_CONFNUM 80
697 #define MAX_PIN     80
698 #define OPTIONS_LEN 100
699
700 /* Enough space for "<conference #>,<pin>,<admin pin>" followed by a 0 byte. */
701 #define MAX_SETTINGS (MAX_CONFNUM + MAX_PIN + MAX_PIN + 3)
702
703 enum announcetypes {
704         CONF_HASJOIN,
705         CONF_HASLEFT
706 };
707
708 struct announce_listitem {
709         AST_LIST_ENTRY(announce_listitem) entry;
710         char namerecloc[PATH_MAX];                              /*!< Name Recorded file Location */
711         char language[MAX_LANGUAGE];
712         struct ast_channel *confchan;
713         int confusers;
714         int vmrec;
715         enum announcetypes announcetype;
716 };
717
718 /*! \brief The MeetMe Conference object */
719 struct ast_conference {
720         ast_mutex_t playlock;                   /*!< Conference specific lock (players) */
721         ast_mutex_t listenlock;                 /*!< Conference specific lock (listeners) */
722         char confno[MAX_CONFNUM];               /*!< Conference */
723         struct ast_channel *chan;               /*!< Announcements channel */
724         struct ast_channel *lchan;              /*!< Listen/Record channel */
725         int fd;                                 /*!< Announcements fd */
726         int dahdiconf;                            /*!< DAHDI Conf # */
727         int users;                              /*!< Number of active users */
728         int markedusers;                        /*!< Number of marked users */
729         int maxusers;                           /*!< Participant limit if scheduled */
730         int endalert;                           /*!< When to play conf ending message */
731         time_t start;                           /*!< Start time (s) */
732         int refcount;                           /*!< reference count of usage */
733         enum recording_state recording:2;       /*!< recording status */
734         unsigned int isdynamic:1;               /*!< Created on the fly? */
735         unsigned int locked:1;                  /*!< Is the conference locked? */
736         unsigned int gmuted:1;                  /*!< Is the conference globally muted? (all non-admins) */
737         pthread_t recordthread;                 /*!< thread for recording */
738         ast_mutex_t recordthreadlock;           /*!< control threads trying to start recordthread */
739         pthread_attr_t attr;                    /*!< thread attribute */
740         char *recordingfilename;                /*!< Filename to record the Conference into */
741         char *recordingformat;                  /*!< Format to record the Conference in */
742         char pin[MAX_PIN];                      /*!< If protected by a PIN */
743         char pinadmin[MAX_PIN];                 /*!< If protected by a admin PIN */
744         char uniqueid[32];
745         long endtime;                           /*!< When to end the conf if scheduled */
746         const char *useropts;                   /*!< RealTime user flags */
747         const char *adminopts;                  /*!< RealTime moderator flags */
748         const char *bookid;                     /*!< RealTime conference id */
749         struct ast_frame *transframe[32];
750         struct ast_frame *origframe;
751         struct ast_trans_pvt *transpath[32];
752         struct ao2_container *usercontainer;
753         AST_LIST_ENTRY(ast_conference) list;
754         /* announce_thread related data */
755         pthread_t announcethread;
756         ast_mutex_t announcethreadlock;
757         unsigned int announcethread_stop:1;
758         ast_cond_t announcelist_addition;
759         AST_LIST_HEAD_NOLOCK(, announce_listitem) announcelist;
760         ast_mutex_t announcelistlock;
761 };
762
763 static AST_LIST_HEAD_STATIC(confs, ast_conference);
764
765 static unsigned int conf_map[1024] = {0, };
766
767 struct volume {
768         int desired;                            /*!< Desired volume adjustment */
769         int actual;                             /*!< Actual volume adjustment (for channels that can't adjust) */
770 };
771
772 /*! \brief The MeetMe User object */
773 struct ast_conf_user {
774         int user_no;                            /*!< User Number */
775         struct ast_flags64 userflags;           /*!< Flags as set in the conference */
776         int adminflags;                         /*!< Flags set by the Admin */
777         struct ast_channel *chan;               /*!< Connected channel */
778         int talking;                            /*!< Is user talking */
779         int dahdichannel;                       /*!< Is a DAHDI channel */
780         char usrvalue[50];                      /*!< Custom User Value */
781         char namerecloc[PATH_MAX];              /*!< Name Recorded file Location */
782         time_t jointime;                        /*!< Time the user joined the conference */
783         time_t kicktime;                        /*!< Time the user will be kicked from the conference */
784         struct timeval start_time;              /*!< Time the user entered into the conference */
785         long timelimit;                         /*!< Time limit for the user to be in the conference L(x:y:z) */
786         long play_warning;                      /*!< Play a warning when 'y' ms are left */
787         long warning_freq;                      /*!< Repeat the warning every 'z' ms */
788         const char *warning_sound;              /*!< File to play as warning if 'y' is defined */
789         const char *end_sound;                  /*!< File to play when time is up. */
790         struct volume talk;
791         struct volume listen;
792         AST_LIST_ENTRY(ast_conf_user) list;
793 };
794
795 enum sla_which_trunk_refs {
796         ALL_TRUNK_REFS,
797         INACTIVE_TRUNK_REFS,
798 };
799
800 enum sla_trunk_state {
801         SLA_TRUNK_STATE_IDLE,
802         SLA_TRUNK_STATE_RINGING,
803         SLA_TRUNK_STATE_UP,
804         SLA_TRUNK_STATE_ONHOLD,
805         SLA_TRUNK_STATE_ONHOLD_BYME,
806 };
807
808 enum sla_hold_access {
809         /*! This means that any station can put it on hold, and any station
810          * can retrieve the call from hold. */
811         SLA_HOLD_OPEN,
812         /*! This means that only the station that put the call on hold may
813          * retrieve it from hold. */
814         SLA_HOLD_PRIVATE,
815 };
816
817 struct sla_trunk_ref;
818
819 struct sla_station {
820         AST_RWLIST_ENTRY(sla_station) entry;
821         AST_DECLARE_STRING_FIELDS(
822                 AST_STRING_FIELD(name); 
823                 AST_STRING_FIELD(device);       
824                 AST_STRING_FIELD(autocontext);  
825         );
826         AST_LIST_HEAD_NOLOCK(, sla_trunk_ref) trunks;
827         struct ast_dial *dial;
828         /*! Ring timeout for this station, for any trunk.  If a ring timeout
829          *  is set for a specific trunk on this station, that will take
830          *  priority over this value. */
831         unsigned int ring_timeout;
832         /*! Ring delay for this station, for any trunk.  If a ring delay
833          *  is set for a specific trunk on this station, that will take
834          *  priority over this value. */
835         unsigned int ring_delay;
836         /*! This option uses the values in the sla_hold_access enum and sets the
837          * access control type for hold on this station. */
838         unsigned int hold_access:1;
839         /*! Use count for inside sla_station_exec */
840         unsigned int ref_count;
841 };
842
843 struct sla_station_ref {
844         AST_LIST_ENTRY(sla_station_ref) entry;
845         struct sla_station *station;
846 };
847
848 struct sla_trunk {
849         AST_RWLIST_ENTRY(sla_trunk) entry;
850         AST_DECLARE_STRING_FIELDS(
851                 AST_STRING_FIELD(name);
852                 AST_STRING_FIELD(device);
853                 AST_STRING_FIELD(autocontext);  
854         );
855         AST_LIST_HEAD_NOLOCK(, sla_station_ref) stations;
856         /*! Number of stations that use this trunk */
857         unsigned int num_stations;
858         /*! Number of stations currently on a call with this trunk */
859         unsigned int active_stations;
860         /*! Number of stations that have this trunk on hold. */
861         unsigned int hold_stations;
862         struct ast_channel *chan;
863         unsigned int ring_timeout;
864         /*! If set to 1, no station will be able to join an active call with
865          *  this trunk. */
866         unsigned int barge_disabled:1;
867         /*! This option uses the values in the sla_hold_access enum and sets the
868          * access control type for hold on this trunk. */
869         unsigned int hold_access:1;
870         /*! Whether this trunk is currently on hold, meaning that once a station
871          *  connects to it, the trunk channel needs to have UNHOLD indicated to it. */
872         unsigned int on_hold:1;
873         /*! Use count for inside sla_trunk_exec */
874         unsigned int ref_count;
875 };
876
877 struct sla_trunk_ref {
878         AST_LIST_ENTRY(sla_trunk_ref) entry;
879         struct sla_trunk *trunk;
880         enum sla_trunk_state state;
881         struct ast_channel *chan;
882         /*! Ring timeout to use when this trunk is ringing on this specific
883          *  station.  This takes higher priority than a ring timeout set at
884          *  the station level. */
885         unsigned int ring_timeout;
886         /*! Ring delay to use when this trunk is ringing on this specific
887          *  station.  This takes higher priority than a ring delay set at
888          *  the station level. */
889         unsigned int ring_delay;
890 };
891
892 static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
893 static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
894
895 static const char sla_registrar[] = "SLA";
896
897 /*! \brief Event types that can be queued up for the SLA thread */
898 enum sla_event_type {
899         /*! A station has put the call on hold */
900         SLA_EVENT_HOLD,
901         /*! The state of a dial has changed */
902         SLA_EVENT_DIAL_STATE,
903         /*! The state of a ringing trunk has changed */
904         SLA_EVENT_RINGING_TRUNK,
905         /*! A reload of configuration has been requested */
906         SLA_EVENT_RELOAD,
907         /*! Poke the SLA thread so it can check if it can perform a reload */
908         SLA_EVENT_CHECK_RELOAD,
909 };
910
911 struct sla_event {
912         enum sla_event_type type;
913         struct sla_station *station;
914         struct sla_trunk_ref *trunk_ref;
915         AST_LIST_ENTRY(sla_event) entry;
916 };
917
918 /*! \brief A station that failed to be dialed 
919  * \note Only used by the SLA thread. */
920 struct sla_failed_station {
921         struct sla_station *station;
922         struct timeval last_try;
923         AST_LIST_ENTRY(sla_failed_station) entry;
924 };
925
926 /*! \brief A trunk that is ringing */
927 struct sla_ringing_trunk {
928         struct sla_trunk *trunk;
929         /*! The time that this trunk started ringing */
930         struct timeval ring_begin;
931         AST_LIST_HEAD_NOLOCK(, sla_station_ref) timed_out_stations;
932         AST_LIST_ENTRY(sla_ringing_trunk) entry;
933 };
934
935 enum sla_station_hangup {
936         SLA_STATION_HANGUP_NORMAL,
937         SLA_STATION_HANGUP_TIMEOUT,
938 };
939
940 /*! \brief A station that is ringing */
941 struct sla_ringing_station {
942         struct sla_station *station;
943         /*! The time that this station started ringing */
944         struct timeval ring_begin;
945         AST_LIST_ENTRY(sla_ringing_station) entry;
946 };
947
948 /*!
949  * \brief A structure for data used by the sla thread
950  */
951 static struct {
952         /*! The SLA thread ID */
953         pthread_t thread;
954         ast_cond_t cond;
955         ast_mutex_t lock;
956         AST_LIST_HEAD_NOLOCK(, sla_ringing_trunk) ringing_trunks;
957         AST_LIST_HEAD_NOLOCK(, sla_ringing_station) ringing_stations;
958         AST_LIST_HEAD_NOLOCK(, sla_failed_station) failed_stations;
959         AST_LIST_HEAD_NOLOCK(, sla_event) event_q;
960         unsigned int stop:1;
961         /*! Attempt to handle CallerID, even though it is known not to work
962          *  properly in some situations. */
963         unsigned int attempt_callerid:1;
964         /*! A reload has been requested */
965         unsigned int reload:1;
966 } sla = {
967         .thread = AST_PTHREADT_NULL,
968 };
969
970 /*! \brief The number of audio buffers to be allocated on pseudo channels
971  *  when in a conference */
972 static int audio_buffers;
973
974 /*! \brief Map 'volume' levels from -5 through +5 into decibel (dB) 
975  *    settings for channel drivers.
976  *
977  *  \note these are not a straight linear-to-dB
978  *  conversion... the numbers have been modified
979  *  to give the user a better level of adjustability.
980  */
981 static const char gain_map[] = {
982         -15,
983         -13,
984         -10,
985         -6,
986         0,
987         0,
988         0,
989         6,
990         10,
991         13,
992         15,
993 };
994
995
996 static int admin_exec(struct ast_channel *chan, const char *data);
997 static void *recordthread(void *args);
998
999 static const char *istalking(int x)
1000 {
1001         if (x > 0)
1002                 return "(talking)";
1003         else if (x < 0)
1004                 return "(unmonitored)";
1005         else 
1006                 return "(not talking)";
1007 }
1008
1009 static int careful_write(int fd, unsigned char *data, int len, int block)
1010 {
1011         int res;
1012         int x;
1013
1014         while (len) {
1015                 if (block) {
1016                         x = DAHDI_IOMUX_WRITE | DAHDI_IOMUX_SIGEVENT;
1017                         res = ioctl(fd, DAHDI_IOMUX, &x);
1018                 } else
1019                         res = 0;
1020                 if (res >= 0)
1021                         res = write(fd, data, len);
1022                 if (res < 1) {
1023                         if (errno != EAGAIN) {
1024                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
1025                                 return -1;
1026                         } else
1027                                 return 0;
1028                 }
1029                 len -= res;
1030                 data += res;
1031         }
1032
1033         return 0;
1034 }
1035
1036 static int set_talk_volume(struct ast_conf_user *user, int volume)
1037 {
1038         char gain_adjust;
1039
1040         /* attempt to make the adjustment in the channel driver;
1041            if successful, don't adjust in the frame reading routine
1042         */
1043         gain_adjust = gain_map[volume + 5];
1044
1045         return ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1046 }
1047
1048 static int set_listen_volume(struct ast_conf_user *user, int volume)
1049 {
1050         char gain_adjust;
1051
1052         /* attempt to make the adjustment in the channel driver;
1053            if successful, don't adjust in the frame reading routine
1054         */
1055         gain_adjust = gain_map[volume + 5];
1056
1057         return ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &gain_adjust, sizeof(gain_adjust), 0);
1058 }
1059
1060 static void tweak_volume(struct volume *vol, enum volume_action action)
1061 {
1062         switch (action) {
1063         case VOL_UP:
1064                 switch (vol->desired) { 
1065                 case 5:
1066                         break;
1067                 case 0:
1068                         vol->desired = 2;
1069                         break;
1070                 case -2:
1071                         vol->desired = 0;
1072                         break;
1073                 default:
1074                         vol->desired++;
1075                         break;
1076                 }
1077                 break;
1078         case VOL_DOWN:
1079                 switch (vol->desired) {
1080                 case -5:
1081                         break;
1082                 case 2:
1083                         vol->desired = 0;
1084                         break;
1085                 case 0:
1086                         vol->desired = -2;
1087                         break;
1088                 default:
1089                         vol->desired--;
1090                         break;
1091                 }
1092         }
1093 }
1094
1095 static void tweak_talk_volume(struct ast_conf_user *user, enum volume_action action)
1096 {
1097         tweak_volume(&user->talk, action);
1098         /* attempt to make the adjustment in the channel driver;
1099            if successful, don't adjust in the frame reading routine
1100         */
1101         if (!set_talk_volume(user, user->talk.desired))
1102                 user->talk.actual = 0;
1103         else
1104                 user->talk.actual = user->talk.desired;
1105 }
1106
1107 static void tweak_listen_volume(struct ast_conf_user *user, enum volume_action action)
1108 {
1109         tweak_volume(&user->listen, action);
1110         /* attempt to make the adjustment in the channel driver;
1111            if successful, don't adjust in the frame reading routine
1112         */
1113         if (!set_listen_volume(user, user->listen.desired))
1114                 user->listen.actual = 0;
1115         else
1116                 user->listen.actual = user->listen.desired;
1117 }
1118
1119 static void reset_volumes(struct ast_conf_user *user)
1120 {
1121         signed char zero_volume = 0;
1122
1123         ast_channel_setoption(user->chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
1124         ast_channel_setoption(user->chan, AST_OPTION_RXGAIN, &zero_volume, sizeof(zero_volume), 0);
1125 }
1126
1127 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, enum entrance_sound sound)
1128 {
1129         unsigned char *data;
1130         int len;
1131         int res = -1;
1132
1133         if (!ast_check_hangup(chan))
1134                 res = ast_autoservice_start(chan);
1135
1136         AST_LIST_LOCK(&confs);
1137
1138         switch(sound) {
1139         case ENTER:
1140                 data = enter;
1141                 len = sizeof(enter);
1142                 break;
1143         case LEAVE:
1144                 data = leave;
1145                 len = sizeof(leave);
1146                 break;
1147         default:
1148                 data = NULL;
1149                 len = 0;
1150         }
1151         if (data) {
1152                 careful_write(conf->fd, data, len, 1);
1153         }
1154
1155         AST_LIST_UNLOCK(&confs);
1156
1157         if (!res) 
1158                 ast_autoservice_stop(chan);
1159 }
1160
1161 static int user_no_cmp(void *obj, void *arg, int flags)
1162 {
1163         struct ast_conf_user *user = obj;
1164         int *user_no = arg;
1165
1166         if (user->user_no == *user_no) {
1167                 return (CMP_MATCH | CMP_STOP);
1168         }
1169
1170         return 0;
1171 }
1172
1173 static int user_max_cmp(void *obj, void *arg, int flags)
1174 {
1175         struct ast_conf_user *user = obj;
1176         int *max_no = arg;
1177
1178         if (user->user_no > *max_no) {
1179                 *max_no = user->user_no;
1180         }
1181
1182         return 0;
1183 }
1184
1185 /*!
1186  * \brief Find or create a conference
1187  *
1188  * \param confno The conference name/number
1189  * \param pin The regular user pin
1190  * \param pinadmin The admin pin
1191  * \param make Make the conf if it doesn't exist
1192  * \param dynamic Mark the newly created conference as dynamic
1193  * \param refcount How many references to mark on the conference
1194  * \param chan The asterisk channel
1195  *
1196  * \return A pointer to the conference struct, or NULL if it wasn't found and
1197  *         make or dynamic were not set.
1198  */
1199 static struct ast_conference *build_conf(const char *confno, const char *pin,
1200         const char *pinadmin, int make, int dynamic, int refcount,
1201         const struct ast_channel *chan, struct ast_test *test)
1202 {
1203         struct ast_conference *cnf;
1204         struct dahdi_confinfo dahdic = { 0, };
1205         int confno_int = 0;
1206         struct ast_format_cap *cap_slin = ast_format_cap_alloc_nolock();
1207         struct ast_format tmp_fmt;
1208
1209         AST_LIST_LOCK(&confs);
1210
1211         AST_LIST_TRAVERSE(&confs, cnf, list) {
1212                 if (!strcmp(confno, cnf->confno)) 
1213                         break;
1214         }
1215
1216         if (cnf || (!make && !dynamic) || !cap_slin)
1217                 goto cnfout;
1218
1219         ast_format_cap_add(cap_slin, ast_format_set(&tmp_fmt, AST_FORMAT_SLINEAR, 0));
1220         /* Make a new one */
1221         if (!(cnf = ast_calloc(1, sizeof(*cnf))) ||
1222                 !(cnf->usercontainer = ao2_container_alloc(1, NULL, user_no_cmp))) {
1223                 goto cnfout;
1224         }
1225
1226         ast_mutex_init(&cnf->playlock);
1227         ast_mutex_init(&cnf->listenlock);
1228         cnf->recordthread = AST_PTHREADT_NULL;
1229         ast_mutex_init(&cnf->recordthreadlock);
1230         cnf->announcethread = AST_PTHREADT_NULL;
1231         ast_mutex_init(&cnf->announcethreadlock);
1232         ast_copy_string(cnf->confno, confno, sizeof(cnf->confno));
1233         ast_copy_string(cnf->pin, pin, sizeof(cnf->pin));
1234         ast_copy_string(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin));
1235         ast_copy_string(cnf->uniqueid, chan->uniqueid, sizeof(cnf->uniqueid));
1236
1237         /* Setup a new dahdi conference */
1238         dahdic.confno = -1;
1239         dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1240         cnf->fd = open("/dev/dahdi/pseudo", O_RDWR);
1241         if (cnf->fd < 0 || ioctl(cnf->fd, DAHDI_SETCONF, &dahdic)) {
1242                 if (test) {
1243                         /* if we are creating a conference for a unit test, it is not neccesary
1244                          * to open a pseudo channel, so, if we fail continue creating
1245                          * the conference. */
1246                         ast_test_status_update(test, "Unable to open DAHDI pseudo device\n");
1247                 } else {
1248                         ast_log(LOG_WARNING, "Unable to open DAHDI pseudo device\n");
1249                         if (cnf->fd >= 0)
1250                                 close(cnf->fd);
1251                         ao2_ref(cnf->usercontainer, -1);
1252                         ast_mutex_destroy(&cnf->playlock);
1253                         ast_mutex_destroy(&cnf->listenlock);
1254                         ast_mutex_destroy(&cnf->recordthreadlock);
1255                         ast_mutex_destroy(&cnf->announcethreadlock);
1256                         ast_free(cnf);
1257                         cnf = NULL;
1258                         goto cnfout;
1259                 }
1260         }
1261
1262         cnf->dahdiconf = dahdic.confno;
1263
1264         /* Setup a new channel for playback of audio files */
1265         cnf->chan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL);
1266         if (cnf->chan) {
1267                 ast_set_read_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1268                 ast_set_write_format_by_id(cnf->chan, AST_FORMAT_SLINEAR);
1269                 dahdic.chan = 0;
1270                 dahdic.confno = cnf->dahdiconf;
1271                 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
1272                 if (ioctl(cnf->chan->fds[0], DAHDI_SETCONF, &dahdic)) {
1273                         if (test) {
1274                                 ast_test_status_update(test, "Error setting conference on pseudo channel\n");
1275                         }
1276                         ast_log(LOG_WARNING, "Error setting conference\n");
1277                         if (cnf->chan)
1278                                 ast_hangup(cnf->chan);
1279                         else
1280                                 close(cnf->fd);
1281                         ao2_ref(cnf->usercontainer, -1);
1282                         ast_mutex_destroy(&cnf->playlock);
1283                         ast_mutex_destroy(&cnf->listenlock);
1284                         ast_mutex_destroy(&cnf->recordthreadlock);
1285                         ast_mutex_destroy(&cnf->announcethreadlock);
1286                         ast_free(cnf);
1287                         cnf = NULL;
1288                         goto cnfout;
1289                 }
1290         }
1291
1292         /* Fill the conference struct */
1293         cnf->start = time(NULL);
1294         cnf->maxusers = 0x7fffffff;
1295         cnf->isdynamic = dynamic ? 1 : 0;
1296         ast_verb(3, "Created MeetMe conference %d for conference '%s'\n", cnf->dahdiconf, cnf->confno);
1297         AST_LIST_INSERT_HEAD(&confs, cnf, list);
1298
1299         /* Reserve conference number in map */
1300         if ((sscanf(cnf->confno, "%30d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024))
1301                 conf_map[confno_int] = 1;
1302         
1303 cnfout:
1304         cap_slin = ast_format_cap_destroy(cap_slin);
1305         if (cnf)
1306                 ast_atomic_fetchadd_int(&cnf->refcount, refcount);
1307
1308         AST_LIST_UNLOCK(&confs);
1309
1310         return cnf;
1311 }
1312
1313 static char *complete_meetmecmd(const char *line, const char *word, int pos, int state)
1314 {
1315         static const char * const cmds[] = {"concise", "lock", "unlock", "mute", "unmute", "kick", "list", NULL};
1316
1317         int len = strlen(word);
1318         int which = 0;
1319         struct ast_conference *cnf = NULL;
1320         struct ast_conf_user *usr = NULL;
1321         char *confno = NULL;
1322         char usrno[50] = "";
1323         char *myline, *ret = NULL;
1324         
1325         if (pos == 1) {         /* Command */
1326                 return ast_cli_complete(word, cmds, state);
1327         } else if (pos == 2) {  /* Conference Number */
1328                 AST_LIST_LOCK(&confs);
1329                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1330                         if (!strncasecmp(word, cnf->confno, len) && ++which > state) {
1331                                 ret = cnf->confno;
1332                                 break;
1333                         }
1334                 }
1335                 ret = ast_strdup(ret); /* dup before releasing the lock */
1336                 AST_LIST_UNLOCK(&confs);
1337                 return ret;
1338         } else if (pos == 3) {
1339                 /* User Number || Conf Command option*/
1340                 if (strstr(line, "mute") || strstr(line, "kick")) {
1341                         if (state == 0 && (strstr(line, "kick") || strstr(line, "mute")) && !strncasecmp(word, "all", len))
1342                                 return ast_strdup("all");
1343                         which++;
1344                         AST_LIST_LOCK(&confs);
1345
1346                         /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
1347                         myline = ast_strdupa(line);
1348                         if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
1349                                 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
1350                                         ;
1351                         }
1352                         
1353                         AST_LIST_TRAVERSE(&confs, cnf, list) {
1354                                 if (!strcmp(confno, cnf->confno))
1355                                     break;
1356                         }
1357
1358                         if (cnf) {
1359                                 struct ao2_iterator user_iter;
1360                                 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1361
1362                                 while((usr = ao2_iterator_next(&user_iter))) {
1363                                         snprintf(usrno, sizeof(usrno), "%d", usr->user_no);
1364                                         if (!strncasecmp(word, usrno, len) && ++which > state) {
1365                                                 ao2_ref(usr, -1);
1366                                                 break;
1367                                         }
1368                                         ao2_ref(usr, -1);
1369                                 }
1370                                 ao2_iterator_destroy(&user_iter);
1371                                 AST_LIST_UNLOCK(&confs);
1372                                 return usr ? ast_strdup(usrno) : NULL;
1373                         }
1374                 }
1375         }
1376
1377         return NULL;
1378 }
1379
1380 static char *meetme_show_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1381 {
1382         /* Process the command */
1383         struct ast_conf_user *user;
1384         struct ast_conference *cnf;
1385         int hr, min, sec;
1386         int i = 0, total = 0;
1387         time_t now;
1388         struct ast_str *cmdline = NULL;
1389 #define MC_HEADER_FORMAT "%-14s %-14s %-10s %-8s  %-8s  %-6s\n"
1390 #define MC_DATA_FORMAT "%-12.12s   %4.4d              %4.4s       %02d:%02d:%02d  %-8s  %-6s\n"
1391
1392         switch (cmd) {
1393         case CLI_INIT:
1394                 e->command = "meetme list [concise]";
1395                 e->usage =
1396                         "Usage: meetme list [concise] <confno> \n"
1397                         "       List all or a specific conference.\n";
1398                 return NULL;
1399         case CLI_GENERATE:
1400                 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1401         }
1402
1403         /* Check for length so no buffer will overflow... */
1404         for (i = 0; i < a->argc; i++) {
1405                 if (strlen(a->argv[i]) > 100)
1406                         ast_cli(a->fd, "Invalid Arguments.\n");
1407         }
1408
1409         /* Max confno length */
1410         if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1411                 return CLI_FAILURE;
1412         }
1413
1414         if (a->argc == 2 || (a->argc == 3 && !strcasecmp(a->argv[2], "concise"))) {
1415                 /* List all the conferences */  
1416                 int concise = (a->argc == 3 && !strcasecmp(a->argv[2], "concise"));
1417                 now = time(NULL);
1418                 AST_LIST_LOCK(&confs);
1419                 if (AST_LIST_EMPTY(&confs)) {
1420                         if (!concise) {
1421                                 ast_cli(a->fd, "No active MeetMe conferences.\n");
1422                         }
1423                         AST_LIST_UNLOCK(&confs);
1424                         ast_free(cmdline);
1425                         return CLI_SUCCESS;
1426                 }
1427                 if (!concise) {
1428                         ast_cli(a->fd, MC_HEADER_FORMAT, "Conf Num", "Parties", "Marked", "Activity", "Creation", "Locked");
1429                 }
1430                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1431                         if (cnf->markedusers == 0) {
1432                                 ast_str_set(&cmdline, 0, "N/A ");
1433                         } else {
1434                                 ast_str_set(&cmdline, 0, "%4.4d", cnf->markedusers);
1435                         }
1436                         hr = (now - cnf->start) / 3600;
1437                         min = ((now - cnf->start) % 3600) / 60;
1438                         sec = (now - cnf->start) % 60;
1439                         if (!concise) {
1440                                 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");
1441                         } else {
1442                                 ast_cli(a->fd, "%s!%d!%d!%02d:%02d:%02d!%d!%d\n",
1443                                         cnf->confno,
1444                                         cnf->users,
1445                                         cnf->markedusers,
1446                                         hr, min, sec,
1447                                         cnf->isdynamic,
1448                                         cnf->locked);
1449                         }
1450
1451                         total += cnf->users;
1452                 }
1453                 AST_LIST_UNLOCK(&confs);
1454                 if (!concise) {
1455                         ast_cli(a->fd, "* Total number of MeetMe users: %d\n", total);
1456                 }
1457                 ast_free(cmdline);
1458                 return CLI_SUCCESS;
1459         } else if (strcmp(a->argv[1], "list") == 0) {
1460                 struct ao2_iterator user_iter;
1461                 int concise = (a->argc == 4 && (!strcasecmp(a->argv[3], "concise")));
1462                 /* List all the users in a conference */
1463                 if (AST_LIST_EMPTY(&confs)) {
1464                         if (!concise) {
1465                                 ast_cli(a->fd, "No active MeetMe conferences.\n");
1466                         }
1467                         ast_free(cmdline);
1468                         return CLI_SUCCESS;     
1469                 }
1470                 /* Find the right conference */
1471                 AST_LIST_LOCK(&confs);
1472                 AST_LIST_TRAVERSE(&confs, cnf, list) {
1473                         if (strcmp(cnf->confno, a->argv[2]) == 0) {
1474                                 break;
1475                         }
1476                 }
1477                 if (!cnf) {
1478                         if (!concise)
1479                                 ast_cli(a->fd, "No such conference: %s.\n", a->argv[2]);
1480                         AST_LIST_UNLOCK(&confs);
1481                         ast_free(cmdline);
1482                         return CLI_SUCCESS;
1483                 }
1484                 /* Show all the users */
1485                 time(&now);
1486                 user_iter = ao2_iterator_init(cnf->usercontainer, 0);
1487                 while((user = ao2_iterator_next(&user_iter))) {
1488                         hr = (now - user->jointime) / 3600;
1489                         min = ((now - user->jointime) % 3600) / 60;
1490                         sec = (now - user->jointime) % 60;
1491                         if (!concise) {
1492                                 ast_cli(a->fd, "User #: %-2.2d %12.12s %-20.20s Channel: %s %s %s %s %s %s %02d:%02d:%02d\n",
1493                                         user->user_no,
1494                                         S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
1495                                         S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<no name>"),
1496                                         user->chan->name,
1497                                         ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "(Admin)" : "",
1498                                         ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "(Listen only)" : "",
1499                                         user->adminflags & ADMINFLAG_MUTED ? "(Admin Muted)" : user->adminflags & ADMINFLAG_SELFMUTED ? "(Muted)" : "",
1500                                         user->adminflags & ADMINFLAG_T_REQUEST ? "(Request to Talk)" : "",
1501                                         istalking(user->talking), hr, min, sec); 
1502                         } else {
1503                                 ast_cli(a->fd, "%d!%s!%s!%s!%s!%s!%s!%s!%d!%02d:%02d:%02d\n",
1504                                         user->user_no,
1505                                         S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, ""),
1506                                         S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, ""),
1507                                         user->chan->name,
1508                                         ast_test_flag64(&user->userflags, CONFFLAG_ADMIN) ? "1" : "",
1509                                         ast_test_flag64(&user->userflags, CONFFLAG_MONITOR) ? "1" : "",
1510                                         user->adminflags & (ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED) ? "1" : "",
1511                                         user->adminflags & ADMINFLAG_T_REQUEST ? "1" : "",
1512                                         user->talking, hr, min, sec);
1513                         }
1514                         ao2_ref(user, -1);
1515                 }
1516                 ao2_iterator_destroy(&user_iter);
1517                 if (!concise) {
1518                         ast_cli(a->fd, "%d users in that conference.\n", cnf->users);
1519                 }
1520                 AST_LIST_UNLOCK(&confs);
1521                 ast_free(cmdline);
1522                 return CLI_SUCCESS;
1523         }
1524         if (a->argc < 2) {
1525                 ast_free(cmdline);
1526                 return CLI_SHOWUSAGE;
1527         }
1528
1529         ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1530
1531         admin_exec(NULL, ast_str_buffer(cmdline));
1532         ast_free(cmdline);
1533
1534         return CLI_SUCCESS;
1535 }
1536
1537
1538 static char *meetme_cmd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1539 {
1540         /* Process the command */
1541         struct ast_str *cmdline = NULL;
1542         int i = 0;
1543
1544         switch (cmd) {
1545         case CLI_INIT:
1546                 e->command = "meetme {lock|unlock|mute|unmute|kick}";
1547                 e->usage =
1548                         "Usage: meetme (un)lock|(un)mute|kick <confno> <usernumber>\n"
1549                         "       Executes a command for the conference or on a conferee\n";
1550                 return NULL;
1551         case CLI_GENERATE:
1552                 return complete_meetmecmd(a->line, a->word, a->pos, a->n);
1553         }
1554
1555         if (a->argc > 8)
1556                 ast_cli(a->fd, "Invalid Arguments.\n");
1557         /* Check for length so no buffer will overflow... */
1558         for (i = 0; i < a->argc; i++) {
1559                 if (strlen(a->argv[i]) > 100)
1560                         ast_cli(a->fd, "Invalid Arguments.\n");
1561         }
1562
1563         /* Max confno length */
1564         if (!(cmdline = ast_str_create(MAX_CONFNUM))) {
1565                 return CLI_FAILURE;
1566         }
1567
1568         if (a->argc < 1) {
1569                 ast_free(cmdline);
1570                 return CLI_SHOWUSAGE;
1571         }
1572
1573         ast_str_set(&cmdline, 0, "%s", a->argv[2]);     /* Argv 2: conference number */
1574         if (strstr(a->argv[1], "lock")) {
1575                 if (strcmp(a->argv[1], "lock") == 0) {
1576                         /* Lock */
1577                         ast_str_append(&cmdline, 0, ",L");
1578                 } else {
1579                         /* Unlock */
1580                         ast_str_append(&cmdline, 0, ",l");
1581                 }
1582         } else if (strstr(a->argv[1], "mute")) { 
1583                 if (a->argc < 4) {
1584                         ast_free(cmdline);
1585                         return CLI_SHOWUSAGE;
1586                 }
1587                 if (strcmp(a->argv[1], "mute") == 0) {
1588                         /* Mute */
1589                         if (strcmp(a->argv[3], "all") == 0) {
1590                                 ast_str_append(&cmdline, 0, ",N");
1591                         } else {
1592                                 ast_str_append(&cmdline, 0, ",M,%s", a->argv[3]);       
1593                         }
1594                 } else {
1595                         /* Unmute */
1596                         if (strcmp(a->argv[3], "all") == 0) {
1597                                 ast_str_append(&cmdline, 0, ",n");
1598                         } else {
1599                                 ast_str_append(&cmdline, 0, ",m,%s", a->argv[3]);
1600                         }
1601                 }
1602         } else if (strcmp(a->argv[1], "kick") == 0) {
1603                 if (a->argc < 4) {
1604                         ast_free(cmdline);
1605                         return CLI_SHOWUSAGE;
1606                 }
1607                 if (strcmp(a->argv[3], "all") == 0) {
1608                         /* Kick all */
1609                         ast_str_append(&cmdline, 0, ",K");
1610                 } else {
1611                         /* Kick a single user */
1612                         ast_str_append(&cmdline, 0, ",k,%s", a->argv[3]);
1613                 }
1614         } else {
1615                 ast_free(cmdline);
1616                 return CLI_SHOWUSAGE;
1617         }
1618
1619         ast_debug(1, "Cmdline: %s\n", ast_str_buffer(cmdline));
1620
1621         admin_exec(NULL, ast_str_buffer(cmdline));
1622         ast_free(cmdline);
1623
1624         return CLI_SUCCESS;
1625 }
1626
1627 static const char *sla_hold_str(unsigned int hold_access)
1628 {
1629         const char *hold = "Unknown";
1630
1631         switch (hold_access) {
1632         case SLA_HOLD_OPEN:
1633                 hold = "Open";
1634                 break;
1635         case SLA_HOLD_PRIVATE:
1636                 hold = "Private";
1637         default:
1638                 break;
1639         }
1640
1641         return hold;
1642 }
1643
1644 static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1645 {
1646         const struct sla_trunk *trunk;
1647
1648         switch (cmd) {
1649         case CLI_INIT:
1650                 e->command = "sla show trunks";
1651                 e->usage =
1652                         "Usage: sla show trunks\n"
1653                         "       This will list all trunks defined in sla.conf\n";
1654                 return NULL;
1655         case CLI_GENERATE:
1656                 return NULL;
1657         }
1658
1659         ast_cli(a->fd, "\n"
1660                     "=============================================================\n"
1661                     "=== Configured SLA Trunks ===================================\n"
1662                     "=============================================================\n"
1663                     "===\n");
1664         AST_RWLIST_RDLOCK(&sla_trunks);
1665         AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
1666                 struct sla_station_ref *station_ref;
1667                 char ring_timeout[16] = "(none)";
1668                 if (trunk->ring_timeout)
1669                         snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
1670                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1671                             "=== Trunk Name:       %s\n"
1672                             "=== ==> Device:       %s\n"
1673                             "=== ==> AutoContext:  %s\n"
1674                             "=== ==> RingTimeout:  %s\n"
1675                             "=== ==> BargeAllowed: %s\n"
1676                             "=== ==> HoldAccess:   %s\n"
1677                             "=== ==> Stations ...\n",
1678                             trunk->name, trunk->device, 
1679                             S_OR(trunk->autocontext, "(none)"), 
1680                             ring_timeout,
1681                             trunk->barge_disabled ? "No" : "Yes",
1682                             sla_hold_str(trunk->hold_access));
1683                 AST_RWLIST_RDLOCK(&sla_stations);
1684                 AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
1685                         ast_cli(a->fd, "===    ==> Station name: %s\n", station_ref->station->name);
1686                 AST_RWLIST_UNLOCK(&sla_stations);
1687                 ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
1688         }
1689         AST_RWLIST_UNLOCK(&sla_trunks);
1690         ast_cli(a->fd, "=============================================================\n\n");
1691
1692         return CLI_SUCCESS;
1693 }
1694
1695 static const char *trunkstate2str(enum sla_trunk_state state)
1696 {
1697 #define S(e) case e: return # e;
1698         switch (state) {
1699         S(SLA_TRUNK_STATE_IDLE)
1700         S(SLA_TRUNK_STATE_RINGING)
1701         S(SLA_TRUNK_STATE_UP)
1702         S(SLA_TRUNK_STATE_ONHOLD)
1703         S(SLA_TRUNK_STATE_ONHOLD_BYME)
1704         }
1705         return "Uknown State";
1706 #undef S
1707 }
1708
1709 static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1710 {
1711         const struct sla_station *station;
1712
1713         switch (cmd) {
1714         case CLI_INIT:
1715                 e->command = "sla show stations";
1716                 e->usage =
1717                         "Usage: sla show stations\n"
1718                         "       This will list all stations defined in sla.conf\n";
1719                 return NULL;
1720         case CLI_GENERATE:
1721                 return NULL;
1722         }
1723
1724         ast_cli(a->fd, "\n" 
1725                     "=============================================================\n"
1726                     "=== Configured SLA Stations =================================\n"
1727                     "=============================================================\n"
1728                     "===\n");
1729         AST_RWLIST_RDLOCK(&sla_stations);
1730         AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1731                 struct sla_trunk_ref *trunk_ref;
1732                 char ring_timeout[16] = "(none)";
1733                 char ring_delay[16] = "(none)";
1734                 if (station->ring_timeout) {
1735                         snprintf(ring_timeout, sizeof(ring_timeout), 
1736                                 "%u", station->ring_timeout);
1737                 }
1738                 if (station->ring_delay) {
1739                         snprintf(ring_delay, sizeof(ring_delay), 
1740                                 "%u", station->ring_delay);
1741                 }
1742                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1743                             "=== Station Name:    %s\n"
1744                             "=== ==> Device:      %s\n"
1745                             "=== ==> AutoContext: %s\n"
1746                             "=== ==> RingTimeout: %s\n"
1747                             "=== ==> RingDelay:   %s\n"
1748                             "=== ==> HoldAccess:  %s\n"
1749                             "=== ==> Trunks ...\n",
1750                             station->name, station->device,
1751                             S_OR(station->autocontext, "(none)"), 
1752                             ring_timeout, ring_delay,
1753                             sla_hold_str(station->hold_access));
1754                 AST_RWLIST_RDLOCK(&sla_trunks);
1755                 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1756                         if (trunk_ref->ring_timeout) {
1757                                 snprintf(ring_timeout, sizeof(ring_timeout),
1758                                         "%u", trunk_ref->ring_timeout);
1759                         } else
1760                                 strcpy(ring_timeout, "(none)");
1761                         if (trunk_ref->ring_delay) {
1762                                 snprintf(ring_delay, sizeof(ring_delay),
1763                                         "%u", trunk_ref->ring_delay);
1764                         } else
1765                                 strcpy(ring_delay, "(none)");
1766                                 ast_cli(a->fd, "===    ==> Trunk Name: %s\n"
1767                                     "===       ==> State:       %s\n"
1768                                     "===       ==> RingTimeout: %s\n"
1769                                     "===       ==> RingDelay:   %s\n",
1770                                     trunk_ref->trunk->name,
1771                                     trunkstate2str(trunk_ref->state),
1772                                     ring_timeout, ring_delay);
1773                 }
1774                 AST_RWLIST_UNLOCK(&sla_trunks);
1775                 ast_cli(a->fd, "=== ---------------------------------------------------------\n"
1776                             "===\n");
1777         }
1778         AST_RWLIST_UNLOCK(&sla_stations);
1779         ast_cli(a->fd, "============================================================\n"
1780                     "\n");
1781
1782         return CLI_SUCCESS;
1783 }
1784
1785 static struct ast_cli_entry cli_meetme[] = {
1786         AST_CLI_DEFINE(meetme_cmd, "Execute a command on a conference or conferee"),
1787         AST_CLI_DEFINE(meetme_show_cmd, "List all or one conference"),
1788         AST_CLI_DEFINE(sla_show_trunks, "Show SLA Trunks"),
1789         AST_CLI_DEFINE(sla_show_stations, "Show SLA Stations"),
1790 };
1791
1792 static void conf_flush(int fd, struct ast_channel *chan)
1793 {
1794         int x;
1795
1796         /* read any frames that may be waiting on the channel
1797            and throw them away
1798         */
1799         if (chan) {
1800                 struct ast_frame *f;
1801
1802                 /* when no frames are available, this will wait
1803                    for 1 millisecond maximum
1804                 */
1805                 while (ast_waitfor(chan, 1)) {
1806                         f = ast_read(chan);
1807                         if (f)
1808                                 ast_frfree(f);
1809                         else /* channel was hung up or something else happened */
1810                                 break;
1811                 }
1812         }
1813
1814         /* flush any data sitting in the pseudo channel */
1815         x = DAHDI_FLUSH_ALL;
1816         if (ioctl(fd, DAHDI_FLUSH, &x))
1817                 ast_log(LOG_WARNING, "Error flushing channel\n");
1818
1819 }
1820
1821 /*! \brief Remove the conference from the list and free it.
1822
1823    We assume that this was called while holding conflock. */
1824 static int conf_free(struct ast_conference *conf)
1825 {
1826         int x;
1827         struct announce_listitem *item;
1828         
1829         AST_LIST_REMOVE(&confs, conf, list);
1830         manager_event(EVENT_FLAG_CALL, "MeetmeEnd", "Meetme: %s\r\n", conf->confno);
1831
1832         if (conf->recording == MEETME_RECORD_ACTIVE) {
1833                 conf->recording = MEETME_RECORD_TERMINATE;
1834                 AST_LIST_UNLOCK(&confs);
1835                 while (1) {
1836                         usleep(1);
1837                         AST_LIST_LOCK(&confs);
1838                         if (conf->recording == MEETME_RECORD_OFF)
1839                                 break;
1840                         AST_LIST_UNLOCK(&confs);
1841                 }
1842         }
1843
1844         for (x = 0; x < AST_FRAME_BITS; x++) {
1845                 if (conf->transframe[x])
1846                         ast_frfree(conf->transframe[x]);
1847                 if (conf->transpath[x])
1848                         ast_translator_free_path(conf->transpath[x]);
1849         }
1850         if (conf->announcethread != AST_PTHREADT_NULL) {
1851                 ast_mutex_lock(&conf->announcelistlock);
1852                 conf->announcethread_stop = 1;
1853                 ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT);
1854                 ast_cond_signal(&conf->announcelist_addition);
1855                 ast_mutex_unlock(&conf->announcelistlock);
1856                 pthread_join(conf->announcethread, NULL);
1857         
1858                 while ((item = AST_LIST_REMOVE_HEAD(&conf->announcelist, entry))) {
1859                         /* If it's a voicemail greeting file we don't want to remove it */
1860                         if (!item->vmrec){
1861                                 ast_filedelete(item->namerecloc, NULL);
1862                         }
1863                         ao2_ref(item, -1);
1864                 }
1865                 ast_mutex_destroy(&conf->announcelistlock);
1866         }
1867
1868         if (conf->origframe)
1869                 ast_frfree(conf->origframe);
1870         if (conf->lchan)
1871                 ast_hangup(conf->lchan);
1872         if (conf->chan)
1873                 ast_hangup(conf->chan);
1874         if (conf->fd >= 0)
1875                 close(conf->fd);
1876         if (conf->recordingfilename) {
1877                 ast_free(conf->recordingfilename);
1878         }
1879         if (conf->usercontainer) {
1880                 ao2_ref(conf->usercontainer, -1);
1881         }
1882         if (conf->recordingformat) {
1883                 ast_free(conf->recordingformat);
1884         }
1885         ast_mutex_destroy(&conf->playlock);
1886         ast_mutex_destroy(&conf->listenlock);
1887         ast_mutex_destroy(&conf->recordthreadlock);
1888         ast_mutex_destroy(&conf->announcethreadlock);
1889         ast_free(conf);
1890
1891         return 0;
1892 }
1893
1894 static void conf_queue_dtmf(const struct ast_conference *conf,
1895         const struct ast_conf_user *sender, struct ast_frame *f)
1896 {
1897         struct ast_conf_user *user;
1898         struct ao2_iterator user_iter;
1899
1900         user_iter = ao2_iterator_init(conf->usercontainer, 0);
1901         while ((user = ao2_iterator_next(&user_iter))) {
1902                 if (user == sender) {
1903                         ao2_ref(user, -1);
1904                         continue;
1905                 }
1906                 if (ast_write(user->chan, f) < 0)
1907                         ast_log(LOG_WARNING, "Error writing frame to channel %s\n", user->chan->name);
1908                 ao2_ref(user, -1);
1909         }
1910         ao2_iterator_destroy(&user_iter);
1911 }
1912
1913 static void sla_queue_event_full(enum sla_event_type type, 
1914         struct sla_trunk_ref *trunk_ref, struct sla_station *station, int lock)
1915 {
1916         struct sla_event *event;
1917
1918         if (sla.thread == AST_PTHREADT_NULL) {
1919                 return;
1920         }
1921
1922         if (!(event = ast_calloc(1, sizeof(*event))))
1923                 return;
1924
1925         event->type = type;
1926         event->trunk_ref = trunk_ref;
1927         event->station = station;
1928
1929         if (!lock) {
1930                 AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1931                 return;
1932         }
1933
1934         ast_mutex_lock(&sla.lock);
1935         AST_LIST_INSERT_TAIL(&sla.event_q, event, entry);
1936         ast_cond_signal(&sla.cond);
1937         ast_mutex_unlock(&sla.lock);
1938 }
1939
1940 static void sla_queue_event_nolock(enum sla_event_type type)
1941 {
1942         sla_queue_event_full(type, NULL, NULL, 0);
1943 }
1944
1945 static void sla_queue_event(enum sla_event_type type)
1946 {
1947         sla_queue_event_full(type, NULL, NULL, 1);
1948 }
1949
1950 /*! \brief Queue a SLA event from the conference */
1951 static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *chan,
1952         struct ast_conference *conf)
1953 {
1954         struct sla_station *station;
1955         struct sla_trunk_ref *trunk_ref = NULL;
1956         char *trunk_name;
1957
1958         trunk_name = ast_strdupa(conf->confno);
1959         strsep(&trunk_name, "_");
1960         if (ast_strlen_zero(trunk_name)) {
1961                 ast_log(LOG_ERROR, "Invalid conference name for SLA - '%s'!\n", conf->confno);
1962                 return;
1963         }
1964
1965         AST_RWLIST_RDLOCK(&sla_stations);
1966         AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
1967                 AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
1968                         if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
1969                                 break;
1970                 }
1971                 if (trunk_ref)
1972                         break;
1973         }
1974         AST_RWLIST_UNLOCK(&sla_stations);
1975
1976         if (!trunk_ref) {
1977                 ast_debug(1, "Trunk not found for event!\n");
1978                 return;
1979         }
1980
1981         sla_queue_event_full(type, trunk_ref, station, 1);
1982 }
1983
1984 /*! \brief Decrement reference counts, as incremented by find_conf() */
1985 static int dispose_conf(struct ast_conference *conf)
1986 {
1987         int res = 0;
1988         int confno_int = 0;
1989
1990         AST_LIST_LOCK(&confs);
1991         if (ast_atomic_dec_and_test(&conf->refcount)) {
1992                 /* Take the conference room number out of an inuse state */
1993                 if ((sscanf(conf->confno, "%4d", &confno_int) == 1) && (confno_int >= 0 && confno_int < 1024)) {
1994                         conf_map[confno_int] = 0;
1995                 }
1996                 conf_free(conf);
1997                 res = 1;
1998         }
1999         AST_LIST_UNLOCK(&confs);
2000
2001         return res;
2002 }
2003
2004 static int rt_extend_conf(const char *confno)
2005 {
2006         char currenttime[32];
2007         char endtime[32];
2008         struct timeval now;
2009         struct ast_tm tm;
2010         struct ast_variable *var, *orig_var;
2011         char bookid[51];
2012
2013         if (!extendby) {
2014                 return 0;
2015         }
2016
2017         now = ast_tvnow();
2018
2019         ast_localtime(&now, &tm, NULL);
2020         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2021
2022         var = ast_load_realtime("meetme", "confno",
2023                 confno, "startTime<= ", currenttime,
2024                 "endtime>= ", currenttime, NULL);
2025
2026         orig_var = var;
2027
2028         /* Identify the specific RealTime conference */
2029         while (var) {
2030                 if (!strcasecmp(var->name, "bookid")) {
2031                         ast_copy_string(bookid, var->value, sizeof(bookid));
2032                 }
2033                 if (!strcasecmp(var->name, "endtime")) {
2034                         ast_copy_string(endtime, var->value, sizeof(endtime));
2035                 }
2036
2037                 var = var->next;
2038         }
2039         ast_variables_destroy(orig_var);
2040
2041         ast_strptime(endtime, DATE_FORMAT, &tm);
2042         now = ast_mktime(&tm, NULL);
2043
2044         now.tv_sec += extendby;
2045
2046         ast_localtime(&now, &tm, NULL);
2047         ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2048         strcat(currenttime, "0"); /* Seconds needs to be 00 */
2049
2050         var = ast_load_realtime("meetme", "confno",
2051                 confno, "startTime<= ", currenttime,
2052                 "endtime>= ", currenttime, NULL);
2053
2054         /* If there is no conflict with extending the conference, update the DB */
2055         if (!var) {
2056                 ast_debug(3, "Trying to update the endtime of Conference %s to %s\n", confno, currenttime);
2057                 ast_update_realtime("meetme", "bookid", bookid, "endtime", currenttime, NULL);
2058                 return 0;
2059
2060         }
2061
2062         ast_variables_destroy(var);
2063         return -1;
2064 }
2065
2066 static void conf_start_moh(struct ast_channel *chan, const char *musicclass)
2067 {
2068         char *original_moh;
2069
2070         ast_channel_lock(chan);
2071         original_moh = ast_strdupa(chan->musicclass);
2072         ast_string_field_set(chan, musicclass, musicclass);
2073         ast_channel_unlock(chan);
2074
2075         ast_moh_start(chan, original_moh, NULL);
2076
2077         ast_channel_lock(chan);
2078         ast_string_field_set(chan, musicclass, original_moh);
2079         ast_channel_unlock(chan);
2080 }
2081
2082 static const char *get_announce_filename(enum announcetypes type)
2083 {
2084         switch (type) {
2085         case CONF_HASLEFT:
2086                 return "conf-hasleft";
2087                 break;
2088         case CONF_HASJOIN:
2089                 return "conf-hasjoin";
2090                 break;
2091         default:
2092                 return "";
2093         }
2094 }
2095
2096 static void *announce_thread(void *data)
2097 {
2098         struct announce_listitem *current;
2099         struct ast_conference *conf = data;
2100         int res;
2101         char filename[PATH_MAX] = "";
2102         AST_LIST_HEAD_NOLOCK(, announce_listitem) local_list;
2103         AST_LIST_HEAD_INIT_NOLOCK(&local_list);
2104
2105         while (!conf->announcethread_stop) {
2106                 ast_mutex_lock(&conf->announcelistlock);
2107                 if (conf->announcethread_stop) {
2108                         ast_mutex_unlock(&conf->announcelistlock);
2109                         break;
2110                 }
2111                 if (AST_LIST_EMPTY(&conf->announcelist))
2112                         ast_cond_wait(&conf->announcelist_addition, &conf->announcelistlock);
2113
2114                 AST_LIST_APPEND_LIST(&local_list, &conf->announcelist, entry);
2115                 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2116
2117                 ast_mutex_unlock(&conf->announcelistlock);
2118                 if (conf->announcethread_stop) {
2119                         break;
2120                 }
2121
2122                 for (res = 1; !conf->announcethread_stop && (current = AST_LIST_REMOVE_HEAD(&local_list, entry)); ao2_ref(current, -1)) {
2123                         ast_debug(1, "About to play %s\n", current->namerecloc);
2124                         if (!ast_fileexists(current->namerecloc, NULL, NULL))
2125                                 continue;
2126                         if ((current->confchan) && (current->confusers > 1) && !ast_check_hangup(current->confchan)) {
2127                                 if (!ast_streamfile(current->confchan, current->namerecloc, current->language))
2128                                         res = ast_waitstream(current->confchan, "");
2129                                 if (!res) {
2130                                         ast_copy_string(filename, get_announce_filename(current->announcetype), sizeof(filename));
2131                                         if (!ast_streamfile(current->confchan, filename, current->language))
2132                                                 ast_waitstream(current->confchan, "");
2133                                 }
2134                         }
2135                         if (current->announcetype == CONF_HASLEFT && current->announcetype && !current->vmrec) {
2136                                 /* only remove it if it isn't a VM recording file */
2137                                 ast_filedelete(current->namerecloc, NULL);
2138                         }
2139                 }
2140         }
2141
2142         /* thread marked to stop, clean up */
2143         while ((current = AST_LIST_REMOVE_HEAD(&local_list, entry))) {
2144                 /* only delete if it's a vm rec */
2145                 if (!current->vmrec) {
2146                         ast_filedelete(current->namerecloc, NULL);
2147                 }
2148                 ao2_ref(current, -1);
2149         }
2150         return NULL;
2151 }
2152
2153 static int can_write(struct ast_channel *chan, struct ast_flags64 *confflags)
2154 {
2155         if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2156                 return 1;
2157         }
2158
2159         return (chan->_state == AST_STATE_UP);
2160 }
2161
2162 static void send_talking_event(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking)
2163 {
2164         ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeTalking",
2165               "Channel: %s\r\n"
2166               "Uniqueid: %s\r\n"
2167               "Meetme: %s\r\n"
2168               "Usernum: %d\r\n"
2169               "Status: %s\r\n",
2170               chan->name, chan->uniqueid, conf->confno, user->user_no, talking ? "on" : "off");
2171 }
2172
2173 static void set_user_talking(struct ast_channel *chan, struct ast_conference *conf, struct ast_conf_user *user, int talking, int monitor)
2174 {
2175         int last_talking = user->talking;
2176         if (last_talking == talking)
2177                 return;
2178
2179         user->talking = talking;
2180
2181         if (monitor) {
2182                 /* Check if talking state changed. Take care of -1 which means unmonitored */
2183                 int was_talking = (last_talking > 0);
2184                 int now_talking = (talking > 0);
2185                 if (was_talking != now_talking) {
2186                         send_talking_event(chan, conf, user, now_talking);
2187                 }
2188         }
2189 }
2190
2191 static int user_set_kickme_cb(void *obj, void *check_admin_arg, int flags)
2192 {
2193         struct ast_conf_user *user = obj;
2194         /* actual pointer contents of check_admin_arg is irrelevant */
2195
2196         if (!check_admin_arg || (check_admin_arg && !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN))) {
2197                 user->adminflags |= ADMINFLAG_KICKME;
2198         }
2199         return 0;
2200 }
2201
2202 static int user_set_unmuted_cb(void *obj, void *check_admin_arg, int flags)
2203 {
2204         struct ast_conf_user *user = obj;
2205         /* actual pointer contents of check_admin_arg is irrelevant */
2206
2207         if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2208                 user->adminflags &= ~(ADMINFLAG_MUTED | ADMINFLAG_SELFMUTED | ADMINFLAG_T_REQUEST);
2209         }
2210         return 0;
2211 }
2212
2213 static int user_set_muted_cb(void *obj, void *check_admin_arg, int flags)
2214 {
2215         struct ast_conf_user *user = obj;
2216         /* actual pointer contents of check_admin_arg is irrelevant */
2217
2218         if (!check_admin_arg || !ast_test_flag64(&user->userflags, CONFFLAG_ADMIN)) {
2219                 user->adminflags |= ADMINFLAG_MUTED;
2220         }
2221         return 0;
2222 }
2223
2224 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, struct ast_flags64 *confflags, char *optargs[])
2225 {
2226         struct ast_conf_user *user = NULL;
2227         int fd;
2228         struct dahdi_confinfo dahdic, dahdic_empty;
2229         struct ast_frame *f;
2230         struct ast_channel *c;
2231         struct ast_frame fr;
2232         int outfd;
2233         int ms;
2234         int nfds;
2235         int res;
2236         int retrydahdi;
2237         int origfd;
2238         int musiconhold = 0, mohtempstopped = 0;
2239         int firstpass = 0;
2240         int lastmarked = 0;
2241         int currentmarked = 0;
2242         int ret = -1;
2243         int x;
2244         int menu_active = 0;
2245         int menu8_active = 0;
2246         int talkreq_manager = 0;
2247         int using_pseudo = 0;
2248         int duration = 20;
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_verb(3, "Setting conference duration limit to: %ldms.\n", timelimit);
2335                 if (play_warning) {
2336                         ast_verb(3, "Setting warning time to %ldms from the conference duration limit.\n", play_warning);
2337                 }
2338                 if (warning_freq) {
2339                         ast_verb(3, "Setting warning frequency to %ldms.\n", warning_freq);
2340                 }
2341
2342                 ast_channel_lock(chan);
2343                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_WARNING_FILE"))) {
2344                         var = ast_strdupa(var);
2345                 }
2346                 ast_channel_unlock(chan);
2347
2348                 warning_sound = var ? var : "timeleft";
2349
2350                 ast_channel_lock(chan);
2351                 if ((var = pbx_builtin_getvar_helper(chan, "CONF_LIMIT_TIMEOUT_FILE"))) {
2352                         var = ast_strdupa(var);
2353                 }
2354                 ast_channel_unlock(chan);
2355
2356                 end_sound = var ? var : NULL;
2357
2358                 /* undo effect of S(x) in case they are both used */
2359                 calldurationlimit = 0;
2360                 /* more efficient do it like S(x) does since no advanced opts */
2361                 if (!play_warning && !end_sound && timelimit) {
2362                         calldurationlimit = timelimit / 1000;
2363                         timelimit = play_warning = warning_freq = 0;
2364                 } else {
2365                         ast_debug(2, "Limit Data for this call:\n");
2366                         ast_debug(2, "- timelimit     = %ld\n", timelimit);
2367                         ast_debug(2, "- play_warning  = %ld\n", play_warning);
2368                         ast_debug(2, "- warning_freq  = %ld\n", warning_freq);
2369                         ast_debug(2, "- warning_sound = %s\n", warning_sound ? warning_sound : "UNDEF");
2370                         ast_debug(2, "- end_sound     = %s\n", end_sound ? end_sound : "UNDEF");
2371                 }
2372         }
2373
2374         /* Get exit keys */
2375         if (ast_test_flag64(confflags, CONFFLAG_KEYEXIT)) {
2376                 if (!ast_strlen_zero(optargs[OPT_ARG_EXITKEYS]))
2377                         exitkeys = ast_strdupa(optargs[OPT_ARG_EXITKEYS]);
2378                 else
2379                         exitkeys = ast_strdupa("#"); /* Default */
2380         }
2381         
2382         if (ast_test_flag64(confflags, CONFFLAG_RECORDCONF)) {
2383                 if (!conf->recordingfilename) {
2384                         const char *var;
2385                         ast_channel_lock(chan);
2386                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFILE"))) {
2387                                 conf->recordingfilename = ast_strdup(var);
2388                         }
2389                         if ((var = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT"))) {
2390                                 conf->recordingformat = ast_strdup(var);
2391                         }
2392                         ast_channel_unlock(chan);
2393                         if (!conf->recordingfilename) {
2394                                 snprintf(recordingtmp, sizeof(recordingtmp), "meetme-conf-rec-%s-%s", conf->confno, chan->uniqueid);
2395                                 conf->recordingfilename = ast_strdup(recordingtmp);
2396                         }
2397                         if (!conf->recordingformat) {
2398                                 conf->recordingformat = ast_strdup("wav");
2399                         }
2400                         ast_verb(4, "Starting recording of MeetMe Conference %s into file %s.%s.\n",
2401                                     conf->confno, conf->recordingfilename, conf->recordingformat);
2402                 }
2403         }
2404
2405         ast_mutex_lock(&conf->recordthreadlock);
2406         if ((conf->recordthread == AST_PTHREADT_NULL) && ast_test_flag64(confflags, CONFFLAG_RECORDCONF) &&
2407                 ((conf->lchan = ast_request("DAHDI", cap_slin, chan, "pseudo", NULL)))) {
2408                 ast_set_read_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2409                 ast_set_write_format_by_id(conf->lchan, AST_FORMAT_SLINEAR);
2410                 dahdic.chan = 0;
2411                 dahdic.confno = conf->dahdiconf;
2412                 dahdic.confmode = DAHDI_CONF_CONFANN | DAHDI_CONF_CONFANNMON;
2413                 if (ioctl(conf->lchan->fds[0], DAHDI_SETCONF, &dahdic)) {
2414                         ast_log(LOG_WARNING, "Error starting listen channel\n");
2415                         ast_hangup(conf->lchan);
2416                         conf->lchan = NULL;
2417                 } else {
2418                         ast_pthread_create_detached_background(&conf->recordthread, NULL, recordthread, conf);
2419                 }
2420         }
2421         ast_mutex_unlock(&conf->recordthreadlock);
2422
2423         ast_mutex_lock(&conf->announcethreadlock);
2424         if ((conf->announcethread == AST_PTHREADT_NULL) && !ast_test_flag64(confflags, CONFFLAG_QUIET) &&
2425                 ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC)) {
2426                 ast_mutex_init(&conf->announcelistlock);
2427                 AST_LIST_HEAD_INIT_NOLOCK(&conf->announcelist);
2428                 ast_pthread_create_background(&conf->announcethread, NULL, announce_thread, conf);
2429         }
2430         ast_mutex_unlock(&conf->announcethreadlock);
2431
2432         time(&user->jointime);
2433         
2434         user->timelimit = timelimit;
2435         user->play_warning = play_warning;
2436         user->warning_freq = warning_freq;
2437         user->warning_sound = warning_sound;
2438         user->end_sound = end_sound;    
2439         
2440         if (calldurationlimit > 0) {
2441                 time(&user->kicktime);
2442                 user->kicktime = user->kicktime + calldurationlimit;
2443         }
2444         
2445         if (ast_tvzero(user->start_time))
2446                 user->start_time = ast_tvnow();
2447         time_left_ms = user->timelimit;
2448         
2449         if (user->timelimit) {
2450                 nexteventts = ast_tvadd(user->start_time, ast_samp2tv(user->timelimit, 1000));
2451                 nexteventts = ast_tvsub(nexteventts, ast_samp2tv(user->play_warning, 1000));
2452         }
2453
2454         if (conf->locked && (!ast_test_flag64(confflags, CONFFLAG_ADMIN))) {
2455                 /* Sorry, but this conference is locked! */     
2456                 if (!ast_streamfile(chan, "conf-locked", chan->language))
2457                         ast_waitstream(chan, "");
2458                 goto outrun;
2459         }
2460
2461         ast_mutex_lock(&conf->playlock);
2462
2463         if (rt_schedule && conf->maxusers) {
2464                 if (conf->users >= conf->maxusers) {
2465                         /* Sorry, but this confernce has reached the participant limit! */      
2466                         if (!ast_streamfile(chan, "conf-full", chan->language))
2467                                 ast_waitstream(chan, "");
2468                         ast_mutex_unlock(&conf->playlock);
2469                         goto outrun;
2470                 }
2471         }
2472
2473         ao2_lock(conf->usercontainer);
2474         ao2_callback(conf->usercontainer, OBJ_NODATA, user_max_cmp, &user->user_no);
2475         user->user_no++;
2476         ao2_link(conf->usercontainer, user);
2477         ao2_unlock(conf->usercontainer);
2478
2479         user->chan = chan;
2480         user->userflags = *confflags;
2481         user->adminflags = ast_test_flag64(confflags, CONFFLAG_STARTMUTED) ? ADMINFLAG_SELFMUTED : 0;
2482         user->adminflags |= (conf->gmuted) ? ADMINFLAG_MUTED : 0;
2483         user->talking = -1;
2484
2485         ast_mutex_unlock(&conf->playlock);
2486
2487         if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER | CONFFLAG_INTROUSERNOREVIEW | CONFFLAG_INTROUSER_VMREC))) {
2488                 char destdir[PATH_MAX];
2489
2490                 snprintf(destdir, sizeof(destdir), "%s/meetme", ast_config_AST_SPOOL_DIR);
2491
2492                 if (ast_mkdir(destdir, 0777) != 0) {
2493                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", destdir, strerror(errno));
2494                         goto outrun;
2495                 }
2496
2497                 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2498                         context = ast_strdupa(optargs[OPT_ARG_INTROUSER_VMREC]);
2499                         mailbox = strsep(&context, "@");
2500
2501                         if (ast_strlen_zero(mailbox)) {
2502                                 /* invalid input, clear the v flag*/
2503                                 ast_clear_flag64(confflags,CONFFLAG_INTROUSER_VMREC);
2504                                 ast_log(LOG_WARNING,"You must specify a mailbox in the v() option\n");
2505                         } else {
2506                                 if (ast_strlen_zero(context)) {
2507                                     context = "default";
2508                                 }
2509                                 /* if there is no mailbox we don't need to do this logic  */
2510                                 snprintf(user->namerecloc, sizeof(user->namerecloc),
2511                                          "%s/voicemail/%s/%s/greet",ast_config_AST_SPOOL_DIR,context,mailbox);
2512
2513                                 /* if the greeting doesn't exist then use the temp file method instead, clear flag v */
2514                                 if (!ast_fileexists(user->namerecloc, NULL, NULL)){
2515                                         snprintf(user->namerecloc, sizeof(user->namerecloc),
2516                                                  "%s/meetme-username-%s-%d", destdir,
2517                                                  conf->confno, user->user_no);
2518                                         ast_clear_flag64(confflags, CONFFLAG_INTROUSER_VMREC);
2519                                 }
2520                         }
2521                 } else {
2522                         snprintf(user->namerecloc, sizeof(user->namerecloc),
2523                                  "%s/meetme-username-%s-%d", destdir,
2524                                  conf->confno, user->user_no);
2525                 }
2526
2527                 res = 0;
2528                 if (ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) && !ast_fileexists(user->namerecloc, NULL, NULL))
2529                         res = ast_play_and_record(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE), 0, NULL);
2530                 else if (ast_test_flag64(confflags, CONFFLAG_INTROUSER) && !ast_fileexists(user->namerecloc, NULL, NULL))
2531                         res = ast_record_review(chan, "vm-rec-name", user->namerecloc, 10, "sln", &duration, NULL);
2532                 if (res == -1)
2533                         goto outrun;
2534
2535         }
2536
2537         ast_mutex_lock(&conf->playlock);
2538
2539         if (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER))
2540                 conf->markedusers++;
2541         conf->users++;
2542         if (rt_log_members) {
2543                 /* Update table */
2544                 snprintf(members, sizeof(members), "%d", conf->users);
2545                 ast_realtime_require_field("meetme",
2546                         "confno", strlen(conf->confno) > 7 ? RQ_UINTEGER4 : strlen(conf->confno) > 4 ? RQ_UINTEGER3 : RQ_UINTEGER2, strlen(conf->confno),
2547                         "members", RQ_UINTEGER1, strlen(members),
2548                         NULL);
2549                 ast_update_realtime("meetme", "confno", conf->confno, "members", members, NULL);
2550         }
2551         setusercount = 1;
2552
2553         /* This device changed state now - if this is the first user */
2554         if (conf->users == 1)
2555                 ast_devstate_changed(AST_DEVICE_INUSE, "meetme:%s", conf->confno);
2556
2557         ast_mutex_unlock(&conf->playlock);
2558
2559         /* return the unique ID of the conference */
2560         pbx_builtin_setvar_helper(chan, "MEETMEUNIQUEID", conf->uniqueid);
2561
2562         if (ast_test_flag64(confflags, CONFFLAG_EXIT_CONTEXT)) {
2563                 ast_channel_lock(chan);
2564                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT"))) {
2565                         ast_copy_string(exitcontext, tmpvar, sizeof(exitcontext));
2566                 } else if (!ast_strlen_zero(chan->macrocontext)) {
2567                         ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
2568                 } else {
2569                         ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
2570                 }
2571                 ast_channel_unlock(chan);
2572         }
2573
2574         /* Play an arbitrary intro message */
2575         if (ast_test_flag64(confflags, CONFFLAG_INTROMSG) &&
2576                         !ast_strlen_zero(optargs[OPT_ARG_INTROMSG])) {
2577                 if (!ast_streamfile(chan, optargs[OPT_ARG_INTROMSG], chan->language)) {
2578                         ast_waitstream(chan, "");
2579                 }
2580         }
2581
2582         if (!ast_test_flag64(confflags, (CONFFLAG_QUIET | CONFFLAG_NOONLYPERSON))) {
2583                 if (conf->users == 1 && !ast_test_flag64(confflags, CONFFLAG_WAITMARKED))
2584                         if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
2585                                 ast_waitstream(chan, "");
2586                 if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && conf->markedusers == 0)
2587                         if (!ast_streamfile(chan, "conf-waitforleader", chan->language))
2588                                 ast_waitstream(chan, "");
2589         }
2590
2591         if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && ast_test_flag64(confflags, CONFFLAG_ANNOUNCEUSERCOUNT) &&
2592                 conf->users > 1) {
2593                 int keepplaying = 1;
2594
2595                 if (conf->users == 2) { 
2596                         if (!ast_streamfile(chan, "conf-onlyone", chan->language)) {
2597                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2598                                 ast_stopstream(chan);
2599                                 if (res > 0)
2600                                         keepplaying = 0;
2601                                 else if (res == -1)
2602                                         goto outrun;
2603                         }
2604                 } else { 
2605                         if (!ast_streamfile(chan, "conf-thereare", chan->language)) {
2606                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2607                                 ast_stopstream(chan);
2608                                 if (res > 0)
2609                                         keepplaying = 0;
2610                                 else if (res == -1)
2611                                         goto outrun;
2612                         }
2613                         if (keepplaying) {
2614                                 res = ast_say_number(chan, conf->users - 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2615                                 if (res > 0)
2616                                         keepplaying = 0;
2617                                 else if (res == -1)
2618                                         goto outrun;
2619                         }
2620                         if (keepplaying && !ast_streamfile(chan, "conf-otherinparty", chan->language)) {
2621                                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2622                                 ast_stopstream(chan);
2623                                 if (res > 0)
2624                                         keepplaying = 0;
2625                                 else if (res == -1) 
2626                                         goto outrun;
2627                         }
2628                 }
2629         }
2630
2631         if (!ast_test_flag64(confflags, CONFFLAG_NO_AUDIO_UNTIL_UP)) {
2632                 /* We're leaving this alone until the state gets changed to up */
2633                 ast_indicate(chan, -1);
2634         }
2635
2636         if (ast_set_write_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2637                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
2638                 goto outrun;
2639         }
2640
2641         if (ast_set_read_format_by_id(chan, AST_FORMAT_SLINEAR) < 0) {
2642                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
2643                 goto outrun;
2644         }
2645
2646         /* Reduce background noise from each participant */
2647         if ((mod_speex = ast_module_helper("", "codec_speex", 0, 0, 0, 0))) {
2648                 ast_free(mod_speex);
2649                 ast_func_write(chan, "DENOISE(rx)", "on");
2650         }
2651
2652         retrydahdi = (strcasecmp(chan->tech->type, "DAHDI") || (chan->audiohooks || chan->monitor) ? 1 : 0);
2653         user->dahdichannel = !retrydahdi;
2654
2655  dahdiretry:
2656         origfd = chan->fds[0];
2657         if (retrydahdi) {
2658                 /* open pseudo in non-blocking mode */
2659                 fd = open("/dev/dahdi/pseudo", O_RDWR | O_NONBLOCK);
2660                 if (fd < 0) {
2661                         ast_log(LOG_WARNING, "Unable to open DAHDI pseudo channel: %s\n", strerror(errno));
2662                         goto outrun;
2663                 }
2664                 using_pseudo = 1;
2665                 /* Setup buffering information */
2666                 memset(&bi, 0, sizeof(bi));
2667                 bi.bufsize = CONF_SIZE / 2;
2668                 bi.txbufpolicy = DAHDI_POLICY_IMMEDIATE;
2669                 bi.rxbufpolicy = DAHDI_POLICY_IMMEDIATE;
2670                 bi.numbufs = audio_buffers;
2671                 if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
2672                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
2673                         close(fd);
2674                         goto outrun;
2675                 }
2676                 x = 1;
2677                 if (ioctl(fd, DAHDI_SETLINEAR, &x)) {
2678                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
2679                         close(fd);
2680                         goto outrun;
2681                 }
2682                 nfds = 1;
2683         } else {
2684                 /* XXX Make sure we're not running on a pseudo channel XXX */
2685                 fd = chan->fds[0];
2686                 nfds = 0;
2687         }
2688         memset(&dahdic, 0, sizeof(dahdic));
2689         memset(&dahdic_empty, 0, sizeof(dahdic_empty));
2690         /* Check to see if we're in a conference... */
2691         dahdic.chan = 0;
2692         if (ioctl(fd, DAHDI_GETCONF, &dahdic)) {
2693                 ast_log(LOG_WARNING, "Error getting conference\n");
2694                 close(fd);
2695                 goto outrun;
2696         }
2697         if (dahdic.confmode) {
2698                 /* Whoa, already in a conference...  Retry... */
2699                 if (!retrydahdi) {
2700                         ast_debug(1, "DAHDI channel is in a conference already, retrying with pseudo\n");
2701                         retrydahdi = 1;
2702                         goto dahdiretry;
2703                 }
2704         }
2705         memset(&dahdic, 0, sizeof(dahdic));
2706         /* Add us to the conference */
2707         dahdic.chan = 0;
2708         dahdic.confno = conf->dahdiconf;
2709
2710         if (!ast_test_flag64(confflags, CONFFLAG_QUIET) && (ast_test_flag64(confflags, CONFFLAG_INTROUSER) ||
2711                         ast_test_flag64(confflags, CONFFLAG_INTROUSERNOREVIEW) || ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)) && conf->users > 1) {
2712                 struct announce_listitem *item;
2713                 if (!(item = ao2_alloc(sizeof(*item), NULL)))
2714                         goto outrun;
2715                 ast_copy_string(item->namerecloc, user->namerecloc, sizeof(item->namerecloc));
2716                 ast_copy_string(item->language, chan->language, sizeof(item->language));
2717                 item->confchan = conf->chan;
2718                 item->confusers = conf->users;
2719                 if (ast_test_flag64(confflags, CONFFLAG_INTROUSER_VMREC)){
2720                         item->vmrec = 1;
2721                 }
2722                 item->announcetype = CONF_HASJOIN;
2723                 ast_mutex_lock(&conf->announcelistlock);
2724                 ao2_ref(item, +1); /* add one more so we can determine when announce_thread is done playing it */
2725                 AST_LIST_INSERT_TAIL(&conf->announcelist, item, entry);
2726                 ast_cond_signal(&conf->announcelist_addition);
2727                 ast_mutex_unlock(&conf->announcelistlock);
2728
2729                 while (!ast_check_hangup(conf->chan) && ao2_ref(item, 0) == 2 && !ast_safe_sleep(chan, 1000)) {
2730                         ;
2731                 }
2732                 ao2_ref(item, -1);
2733         }
2734
2735         if (ast_test_flag64(confflags, CONFFLAG_WAITMARKED) && !conf->markedusers)
2736                 dahdic.confmode = DAHDI_CONF_CONF;
2737         else if (ast_test_flag64(confflags, CONFFLAG_MONITOR))
2738                 dahdic.confmode = DAHDI_CONF_CONFMON | DAHDI_CONF_LISTENER;
2739         else if (ast_test_flag64(confflags, CONFFLAG_TALKER))
2740                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER;
2741         else
2742                 dahdic.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
2743
2744         if (ioctl(fd, DAHDI_SETCONF, &dahdic)) {
2745                 ast_log(LOG_WARNING, "Error setting conference\n");
2746                 close(fd);
2747                 goto outrun;
2748         }
2749         ast_debug(1, "Placed channel %s in DAHDI conf %d\n", chan->name, conf->dahdiconf);
2750
2751         if (!sent_event) {
2752                 ast_manager_event(chan, EVENT_FLAG_CALL, "MeetmeJoin",
2753                         "Channel: %s\r\n"
2754                         "Uniqueid: %s\r\n"
2755                         "Meetme: %s\r\n"
2756                         "Usernum: %d\r\n"
2757                         "CallerIDnum: %s\r\n"
2758                         "CallerIDname: %s\r\n",
2759                         chan->name, chan->uniqueid, conf->confno,
2760                         user->user_no,
2761                         S_COR(user->chan->caller.id.number.valid, user->chan->caller.id.number.str, "<unknown>"),
2762                         S_COR(user->chan->caller.id.name.valid, user->chan->caller.id.name.str, "<unknown>")
2763                         );
2764                 sent_event = 1;
2765         }
2766
2767         if (!firstpass && !ast_test_flag64(confflags, CONFFLAG_MONITOR) &&
2768                 !ast_test_flag64(confflags, CONFFLAG_ADMIN)) {
2769                 firstpass = 1;
2770                 if (!ast_test_flag64(confflags, CONFFLAG_QUIET))
2771                         if (!ast_test_flag64(confflags, CONFFLAG_WAITMARKED) || (ast_test_flag64(confflags, CONFFLAG_MARKEDUSER) &&
2772                                 (conf->markedusers >= 1))) {
2773                                 conf_play(chan, conf, ENTER);
2774                         }
2775         }
2776
2777         conf_flush(fd, chan);
2778
2779         if (dsp)
2780                 ast_dsp_free(dsp);
2781
2782         if (!(dsp = ast_dsp_new())) {
2783                 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
2784                 res = -1;
2785         }
2786
2787         if (ast_test_flag64(confflags, CONFFLAG_AGI)) {
2788                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
2789                    or use default filename of conf-background.agi */
2790
2791                 ast_channel_lock(chan);
2792                 if ((tmpvar = pbx_builtin_getvar_helper(chan, "MEETME_AGI_BACKGROUND"))) {
2793                         agifile = ast_strdupa(tmpvar);
2794                 } else {
2795                         agifile = ast_strdupa(agifiledefault);
2796                 }
2797                 ast_channel_unlock(chan);
2798                 
2799                 if (user->dahdichannel) {
2800                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones */
2801                         x = 1;
2802                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2803                 }
2804                 /* Find a pointer to the agi app and execute the script */
2805                 agi_app = pbx_findapp("agi");
2806                 if (agi_app) {
2807                         ret = pbx_exec(chan, agi_app, agifile);
2808                 } else {
2809                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
2810                         ret = -2;
2811                 }
2812                 if (user->dahdichannel) {
2813                         /*  Remove CONFMUTE mode on DAHDI channel */
2814                         x = 0;
2815                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2816                 }
2817         } else {
2818                 if (user->dahdichannel && ast_test_flag64(confflags, CONFFLAG_STARMENU)) {
2819                         /*  Set CONFMUTE mode on DAHDI channel to mute DTMF tones when the menu is enabled */
2820                         x = 1;
2821                         ast_channel_setoption(chan, AST_OPTION_TONE_VERIFY, &x, sizeof(char), 0);
2822                 }       
2823                 for (;;) {
2824                         int menu_was_active = 0;
2825
2826                         outfd = -1;
2827                         ms = -1;
2828                         now = ast_tvnow();
2829
2830                         if (rt_schedule && conf->endtime) {
2831                                 char currenttime[32];
2832                                 long localendtime = 0;
2833                                 int extended = 0;
2834                                 struct ast_tm tm;
2835                                 struct ast_variable *var, *origvar;
2836                                 struct timeval tmp;
2837
2838                                 if (now.tv_sec % 60 == 0) {
2839                                         if (!checked) {
2840                                                 ast_localtime(&now, &tm, NULL);
2841                                                 ast_strftime(currenttime, sizeof(currenttime), DATE_FORMAT, &tm);
2842                                                 var = origvar = ast_load_realtime("meetme", "confno",
2843                                                         conf->confno, "starttime <=", currenttime,
2844                                                          "endtime >=", currenttime, NULL);
2845
2846                                                 for ( ; var; var = var->next) {
2847                                                         if (!strcasecmp(var->name, "endtime")) {
2848                                                                 struct ast_tm endtime_tm;
2849                                                                 ast_strptime(var->value, "%Y-%m-%d %H:%M:%S", &endtime_tm);
2850                                                                 tmp = ast_mktime(&endtime_tm, NULL);
2851                                                                 localendtime = tmp.tv_sec;
2852                                                         }
2853                                                 }
2854                                                 ast_variables_destroy(origvar);
2855
2856                                                 /* A conference can be extended from the
2857                                                    Admin/User menu or by an external source */
2858                                                 if (localendtime > conf->endtime){
2859                                                         conf->endtime = localendtime;
2860                                                         extended = 1;
2861                                                 }
2862
2863                                                 if (conf->endtime && (now.tv_sec >= conf->endtime)) {
2864                                                         ast_verbose("Quitting time...\n");
2865                                                         goto outrun;
2866                                                 }
2867
2868                                                 if (!announcement_played && conf->endalert) {
2869                                                         if (now.tv_sec + conf->endalert >= conf->endtime) {
2870                                                                 if (!ast_streamfile(chan, "conf-will-end-in", chan->language))
2871                                                                         ast_waitstream(chan, "");
2872                                                                 ast_say_digits(chan, (conf->endtime - now.tv_sec) / 60, "", chan->language);
2873                                                                 if (!ast_streamfile(chan, "minutes", chan->language))
2874                                                                         ast_waitstream(chan, "");
2875                                                                 if (musiconhold) {
2876                                                                         conf_start_moh(chan, optargs[OPT_ARG_MOH_CLASS]);
2877                                                                 }
2878                                                                 announcement_played = 1;
2879                                                         }
2880                                                 }
2881
2882                                                 if (extended) {
2883                                                         announcement_played = 0;
2884                                                 }
2885