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