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