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