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