Merge alternate hangup and meetme patches from Matt N.
[asterisk/asterisk.git] / apps / app_meetme.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Meet me conference bridge
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/pbx.h>
19 #include <asterisk/module.h>
20 #include <asterisk/config.h>
21 #include <asterisk/app.h>
22 #include <asterisk/musiconhold.h>
23 #include <asterisk/manager.h>
24 #include <asterisk/options.h>
25 #include <asterisk/cli.h>
26 #include <asterisk/say.h>
27 #include <asterisk/utils.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <sys/ioctl.h>
34
35 #ifdef __linux__
36 #include <linux/zaptel.h>
37 #else
38 #include <zaptel.h>
39 #endif /* __linux__ */
40
41 static char *tdesc = "MeetMe conference bridge";
42
43 static char *app = "MeetMe";
44 static char *app2 = "MeetMeCount";
45 static char *app3 = "MeetMeAdmin";
46
47 static char *synopsis = "MeetMe conference bridge";
48 static char *synopsis2 = "MeetMe participant count";
49 static char *synopsis3 = "MeetMe conference Administration";
50
51 static char *descrip =
52 "  MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
53 "If the conference number is omitted, the user will be prompted to enter\n"
54 "one. \n"
55 "MeetMe returns 0 if user pressed # to exit (see option 'p'), otherwise -1.\n"
56 "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
57
58 "The option string may contain zero or more of the following characters:\n"
59 "      'm' -- set monitor only mode (Listen only, no talking)\n"
60 "      't' -- set talk only mode. (Talk only, no listening)\n"
61 "      'p' -- allow user to exit the conference by pressing '#'\n"
62 "      'd' -- dynamically add conference\n"
63 "      'D' -- dynamically add conference, prompting for a PIN\n"
64 "      'e' -- select an empty conference\n"
65 "      'E' -- select an empty pinless conference\n"
66 "      'v' -- video mode\n"
67 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
68 "      'M' -- enable music on hold when the conference has a single caller\n"
69 "      'x' -- close the conference when last user marked with 'x' exits\n"
70 "      'w' -- wait until a user marked with the 'x' option enters the conference\n"
71 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
72 "         Default: conf-background.agi\n"
73 "        (Note: This does not work with non-Zap channels in the same conference)\n"
74 "      's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
75 "      'a' -- set admin mode\n";
76
77 static char *descrip2 =
78 "  MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
79 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
80 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
81 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
82
83 static char *descrip3 = 
84 "  MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
85 "      'K' -- Kick all users out of conference\n"
86 "      'k' -- Kick one user out of conference\n"
87 "      'L' -- Lock conference\n"
88 "      'l' -- Unlock conference\n"
89 "      'M' -- Mute conference\n"
90 "      'm' -- Unmute conference\n"
91 "";
92
93 STANDARD_LOCAL_USER;
94
95 LOCAL_USER_DECL;
96
97 static struct ast_conference {
98         char confno[AST_MAX_EXTENSION];         /* Conference */
99         struct ast_channel *chan;       /* Announcements channel */
100         int fd;                         /* Announcements fd */
101         int zapconf;                    /* Zaptel Conf # */
102         int users;                      /* Number of active users */
103         int markedusers;                  /* Number of marked users */
104         struct ast_conf_user *firstuser;  /* Pointer to the first user struct */
105         struct ast_conf_user *lastuser;   /* Pointer to the last user struct */
106         time_t start;                   /* Start time (s) */
107         int isdynamic;                  /* Created on the fly? */
108         int locked;                       /* Is the conference locked? */
109         char pin[AST_MAX_EXTENSION];                    /* If protected by a PIN */
110         struct ast_conference *next;
111 } *confs;
112
113 struct ast_conf_user {
114         int user_no;                 /* User Number */
115         struct ast_conf_user *prevuser;  /* Pointer to the previous user */
116         struct ast_conf_user *nextuser;  /* Pointer to the next user */
117         int userflags;                   /* Flags as set in the conference */
118         int adminflags;                  /* Flags set by the Admin */
119         struct ast_channel *chan;        /* Connected channel */
120         char usrvalue[50];               /* Custom User Value */
121         time_t jointime;                 /* Time the user joined the conference */
122 };
123
124 #define ADMINFLAG_MUTED (1 << 1)        /* User is muted */
125 #define ADMINFLAG_KICKME (1 << 2)       /* User is kicked */
126
127
128 AST_MUTEX_DEFINE_STATIC(conflock);
129
130 static int admin_exec(struct ast_channel *chan, void *data);
131
132 #include "enter.h"
133 #include "leave.h"
134
135 #define ENTER   0
136 #define LEAVE   1
137
138 #define CONF_SIZE 320
139
140 #define CONFFLAG_ADMIN  (1 << 1)        /* If set the user has admin access on the conference */
141 #define CONFFLAG_MONITOR (1 << 2)       /* If set the user can only receive audio from the conference */
142 #define CONFFLAG_POUNDEXIT (1 << 3)     /* If set asterisk will exit conference when '#' is pressed */
143 #define CONFFLAG_STARMENU (1 << 4)      /* If set asterisk will provide a menu to the user what '*' is pressed */
144 #define CONFFLAG_TALKER (1 << 5)        /* If set the use can only send audio to the conference */
145 #define CONFFLAG_QUIET (1 << 6)         /* If set there will be no enter or leave sounds */
146 #define CONFFLAG_VIDEO (1 << 7)         /* Set to enable video mode */
147 #define CONFFLAG_AGI (1 << 8)           /* Set to run AGI Script in Background */
148 #define CONFFLAG_MOH (1 << 9)           /* Set to have music on hold when user is alone in conference */
149 #define CONFFLAG_ADMINEXIT (1 << 10)    /* If set the MeetMe will return if all marked with this flag left */
150 #define CONFFLAG_WAITMARKED (1 << 11)           /* If set, the MeetMe will wait until a marked user enters */
151
152
153 static int careful_write(int fd, unsigned char *data, int len)
154 {
155         int res;
156         int x;
157         while(len) {
158                 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
159                 res = ioctl(fd, ZT_IOMUX, &x);
160                 if (res >= 0)
161                         res = write(fd, data, len);
162                 if (res < 1) {
163                         if (errno != EAGAIN) {
164                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
165                                 return -1;
166                         } else
167                                 return 0;
168                 }
169                 len -= res;
170                 data += res;
171         }
172         return 0;
173 }
174
175 static void conf_play(struct ast_conference *conf, int sound)
176 {
177         unsigned char *data;
178         int len;
179         ast_mutex_lock(&conflock);
180         switch(sound) {
181         case ENTER:
182                 data = enter;
183                 len = sizeof(enter);
184                 break;
185         case LEAVE:
186                 data = leave;
187                 len = sizeof(leave);
188                 break;
189         default:
190                 data = NULL;
191                 len = 0;
192         }
193         if (data) 
194                 careful_write(conf->fd, data, len);
195         ast_mutex_unlock(&conflock);
196 }
197
198 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
199 {
200         struct ast_conference *cnf;
201         struct zt_confinfo ztc;
202         ast_mutex_lock(&conflock);
203         cnf = confs;
204         while(cnf) {
205                 if (!strcmp(confno, cnf->confno)) 
206                         break;
207                 cnf = cnf->next;
208         }
209         if (!cnf && (make || dynamic)) {
210                 cnf = malloc(sizeof(struct ast_conference));
211                 if (cnf) {
212                         /* Make a new one */
213                         memset(cnf, 0, sizeof(struct ast_conference));
214                         strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
215                         strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
216                         cnf->markedusers = -1;
217                         cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo");
218                         if (cnf->chan) {
219                                 cnf->fd = cnf->chan->fds[0];    /* for use by conf_play() */
220                         } else {
221                                 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
222                                 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
223                                 if (cnf->fd < 0) {
224                                         ast_log(LOG_WARNING, "Unable to open pseudo device\n");
225                                         free(cnf);
226                                         cnf = NULL;
227                                         goto cnfout;
228                                 }
229                         }
230                         memset(&ztc, 0, sizeof(ztc));
231                         /* Setup a new zap conference */
232                         ztc.chan = 0;
233                         ztc.confno = -1;
234                         ztc.confmode = ZT_CONF_CONFANN;
235                         if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
236                                 ast_log(LOG_WARNING, "Error setting conference\n");
237                                 if (cnf->chan)
238                                         ast_hangup(cnf->chan);
239                                 else
240                                         close(cnf->fd);
241                                 free(cnf);
242                                 cnf = NULL;
243                                 goto cnfout;
244                         }
245                         /* Fill the conference struct */
246                         cnf->start = time(NULL);
247                         cnf->zapconf = ztc.confno;
248                         cnf->isdynamic = dynamic;
249                         cnf->firstuser = NULL;
250                         cnf->lastuser = NULL;
251                         cnf->locked = 0;
252                         if (option_verbose > 2)
253                                 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
254                         cnf->next = confs;
255                         confs = cnf;
256                 } else  
257                         ast_log(LOG_WARNING, "Out of memory\n");
258         }
259 cnfout:
260         ast_mutex_unlock(&conflock);
261         return cnf;
262 }
263
264 static int confs_show(int fd, int argc, char **argv)
265 {
266         ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
267         return RESULT_SUCCESS;
268 }
269
270 static char show_confs_usage[] =
271 "Deprecated! Please use 'meetme' instead.\n";
272
273 static struct ast_cli_entry cli_show_confs = {
274         { "show", "conferences", NULL }, confs_show,
275         "Show status of conferences", show_confs_usage, NULL };
276         
277 static int conf_cmd(int fd, int argc, char **argv) {
278         /* Process the command */
279         struct ast_conference *cnf;
280         struct ast_conf_user *user;
281         int hr, min, sec;
282         int i = 0, total = 0;
283         time_t now;
284         char *header_format = "%-14s %-14s %-10s %-8s  %-8s\n";
285         char *data_format = "%-12.12s   %4.4d         %4.4s       %02d:%02d:%02d  %-8s\n";
286         char cmdline[1024] = "";
287
288         if (argc > 8)
289                 ast_cli(fd, "Invalid Arguments.\n");
290         /* Check for length so no buffer will overflow... */
291         for (i = 0; i < argc; i++) {
292                 if (strlen(argv[i]) > 100)
293                         ast_cli(fd, "Invalid Arguments.\n");
294         }
295         if (argc == 1) {
296                 /* 'MeetMe': List all the conferences */        
297         now = time(NULL);
298                 cnf = confs;
299                 if (!cnf) {
300                 ast_cli(fd, "No active MeetMe conferences.\n");
301                 return RESULT_SUCCESS;
302         }
303         ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
304                 while(cnf) {
305                         if (cnf->markedusers < 0)
306                                 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
307                         else 
308                                 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
309                         hr = (now - cnf->start) / 3600;
310                         min = ((now - cnf->start) % 3600) / 60;
311                         sec = (now - cnf->start) % 60;
312
313                         ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
314
315                         total += cnf->users;    
316                         cnf = cnf->next;
317                 }
318                 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
319                 return RESULT_SUCCESS;
320         }
321         if (argc < 3)
322                 return RESULT_SHOWUSAGE;
323         strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
324         if (strstr(argv[1], "lock")) {  
325                 if (strcmp(argv[1], "lock") == 0) {
326                         /* Lock */
327                         strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
328                 } else {
329                         /* Unlock */
330                         strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
331                 }
332         } else if (strstr(argv[1], "mute")) { 
333                 if (argc < 4)
334                         return RESULT_SHOWUSAGE;
335                 if (strcmp(argv[1], "mute") == 0) {
336                         /* Mute */
337                         strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1); 
338                         strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
339                 } else {
340                         /* Unmute */
341                         strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
342                         strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
343                 }
344         } else if (strcmp(argv[1], "kick") == 0) {
345                 if (argc < 4)
346                         return RESULT_SHOWUSAGE;
347                 if (strcmp(argv[3], "all") == 0) {
348                         /* Kick all */
349                         strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
350                 } else {
351                         /* Kick a single user */
352                         strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
353                         strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
354                 }       
355         } else if(strcmp(argv[1], "list") == 0) {
356                 /* List all the users in a conference */
357                 if (!confs) {
358                         ast_cli(fd, "No active conferences.\n");
359                         return RESULT_SUCCESS;  
360                 }
361                 cnf = confs;
362                 /* Find the right conference */
363                 while(cnf) {
364                         if (strcmp(cnf->confno, argv[2]) == 0)
365                                 break;
366                         if (cnf->next) {
367                                 cnf = cnf->next;        
368                         } else {
369                                 ast_cli(fd, "No such conference: %s.\n",argv[2]);
370                                 return RESULT_SUCCESS;
371                         }
372                 }
373                 /* Show all the users */
374                 user = cnf->firstuser;
375                 while(user) {
376                         ast_cli(fd, "User #: %i  Channel: %s %s %s\n", user->user_no, user->chan->name, (user->userflags & CONFFLAG_ADMIN) ? "(Admin)" : "", (user->userflags & CONFFLAG_MONITOR) ? "(Listen only)" : "" );
377                         user = user->nextuser;
378                 }
379                 return RESULT_SUCCESS;
380         } else 
381                 return RESULT_SHOWUSAGE;
382         ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
383         admin_exec(NULL, cmdline);
384         return 0;
385 }
386
387 static char *complete_confcmd(char *line, char *word, int pos, int state) {
388         #define CONF_COMMANDS 6
389         int which = 0, x = 0;
390         struct ast_conference *cnf = NULL;
391         struct ast_conf_user *usr = NULL;
392         char *confno = NULL;
393         char usrno[50] = "";
394         char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
395         char *myline;
396         
397         if (pos == 1) {
398                 /* Command */
399                 for (x = 0;x < CONF_COMMANDS; x++) {
400                         if (!strncasecmp(cmds[x], word, strlen(word))) {
401                                 if (++which > state) {
402                                         return strdup(cmds[x]);
403                                 }
404                         }
405                 }
406         } else if (pos == 2) {
407                 /* Conference Number */
408                 ast_mutex_lock(&conflock);
409                 cnf = confs;
410                 while(cnf) {
411                         if (!strncasecmp(word, cnf->confno, strlen(word))) {
412                                 if (++which > state)
413                                         break;
414                         }
415                         cnf = cnf->next;
416                 }
417                 ast_mutex_unlock(&conflock);
418                 return cnf ? strdup(cnf->confno) : NULL;
419         } else if (pos == 3) {
420                 /* User Number || Conf Command option*/
421                 if (strstr(line, "mute") || strstr(line, "kick")) {
422                         if ((state == 0) && (strstr(line, "kick")) && !(strncasecmp(word, "all", strlen(word)))) {
423                                 return strdup("all");
424                         }
425                         which++;
426                         ast_mutex_lock(&conflock);
427                         cnf = confs;
428
429                         /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
430                         myline = ast_strdupa(line);
431                         if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
432                                 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
433                                         ;
434                         }
435                         
436                         while(cnf) {
437                                 if (strcmp(confno, cnf->confno) == 0) {
438                                         break;
439                                 }
440                                 cnf = cnf->next;
441                         }
442                         if (cnf) {
443                                 /* Search for the user */
444                                 usr = cnf->firstuser;
445                                 while(usr) {
446                                         snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
447                                         if (!strncasecmp(word, usrno, strlen(word))) {
448                                                 if (++which > state)
449                                                         break;
450                                         }
451                                         usr = usr->nextuser;
452                                 }
453                         }
454                         ast_mutex_unlock(&conflock);
455                         return usr ? strdup(usrno) : NULL;
456                 }
457         }
458         return NULL;
459 }
460         
461 static char conf_usage[] =
462 "Usage: meetme  (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
463 "       Executes a command for the conference or on a conferee\n";
464
465 static struct ast_cli_entry cli_conf = {
466         { "meetme", NULL, NULL }, conf_cmd,
467         "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
468
469 static int confnonzero(void *ptr)
470 {
471         struct ast_conference *conf = ptr;
472         int res;
473         ast_mutex_lock(&conflock);
474         res = (conf->markedusers < 0);
475         ast_mutex_unlock(&conflock);
476         return res;
477 }
478
479 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
480 {
481         struct ast_conference *prev=NULL, *cur;
482         struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
483         int fd;
484         struct zt_confinfo ztc;
485         struct ast_frame *f;
486         struct ast_channel *c;
487         struct ast_frame fr;
488         int outfd;
489         int ms;
490         int nfds;
491         int res;
492         int flags;
493         int retryzap;
494         int origfd;
495         int musiconhold = 0;
496         int firstpass = 0;
497         int origquiet;
498         int ret = -1;
499         int x;
500         int menu_active = 0;
501         int using_pseudo = 0;
502
503         struct ast_app *app;
504         char *agifile;
505         char *agifiledefault = "conf-background.agi";
506         char meetmesecs[30] = "";
507
508         ZT_BUFFERINFO bi;
509         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
510         char *buf = __buf + AST_FRIENDLY_OFFSET;
511         
512         if (!user) {
513                 ast_log(LOG_ERROR, "Out of memory\n");
514                 return(ret);
515         }
516         memset(user, 0, sizeof(struct ast_conf_user));
517
518         user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
519         
520         if (conf->locked) {
521                 /* Sorry, but this confernce is locked! */      
522                 if (!ast_streamfile(chan, "conf-locked", chan->language))
523                         ast_waitstream(chan, "");
524                 goto outrun;
525         }
526         conf->users++;
527         if (confflags & CONFFLAG_ADMINEXIT) {
528                 if (conf->markedusers == -1) {
529                         conf->markedusers = 1;
530                 } else {
531                         conf->markedusers++;
532                 }
533         }
534       
535         ast_mutex_lock(&conflock);
536         if (conf->firstuser == NULL) {
537                 /* Fill the first new User struct */
538                 user->user_no = 1;
539                 user->nextuser = NULL;
540                 user->prevuser = NULL;
541                 conf->firstuser = user;
542                 conf->lastuser = user;
543         } else {
544                 /* Fill the new user struct */  
545                 user->user_no = conf->lastuser->user_no + 1; 
546                 user->prevuser = conf->lastuser;
547                 user->nextuser = NULL;
548                 if (conf->lastuser->nextuser != NULL) {
549                         ast_log(LOG_WARNING, "Error in User Management!\n");
550                         ast_mutex_unlock(&conflock);
551                         goto outrun;
552                 } else {
553                         conf->lastuser->nextuser = user;
554                         conf->lastuser = user;
555                 }
556         }
557         strncpy(user->usrvalue, "test", sizeof(user->usrvalue) - 1);
558         user->chan = chan;
559         user->userflags = confflags;
560         user->adminflags = 0;
561         ast_mutex_unlock(&conflock);
562         origquiet = confflags & CONFFLAG_QUIET;
563         while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers < 0)) {
564                 confflags &= ~CONFFLAG_QUIET;
565                 confflags |= origquiet;
566                 /* XXX Announce that we're waiting on the conference lead to join */
567                 if (!(confflags & CONFFLAG_QUIET)) {
568                         res = ast_streamfile(chan, "vm-dialout", chan->language);
569                         if (!res)
570                                 res = ast_waitstream(chan, "");
571                 } else
572                         res = 0;
573                 /* If we're waiting with hold music, set to silent mode */
574                 if (!res) {
575                         confflags |= CONFFLAG_QUIET;
576                         ast_moh_start(chan, NULL);
577                         res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
578                         ast_moh_stop(chan);
579                 }
580                 if (res < 0) {
581                         ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
582                         goto outrun;
583                 }
584         }
585         
586         if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
587                 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
588                         if (ast_waitstream(chan, "") < 0)
589                                 goto outrun;
590                 } else
591                         goto outrun;
592         }
593
594         /* Set it into linear mode (write) */
595         if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
596                 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
597                 goto outrun;
598         }
599
600         /* Set it into linear mode (read) */
601         if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
602                 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
603                 goto outrun;
604         }
605         ast_indicate(chan, -1);
606         retryzap = strcasecmp(chan->type, "Zap");
607 zapretry:
608         origfd = chan->fds[0];
609         if (retryzap) {
610                 fd = open("/dev/zap/pseudo", O_RDWR);
611                 if (fd < 0) {
612                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
613                         goto outrun;
614                 }
615                 using_pseudo = 1;
616                 /* Make non-blocking */
617                 flags = fcntl(fd, F_GETFL);
618                 if (flags < 0) {
619                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
620                         close(fd);
621                         goto outrun;
622                 }
623                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
624                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
625                         close(fd);
626                         goto outrun;
627                 }
628                 /* Setup buffering information */
629                 memset(&bi, 0, sizeof(bi));
630                 bi.bufsize = CONF_SIZE/2;
631                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
632                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
633                 bi.numbufs = 4;
634                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
635                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
636                         close(fd);
637                         goto outrun;
638                 }
639                 x = 1;
640                 if (ioctl(fd, ZT_SETLINEAR, &x)) {
641                         ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
642                         close(fd);
643                         goto outrun;
644                 }
645                 nfds = 1;
646         } else {
647                 /* XXX Make sure we're not running on a pseudo channel XXX */
648                 fd = chan->fds[0];
649                 nfds = 0;
650         }
651         memset(&ztc, 0, sizeof(ztc));
652         /* Check to see if we're in a conference... */
653         ztc.chan = 0;   
654         if (ioctl(fd, ZT_GETCONF, &ztc)) {
655                 ast_log(LOG_WARNING, "Error getting conference\n");
656                 close(fd);
657                 goto outrun;
658         }
659         if (ztc.confmode) {
660                 /* Whoa, already in a conference...  Retry... */
661                 if (!retryzap) {
662                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
663                         retryzap = 1;
664                         goto zapretry;
665                 }
666         }
667         memset(&ztc, 0, sizeof(ztc));
668         /* Add us to the conference */
669         ztc.chan = 0;   
670         ztc.confno = conf->zapconf;
671         if (confflags & CONFFLAG_MONITOR)
672                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
673         else if (confflags & CONFFLAG_TALKER)
674                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
675         else 
676                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
677
678         if (ioctl(fd, ZT_SETCONF, &ztc)) {
679                 ast_log(LOG_WARNING, "Error setting conference\n");
680                 close(fd);
681                 goto outrun;
682         }
683         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
684
685         manager_event(EVENT_FLAG_CALL, "MeetmeJoin", 
686                         "Channel: %s\r\n"
687                         "Uniqueid: %s\r\n"
688                         "Meetme: %s\r\n",
689                         chan->name, chan->uniqueid, conf->confno);
690
691         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
692                 firstpass = 1;
693                 if (!(confflags & CONFFLAG_QUIET))
694                         conf_play(conf, ENTER);
695         }
696
697         if (confflags & CONFFLAG_AGI) {
698
699                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
700                   or use default filename of conf-background.agi */
701
702                 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
703                 if (!agifile)
704                         agifile = agifiledefault;
705
706                 if (!strcasecmp(chan->type,"Zap")) {
707                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
708                         x = 1;
709                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
710                 }
711                 /* Find a pointer to the agi app and execute the script */
712                 app = pbx_findapp("agi");
713                 if (app) {
714                         ret = pbx_exec(chan, app, agifile, 1);
715                 } else {
716                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
717                         ret = -2;
718                 }
719                 if (!strcasecmp(chan->type,"Zap")) {
720                         /*  Remove CONFMUTE mode on Zap channel */
721                         x = 0;
722                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
723                 }
724         } else {
725                 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
726                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
727                         x = 1;
728                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
729                 }       
730                 for(;;) {
731                         outfd = -1;
732                         ms = -1;
733                         c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
734                         
735                         /* Update the struct with the actual confflags */
736                         user->userflags = confflags;
737                         
738                         /* trying to add moh for single person conf */
739                         if (confflags & CONFFLAG_MOH) {
740                                 if (conf->users == 1) {
741                                         if (musiconhold == 0) {
742                                                 ast_moh_start(chan, NULL);
743                                                 musiconhold = 1;
744                                         } 
745                                 } else {
746                                         if (musiconhold) {
747                                                 ast_moh_stop(chan);
748                                                 musiconhold = 0;
749                                         }
750                                 }
751                         }
752                         
753                         /* Leave if the last marked user left */
754                         if (conf->markedusers == 0) {
755                                 ret = -1;
756                                 break;
757                         }
758         
759                         /* Check if the admin changed my modes */
760                         if (user->adminflags) {                 
761                                 /* Set the new modes */
762                                 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
763                                         ztc.confmode ^= ZT_CONF_TALKER;
764                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
765                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
766                                                 ret = -1;
767                                                 break;
768                                         }
769                                 }
770                                 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
771                                         ztc.confmode |= ZT_CONF_TALKER;
772                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
773                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
774                                                 ret = -1;
775                                                 break;
776                                         }
777                                 }
778                                 if (user->adminflags & ADMINFLAG_KICKME) {
779                                         //You have been kicked.
780                                         if (!ast_streamfile(chan, "conf-kicked", chan->language))
781                                                 ast_waitstream(chan, "");
782                                         ret = 0;
783                                         break;
784                                 }
785                         } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
786                                 ztc.confmode |= ZT_CONF_TALKER;
787                                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
788                                         ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
789                                         ret = -1;
790                                         break;
791                                 }
792                         }
793
794                         if (c) {
795                                 if (c->fds[0] != origfd) {
796                                         if (using_pseudo) {
797                                                 /* Kill old pseudo */
798                                                 close(fd);
799                                         }
800                                         ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
801                                         retryzap = 0;
802                                         using_pseudo = 0;
803                                         goto zapretry;
804                                 }
805                                 f = ast_read(c);
806                                 if (!f) 
807                                         break;
808                                 if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
809                                         ret = 0;
810                                         break;
811                                 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
812                                         if (musiconhold) {
813                                                 ast_moh_stop(chan);
814                                         }
815                                         if ((confflags & CONFFLAG_ADMIN)) {
816                                                 /* Admin menu */
817                                                 if (!menu_active) {
818                                                         menu_active = 1;
819                                                         /* Record this sound! */
820                                                         if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
821                                                                 ast_waitstream(chan, "");
822                                                 } else {
823                                                         switch(f->subclass - 48) {
824                                                                 case 1: /* Un/Mute */
825                                                                         menu_active = 0;
826                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
827                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
828                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
829                                                                         } else {
830                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
831                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
832                                                                         }
833                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
834                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
835                                                                                 ret = -1;
836                                                                                 break;
837                                                                         }
838                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
839                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
840                                                                                         ast_waitstream(chan, "");
841                                                                         } else {
842                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
843                                                                                         ast_waitstream(chan, "");
844                                                                         }
845                                                                         break;
846                                                                 case 2: /* Un/Lock the Conference */
847                                                                         menu_active = 0;
848                                                                         if (conf->locked) {
849                                                                                 conf->locked = 0;
850                                                                                 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
851                                                                                         ast_waitstream(chan, "");
852                                                                         } else {
853                                                                                 conf->locked = 1;
854                                                                                 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
855                                                                                         ast_waitstream(chan, "");
856                                                                         }
857                                                                         break;
858                                                                 default:
859                                                                         menu_active = 0;
860                                                                         /* Play an error message! */
861                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
862                                                                                 ast_waitstream(chan, "");
863                                                                         break;
864                                                         }
865                                                 }
866                                         } else {
867                                                 /* User menu */
868                                                 if (!menu_active) {
869                                                         menu_active = 1;
870                                                         /* Record this sound! */
871                                                         if (!ast_streamfile(chan, "conf-usermenu", chan->language))
872                                                                 ast_waitstream(chan, "");
873                                                 } else {
874                                                         switch(f->subclass - 48) {
875                                                                 case 1: /* Un/Mute */
876                                                                         menu_active = 0;
877                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
878                                                                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
879                                                                         confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
880                                                                         } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
881                                                                                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
882                                                                                 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
883                                                                         }
884                                                                         if (ioctl(fd, ZT_SETCONF, &ztc)) {
885                                                                                 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
886                                                                                 ret = -1;
887                                                                                 break;
888                                                                         }
889                                                                         if (ztc.confmode & ZT_CONF_TALKER) {
890                                                                                 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
891                                                                                         ast_waitstream(chan, "");
892                                                                         } else {
893                                                                                 if (!ast_streamfile(chan, "conf-muted", chan->language))
894                                                                                         ast_waitstream(chan, "");
895                                                                         }
896                                                                         break;
897                                                                 default:
898                                                                         menu_active = 0;
899                                                                         /* Play an error message! */
900                                                                         if (!ast_streamfile(chan, "conf-errormenu", chan->language))
901                                                                                 ast_waitstream(chan, "");
902                                                                         break;
903                                                         }
904                                                 }
905                                         }
906                                         if (musiconhold) {
907                                                 ast_moh_start(chan, NULL);
908                                         }
909                                 } else if (using_pseudo) {
910                                         if (f->frametype == AST_FRAME_VOICE) {
911                                                 if (f->subclass == AST_FORMAT_SLINEAR) {
912                                                         /* Carefully write */
913                                                         careful_write(fd, f->data, f->datalen);
914                                                 } else
915                                                         ast_log(LOG_WARNING, "Huh?  Got a non-linear (%d) frame in the conference\n", f->subclass);
916                                         }
917                                 }
918                                 ast_frfree(f);
919                         } else if (outfd > -1) {
920                                 res = read(outfd, buf, CONF_SIZE);
921                                 if (res > 0) {
922                                         memset(&fr, 0, sizeof(fr));
923                                         fr.frametype = AST_FRAME_VOICE;
924                                         fr.subclass = AST_FORMAT_SLINEAR;
925                                         fr.datalen = res;
926                                         fr.samples = res/2;
927                                         fr.data = buf;
928                                         fr.offset = AST_FRIENDLY_OFFSET;
929                                         if (ast_write(chan, &fr) < 0) {
930                                                 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
931                                                 /* break; */
932                                         }
933                                 } else 
934                                         ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
935                         }
936                 }
937         }
938         if (using_pseudo)
939                 close(fd);
940         else {
941                 /* Take out of conference */
942                 /* Add us to the conference */
943                 ztc.chan = 0;   
944                 ztc.confno = 0;
945                 ztc.confmode = 0;
946                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
947                         ast_log(LOG_WARNING, "Error setting conference\n");
948                 }
949         }
950         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
951                 conf_play(conf, LEAVE);
952
953 outrun:
954         ast_mutex_lock(&conflock);
955         if (user->user_no) { /* Only cleanup users who really joined! */
956                 manager_event(EVENT_FLAG_CALL, "MeetmeLeave", 
957                         "Channel: %s\r\n"
958                         "Uniqueid: %s\r\n"
959                         "Meetme: %s\r\n",
960                         chan->name, chan->uniqueid, conf->confno);
961                 prev = NULL;
962                 conf->users--;
963                 if (confflags & CONFFLAG_ADMINEXIT) 
964                         conf->markedusers--;
965                 cur = confs;
966                 if (!conf->users) {
967                         /* No more users -- close this one out */
968                         while(cur) {
969                                 if (cur == conf) {
970                                         if (prev)
971                                                 prev->next = conf->next;
972                                         else
973                                                 confs = conf->next;
974                                         break;
975                                 }
976                                 prev = cur;
977                                 cur = cur->next;
978                         }
979                         if (!cur) 
980                                 ast_log(LOG_WARNING, "Conference not found\n");
981                         if (conf->chan)
982                                 ast_hangup(conf->chan);
983                         else
984                                 close(conf->fd);
985                         free(conf);
986                 } else {
987                         /* Remove the user struct */ 
988                         if (user == conf->firstuser) {
989                                 if (user->nextuser) {
990                                         /* There is another entry */
991                                         user->nextuser->prevuser = NULL;
992                                 } else {
993                                         /* We are the only entry */
994                                         conf->lastuser = NULL;
995                                 }
996                                 /* In either case */
997                                 conf->firstuser = user->nextuser;
998                         } else if (user == conf->lastuser){
999                                 if (user->prevuser)
1000                                         user->prevuser->nextuser = NULL;
1001                                 else
1002                                         ast_log(LOG_ERROR, "Bad bad bad!  We're the last, not the first, but nobody before us??\n");
1003                                 conf->lastuser = user->prevuser;
1004                         } else {
1005                                 if (user->nextuser)
1006                                         user->nextuser->prevuser = user->prevuser;
1007                                 else
1008                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1009                                 if (user->prevuser)
1010                                         user->prevuser->nextuser = user->nextuser;
1011                                 else
1012                                         ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1013                         }
1014                         /* Return the number of seconds the user was in the conf */
1015                         snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (user->jointime - time(NULL)));
1016                         pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1017                 }
1018         }
1019         free(user);
1020         ast_mutex_unlock(&conflock);
1021         return ret;
1022 }
1023
1024 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1025 {
1026         struct ast_config *cfg;
1027         struct ast_variable *var;
1028         struct ast_conference *cnf;
1029
1030         /* Check first in the conference list */
1031         ast_mutex_lock(&conflock);
1032         cnf = confs;
1033         while (cnf) {
1034                 if (!strcmp(confno, cnf->confno)) 
1035                         break;
1036                 cnf = cnf->next;
1037         }
1038         ast_mutex_unlock(&conflock);
1039
1040         if (!cnf) {
1041                 if (dynamic) {
1042                         /* No need to parse meetme.conf */
1043                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1044                         if (dynamic_pin) {
1045                                 if (dynamic_pin[0] == 'q') {
1046                                         /* Query the user to enter a PIN */
1047                                         ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1048                                 }
1049                                 cnf = build_conf(confno, dynamic_pin, make, dynamic);
1050                         } else {
1051                                 cnf = build_conf(confno, "", make, dynamic);
1052                         }
1053                 } else {
1054                         /* Check the config */
1055                         cfg = ast_load("meetme.conf");
1056                         if (!cfg) {
1057                                 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1058                                 return NULL;
1059                         }
1060                         var = ast_variable_browse(cfg, "rooms");
1061                         while(var) {
1062                                 if (!strcasecmp(var->name, "conf")) {
1063                                         /* Separate the PIN */
1064                                         char *pin, *conf;
1065
1066                                         if ((pin = ast_strdupa(var->value))) {
1067                                                 conf = strsep(&pin, "|,");
1068                                                 if (!strcasecmp(conf, confno)) {
1069                                                         /* Bingo it's a valid conference */
1070                                                         if (pin)
1071                                                                 cnf = build_conf(confno, pin, make, dynamic);
1072                                                         else
1073                                                                 cnf = build_conf(confno, "", make, dynamic);
1074                                                         break;
1075                                                 }
1076                                         }
1077                                 }
1078                                 var = var->next;
1079                         }
1080                         if (!var) {
1081                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1082                         }
1083                         ast_destroy(cfg);
1084                 }
1085         }
1086         return cnf;
1087 }
1088
1089 /*--- count_exec: The MeetmeCount application */
1090 static int count_exec(struct ast_channel *chan, void *data)
1091 {
1092         struct localuser *u;
1093         int res = 0;
1094         struct ast_conference *conf;
1095         int count;
1096         char *confnum, *localdata;
1097         char val[80] = "0"; 
1098
1099         if (!data || ast_strlen_zero(data)) {
1100                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1101                 return -1;
1102         }
1103         localdata = ast_strdupa(data);
1104         LOCAL_USER_ADD(u);
1105         confnum = strsep(&localdata,"|");       
1106         conf = find_conf(chan, confnum, 0, 0, NULL);
1107         if (conf)
1108                 count = conf->users;
1109         else
1110                 count = 0;
1111
1112         if (localdata && !ast_strlen_zero(localdata)){
1113                 /* have var so load it and exit */
1114                 snprintf(val,sizeof(val), "%i",count);
1115                 pbx_builtin_setvar_helper(chan, localdata,val);
1116         } else {
1117                 if (chan->_state != AST_STATE_UP)
1118                         ast_answer(chan);
1119                 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1120         }
1121         LOCAL_USER_REMOVE(u);
1122         return res;
1123 }
1124
1125 /*--- conf_exec: The meetme() application */
1126 static int conf_exec(struct ast_channel *chan, void *data)
1127 {
1128         int res=-1;
1129         struct localuser *u;
1130         char confno[AST_MAX_EXTENSION] = "";
1131         int allowretry = 0;
1132         int retrycnt = 0;
1133         struct ast_conference *cnf;
1134         int confflags = 0;
1135         int dynamic = 0;
1136         int empty = 0, empty_no_pin = 0;
1137         char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1138
1139         if (!data || ast_strlen_zero(data)) {
1140                 allowretry = 1;
1141                 notdata = "";
1142         } else {
1143                 notdata = data;
1144         }
1145         LOCAL_USER_ADD(u);
1146         if (chan->_state != AST_STATE_UP)
1147                 ast_answer(chan);
1148
1149         info = ast_strdupa((char *)notdata);
1150
1151         if (info) {
1152                 char *tmp = strsep(&info, "|");
1153                 strncpy(confno, tmp, sizeof(confno) - 1);
1154                 if (ast_strlen_zero(confno)) {
1155                         allowretry = 1;
1156                 }
1157         }
1158         if (info)
1159                 inflags = strsep(&info, "|");
1160         if (info)
1161                 inpin = strsep(&info, "|");
1162         if (inpin)
1163                 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1164
1165         if (inflags) {
1166                 if (strchr(inflags, 'a'))
1167                         confflags |= CONFFLAG_ADMIN;
1168                 if (strchr(inflags, 'm'))
1169                         confflags |= CONFFLAG_MONITOR;
1170                 if (strchr(inflags, 'p'))
1171                         confflags |= CONFFLAG_POUNDEXIT;
1172                 if (strchr(inflags, 's'))
1173                         confflags |= CONFFLAG_STARMENU;
1174                 if (strchr(inflags, 't'))
1175                         confflags |= CONFFLAG_TALKER;
1176                 if (strchr(inflags, 'q'))
1177                         confflags |= CONFFLAG_QUIET;
1178                 if (strchr(inflags, 'M'))
1179                         confflags |= CONFFLAG_MOH;
1180                 if (strchr(inflags, 'x'))
1181                         confflags |= CONFFLAG_ADMINEXIT;
1182                 if (strchr(inflags, 'b'))
1183                         confflags |= CONFFLAG_AGI;
1184                 if (strchr(inflags, 'w'))
1185                         confflags |= CONFFLAG_WAITMARKED;
1186                 if (strchr(inflags, 'd'))
1187                         dynamic = 1;
1188                 if (strchr(inflags, 'D')) {
1189                         dynamic = 1;
1190                         if (! inpin) {
1191                                 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1192                         }
1193                 }
1194                 if (strchr(inflags, 'e'))
1195                         empty = 1;
1196                 if (strchr(inflags, 'E')) {
1197                         empty = 1;
1198                         empty_no_pin = 1;
1199                 }
1200         }
1201
1202         do {
1203                 if (retrycnt > 3)
1204                         allowretry = 0;
1205                 if (empty) {
1206                         int i, map[1024];
1207                         struct ast_config *cfg;
1208                         struct ast_variable *var;
1209                         int confno_int;
1210
1211                         memset(map, 0, sizeof(map));
1212
1213                         ast_mutex_lock(&conflock);
1214                         cnf = confs;
1215                         while (cnf) {
1216                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1217                                         /* Disqualify in use conference */
1218                                         if (confno_int >= 0 && confno_int < 1024)
1219                                                 map[confno_int]++;
1220                                 }
1221                                 cnf = cnf->next;
1222                         }
1223                         ast_mutex_unlock(&conflock);
1224
1225                         /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1226                         if ((empty_no_pin) || (!dynamic)) {
1227                                 cfg = ast_load("meetme.conf");
1228                                 if (cfg) {
1229                                         var = ast_variable_browse(cfg, "rooms");
1230                                         while(var) {
1231                                                 if (!strcasecmp(var->name, "conf")) {
1232                                                         char *stringp = ast_strdupa(var->value);
1233                                                         if (stringp) {
1234                                                                 char *confno_tmp = strsep(&stringp, "|,");
1235                                                                 int found = 0;
1236                                                                 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1237                                                                         if ((confno_int >= 0) && (confno_int < 1024)) {
1238                                                                                 if (stringp && empty_no_pin) {
1239                                                                                         map[confno_int]++;
1240                                                                                 }
1241                                                                         }
1242                                                                 }
1243                                                                 if (! dynamic) {
1244                                                                         /* For static:  run through the list and see if this conference is empty */
1245                                                                         ast_mutex_lock(&conflock);
1246                                                                         cnf = confs;
1247                                                                         while (cnf) {
1248                                                                                 if (!strcmp(confno_tmp, cnf->confno)) {
1249                                                                                         /* The conference exists, therefore it's not empty */
1250                                                                                         found = 1;
1251                                                                                         break;
1252                                                                                 }
1253                                                                                 cnf = cnf->next;
1254                                                                         }
1255                                                                         ast_mutex_unlock(&conflock);
1256                                                                         if (!found) {
1257                                                                                 /* At this point, we have a confno_tmp (static conference) that is empty */
1258                                                                                 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1259                                                                                 /* Case 1:  empty_no_pin and pin is nonexistant (NULL)
1260                                                                                  * Case 2:  empty_no_pin and pin is blank (but not NULL)
1261                                                                                  * Case 3:  not empty_no_pin
1262                                                                                  */
1263                                                                                         strncpy(confno, confno_tmp, sizeof(confno) - 1);
1264                                                                                         break;
1265                                                                                         /* XXX the map is not complete (but we do have a confno) */
1266                                                                                 }
1267                                                                         }
1268                                                                 }
1269                                                         } else {
1270                                                                 ast_log(LOG_ERROR, "Out of memory\n");
1271                                                         }
1272                                                 }
1273                                                 var = var->next;
1274                                         }
1275                                         ast_destroy(cfg);
1276                                 }
1277                         }
1278                         /* Select first conference number not in use */
1279                         if (ast_strlen_zero(confno) && dynamic) {
1280                                 for (i=0;i<1024;i++) {
1281                                         if (!map[i]) {
1282                                                 snprintf(confno, sizeof(confno), "%d", i);
1283                                                 break;
1284                                         }
1285                                 }
1286                         }
1287
1288                         /* Not found? */
1289                         if (ast_strlen_zero(confno)) {
1290                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
1291                                 if (!res)
1292                                         ast_waitstream(chan, "");
1293                         } else {
1294                                 if (sscanf(confno, "%d", &confno_int) == 1) {
1295                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
1296                                         if (!res) {
1297                                                 ast_waitstream(chan, "");
1298                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
1299                                         }
1300                                 } else {
1301                                         ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1302                                 }
1303                         }
1304                 }
1305                 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1306                         /* Prompt user for conference number */
1307                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1308                         if (res < 0) {
1309                                 /* Don't try to validate when we catch an error */
1310                                 confno[0] = '\0';
1311                                 allowretry = 0;
1312                                 break;
1313                         }
1314                 }
1315                 if (!ast_strlen_zero(confno)) {
1316                         /* Check the validity of the conference */
1317                         cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1318                         if (!cnf) {
1319                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
1320                                 if (!res)
1321                                         ast_waitstream(chan, "");
1322                                 res = -1;
1323                                 if (allowretry)
1324                                         confno[0] = '\0';
1325                         } else {
1326                                 if (!ast_strlen_zero(cnf->pin)) {
1327                                         char pin[AST_MAX_EXTENSION]="";
1328                                         int j;
1329
1330                                         /* Allow the pin to be retried up to 3 times */
1331                                         for (j=0; j<3; j++) {
1332                                                 if (*the_pin) {
1333                                                         strncpy(pin, the_pin, sizeof(pin) - 1);
1334                                                         res = 0;
1335                                                 } else {
1336                                                         /* Prompt user for pin if pin is required */
1337                                                         res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1338                                                 }
1339                                                 if (res >= 0) {
1340                                                         if (!strcasecmp(pin, cnf->pin)) {
1341                                                                 /* Pin correct */
1342                                                                 allowretry = 0;
1343                                                                 /* Run the conference */
1344                                                                 res = conf_run(chan, cnf, confflags);
1345                                                                 break;
1346                                                         } else {
1347                                                                 /* Pin invalid */
1348                                                                 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1349                                                                 if (!res)
1350                                                                         ast_waitstream(chan, AST_DIGIT_ANY);
1351                                                                 if (res < 0)
1352                                                                         break;
1353                                                                 pin[0] = res;
1354                                                                 pin[1] = '\0';
1355                                                                 res = -1;
1356                                                                 if (allowretry)
1357                                                                         confno[0] = '\0';
1358                                                         }
1359                                                 } else {
1360                                                         res = -1;
1361                                                         allowretry = 0;
1362                                                         break;
1363                                                 }
1364
1365                                                 /* Don't retry pin with a static pin */
1366                                                 if (*the_pin) {
1367                                                         break;
1368                                                 }
1369                                         }
1370                                 } else {
1371                                         /* No pin required */
1372                                         allowretry = 0;
1373
1374                                         /* Run the conference */
1375                                         res = conf_run(chan, cnf, confflags);
1376                                 }
1377                         }
1378                 }
1379         } while (allowretry);
1380         /* Do the conference */
1381         LOCAL_USER_REMOVE(u);
1382         return res;
1383 }
1384
1385 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1386         struct ast_conf_user *user = NULL;
1387         char usrno[1024] = "";
1388         if (conf && callerident) {
1389                 user = conf->firstuser;
1390                 while(user) {
1391                         snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1392                         if (strcmp(usrno, callerident) == 0)
1393                                 return user;
1394                         user = user->nextuser;
1395                 }
1396         }
1397         return NULL;
1398 }
1399
1400 /*--- admin_exec: The MeetMeadmin application */
1401 /* MeetMeAdmin(confno, command, caller) */
1402 static int admin_exec(struct ast_channel *chan, void *data) {
1403         char *params, *command = NULL, *caller = NULL, *conf = NULL;
1404         struct ast_conference *cnf;
1405         struct ast_conf_user *user = NULL;
1406
1407         ast_mutex_lock(&conflock);
1408         /* The param has the conference number the user and the command to execute */
1409         if (data && !ast_strlen_zero(data)) {           
1410                 params = ast_strdupa((char *) data);
1411                 conf = strsep(&params, "|");
1412                 command = strsep(&params, "|");
1413                 caller = strsep(&params, "|");
1414                 
1415                 ast_mutex_lock(&conflock);
1416                 cnf = confs;
1417                 while (cnf) {
1418                         if (strcmp(cnf->confno, conf) == 0) 
1419                                 break;
1420                         cnf = cnf->next;
1421                 }
1422                 ast_mutex_unlock(&conflock);
1423                 
1424                 if (caller)
1425                         user = find_user(cnf, caller);
1426                 
1427                 if (cnf) {
1428                         switch((int) (*command)) {
1429                                 case 76: /* L: Lock */ 
1430                                         cnf->locked = 1;
1431                                         break;
1432                                 case 108: /* l: Unlock */ 
1433                                         cnf->locked = 0;
1434                                         break;
1435                                 case 75: /* K: kick all users*/
1436                                         user = cnf->firstuser;
1437                                         while(user) {
1438                                                 user->adminflags |= ADMINFLAG_KICKME;
1439                                                 if (user->nextuser) {
1440                                                         user = user->nextuser;
1441                                                 } else {
1442                                                         break;
1443                                                 }
1444                                         }
1445                                         break;
1446                                 case 77: /* M: Mute */ 
1447                                         if (user) {
1448                                                 user->adminflags |= ADMINFLAG_MUTED;
1449                                         } else {
1450                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1451                                         }
1452                                         break;
1453                                 case 109: /* m: Unmute */ 
1454                                         if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1455                                                 user->adminflags ^= ADMINFLAG_MUTED;
1456                                         } else {
1457                                                 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1458                                         }
1459                                         break;
1460                                 case 107: /* k: Kick user */ 
1461                                         if (user) {
1462                                                 user->adminflags |= ADMINFLAG_KICKME;
1463                                         } else {
1464                                                 ast_log(LOG_NOTICE, "Specified User not found!");
1465                                         }
1466                                         break;
1467                         }
1468                 } else {
1469                         ast_log(LOG_NOTICE, "Conference Number not found\n");
1470                 }
1471         }
1472         ast_mutex_unlock(&conflock);
1473         return 0;
1474 }
1475
1476 int unload_module(void)
1477 {
1478         STANDARD_HANGUP_LOCALUSERS;
1479         ast_cli_unregister(&cli_show_confs);
1480         ast_cli_unregister(&cli_conf);
1481         ast_unregister_application(app3);
1482         ast_unregister_application(app2);
1483         return ast_unregister_application(app);
1484 }
1485
1486 int load_module(void)
1487 {
1488         ast_cli_register(&cli_show_confs);
1489         ast_cli_register(&cli_conf);
1490         ast_register_application(app3, admin_exec, synopsis3, descrip3);
1491         ast_register_application(app2, count_exec, synopsis2, descrip2);
1492         return ast_register_application(app, conf_exec, synopsis, descrip);
1493 }
1494
1495 char *description(void)
1496 {
1497         return tdesc;
1498 }
1499
1500 int usecount(void)
1501 {
1502         int res;
1503         STANDARD_USECOUNT(res);
1504         return res;
1505 }
1506
1507 char *key()
1508 {
1509         return ASTERISK_GPL_KEY;
1510 }