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