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