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