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