Add ability to do PINs with dynamic conf (bug 1026)
[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/options.h>
24 #include <asterisk/cli.h>
25 #include <asterisk/say.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <sys/ioctl.h>
32
33 #include <pthread.h>
34 #include <linux/zaptel.h>
35
36 static char *tdesc = "Simple MeetMe conference bridge";
37
38 static char *app = "MeetMe";
39 static char *app2 = "MeetMeCount";
40
41 static char *synopsis = "Simple MeetMe conference bridge";
42 static char *synopsis2 = "MeetMe participant count";
43
44 static char *descrip =
45 "  MeetMe(confno[,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
46 "If the conference number is omitted, the user will be prompted to enter\n"
47 "one.  This application always returns -1.  A ZAPTEL INTERFACE MUST BE INSTALLED\n"
48 "FOR CONFERENCING TO WORK!\n\n"
49
50 "The option string may contain zero or more of the following characters:\n"
51 "      'm' -- set monitor only mode (Listen only, no talking\n"
52 "      't' -- set talk only mode. (Talk only, no listening)\n"
53 "      'p' -- allow user to exit the conference by pressing '#'\n"
54 "      'd' -- dynamically add conference\n"
55 "      'D' -- dynamically add conference, prompting for a PIN\n"
56 "      'e' -- select an empty conference\n"
57 "      'E' -- select an empty pinless conference\n"
58 "      'v' -- video mode\n"
59 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
60 "      'M' -- enable music on hold when the conference has a single caller\n"
61 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
62 "             Default: conf-background.agi\n"
63 "             (Note: This does not work with non-Zap channels in the same conference)\n"
64 "      Not implemented yet:\n"
65 "      's' -- send user to admin/user menu if '*' is received\n"
66 "      'a' -- set admin mode\n";
67
68 static char *descrip2 =
69 "  MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
70 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
71 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
72 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
73
74 STANDARD_LOCAL_USER;
75
76 LOCAL_USER_DECL;
77
78 static struct ast_conference {
79         char confno[AST_MAX_EXTENSION];         /* Conference */
80         int fd;                         /* Announcements fd */
81         int zapconf;                    /* Zaptel Conf # */
82         int users;                      /* Number of active users */
83         time_t start;                   /* Start time (s) */
84         int isdynamic;                  /* Created on the fly? */
85         char pin[AST_MAX_EXTENSION];                    /* If protected by a PIN */
86         struct ast_conference *next;
87 } *confs;
88
89 static ast_mutex_t conflock = AST_MUTEX_INITIALIZER;
90
91 #include "enter.h"
92 #include "leave.h"
93
94 #define ENTER   0
95 #define LEAVE   1
96
97 #define CONF_SIZE 160
98
99 #define CONFFLAG_ADMIN  (1 << 1)        /* If set the user has admin access on the conference */
100 #define CONFFLAG_MONITOR (1 << 2)       /* If set the user can only receive audio from the conference */
101 #define CONFFLAG_POUNDEXIT (1 << 3)     /* If set asterisk will exit conference when '#' is pressed */
102 #define CONFFLAG_STARMENU (1 << 4)      /* If set asterisk will provide a menu to the user what '*' is pressed */
103 #define CONFFLAG_TALKER (1 << 5)        /* If set the use can only send audio to the conference */
104 #define CONFFLAG_QUIET (1 << 6)         /* If set there will be no enter or leave sounds */
105 #define CONFFLAG_VIDEO (1 << 7)         /* Set to enable video mode */
106 #define CONFFLAG_AGI (1 << 8)           /* Set to run AGI Script in Background */
107 #define CONFFLAG_MOH (1 << 9)           /* Set to have music on hold when */
108
109
110 static int careful_write(int fd, unsigned char *data, int len)
111 {
112         int res;
113         while(len) {
114                 res = write(fd, data, len);
115                 if (res < 1) {
116                         if (errno != EAGAIN) {
117                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
118                                 return -1;
119                         } else
120                                 return 0;
121                 }
122                 len -= res;
123                 data += res;
124         }
125         return 0;
126 }
127
128 static void conf_play(struct ast_conference *conf, int sound)
129 {
130         unsigned char *data;
131         int len;
132         ast_mutex_lock(&conflock);
133         switch(sound) {
134         case ENTER:
135                 data = enter;
136                 len = sizeof(enter);
137                 break;
138         case LEAVE:
139                 data = leave;
140                 len = sizeof(leave);
141                 break;
142         default:
143                 data = NULL;
144                 len = 0;
145         }
146         if (data) 
147                 careful_write(conf->fd, data, len);
148         ast_mutex_unlock(&conflock);
149 }
150
151 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
152 {
153         struct ast_conference *cnf;
154         struct zt_confinfo ztc;
155         ast_mutex_lock(&conflock);
156         cnf = confs;
157         while(cnf) {
158                 if (!strcmp(confno, cnf->confno)) 
159                         break;
160                 cnf = cnf->next;
161         }
162         if (!cnf && (make || dynamic)) {
163                 cnf = malloc(sizeof(struct ast_conference));
164                 if (cnf) {
165                         /* Make a new one */
166                         memset(cnf, 0, sizeof(struct ast_conference));
167                         strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
168                         strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
169                         cnf->fd = open("/dev/zap/pseudo", O_RDWR);
170                         if (cnf->fd < 0) {
171                                 ast_log(LOG_WARNING, "Unable to open pseudo channel\n");
172                                 free(cnf);
173                                 cnf = NULL;
174                                 goto cnfout;
175                         }
176                         memset(&ztc, 0, sizeof(ztc));
177                         /* Setup a new zap conference */
178                         ztc.chan = 0;
179                         ztc.confno = -1;
180                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
181                         if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
182                                 ast_log(LOG_WARNING, "Error setting conference\n");
183                                 close(cnf->fd);
184                                 free(cnf);
185                                 cnf = NULL;
186                                 goto cnfout;
187                         }
188                         cnf->start = time(NULL);
189                         cnf->zapconf = ztc.confno;
190                         cnf->isdynamic = dynamic;
191                         if (option_verbose > 2)
192                                 ast_verbose(VERBOSE_PREFIX_3 "Created ZapTel conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
193                         cnf->next = confs;
194                         confs = cnf;
195                 } else  
196                         ast_log(LOG_WARNING, "Out of memory\n");
197         }
198 cnfout:
199         ast_mutex_unlock(&conflock);
200         return cnf;
201 }
202
203 static int confs_show(int fd, int argc, char **argv)
204 {
205         struct ast_conference *conf;
206         int hr, min, sec;
207         time_t now;
208         char *header_format = "%-14s %-14s %-8s  %-8s\n";
209         char *data_format = "%-12.12s   %4.4d           %02d:%02d:%02d  %-8s\n";
210
211         now = time(NULL);
212         if (argc != 2)
213                 return RESULT_SHOWUSAGE;
214         conf = confs;
215         if (!conf) {
216                 ast_cli(fd, "No active conferences.\n");
217                 return RESULT_SUCCESS;
218         }
219         ast_cli(fd, header_format, "Conf Num", "Parties", "Activity", "Creation");
220         while(conf) {
221                 hr = (now - conf->start) / 3600;
222                 min = ((now - conf->start) % 3600) / 60;
223                 sec = (now - conf->start) % 60;
224
225                 if (conf->isdynamic)
226                         ast_cli(fd, data_format, conf->confno, conf->users, hr, min, sec, "Dynamic");
227                 else
228                         ast_cli(fd, data_format, conf->confno, conf->users, hr, min, sec, "Static");
229
230                 conf = conf->next;
231         }
232         return RESULT_SUCCESS;
233 }
234
235 static char show_confs_usage[] = 
236 "Usage: show conferences\n"
237 "       Provides summary information on conferences with active\n"
238 "       participation.\n";
239
240 static struct ast_cli_entry cli_show_confs = {
241         { "show", "conferences", NULL }, confs_show, 
242         "Show status of conferences", show_confs_usage, NULL };
243
244 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
245 {
246         struct ast_conference *prev=NULL, *cur;
247         int fd;
248         struct zt_confinfo ztc;
249         struct ast_frame *f;
250         struct ast_channel *c;
251         struct ast_frame fr;
252         int outfd;
253         int ms;
254         int nfds;
255         int res;
256         int flags;
257         int retryzap;
258         int origfd;
259         int musiconhold = 0;
260         int firstpass = 0;
261         int ret = -1;
262         int x;
263
264         struct ast_app *app;
265         char *agifile;
266         char *agifiledefault = "conf-background.agi";
267
268         ZT_BUFFERINFO bi;
269         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
270         char *buf = __buf + AST_FRIENDLY_OFFSET;
271         
272         conf->users++;
273         if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
274                 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
275                         ast_waitstream(chan, "");
276         }
277
278         if (confflags & CONFFLAG_VIDEO) {       
279                 /* Set it into linear mode (write) */
280                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
281                         ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
282                         goto outrun;
283                 }
284
285                 /* Set it into linear mode (read) */
286                 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
287                         ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
288                         goto outrun;
289                 }
290         } else {
291                 /* Set it into U-law mode (write) */
292                 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
293                         ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
294                         goto outrun;
295                 }
296
297                 /* Set it into U-law mode (read) */
298                 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
299                         ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
300                         goto outrun;
301                 }
302         }
303         ast_indicate(chan, -1);
304         retryzap = strcasecmp(chan->type, "Zap");
305 zapretry:
306         origfd = chan->fds[0];
307         if (retryzap) {
308                 fd = open("/dev/zap/pseudo", O_RDWR);
309                 if (fd < 0) {
310                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
311                         goto outrun;
312                 }
313                 /* Make non-blocking */
314                 flags = fcntl(fd, F_GETFL);
315                 if (flags < 0) {
316                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
317                         close(fd);
318                         goto outrun;
319                 }
320                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
321                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
322                         close(fd);
323                         goto outrun;
324                 }
325                 /* Setup buffering information */
326                 memset(&bi, 0, sizeof(bi));
327                 bi.bufsize = CONF_SIZE;
328                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
329                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
330                 bi.numbufs = 4;
331                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
332                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
333                         close(fd);
334                         goto outrun;
335                 }
336                 if (confflags & CONFFLAG_VIDEO) {       
337                         x = 1;
338                         if (ioctl(fd, ZT_SETLINEAR, &x)) {
339                                 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
340                                 close(fd);
341                                 goto outrun;
342                         }
343                 }
344                 nfds = 1;
345         } else {
346                 /* XXX Make sure we're not running on a pseudo channel XXX */
347                 fd = chan->fds[0];
348                 nfds = 0;
349         }
350         memset(&ztc, 0, sizeof(ztc));
351         /* Check to see if we're in a conference... */
352         ztc.chan = 0;   
353         if (ioctl(fd, ZT_GETCONF, &ztc)) {
354                 ast_log(LOG_WARNING, "Error getting conference\n");
355                 close(fd);
356                 goto outrun;
357         }
358         if (ztc.confmode) {
359                 /* Whoa, already in a conference...  Retry... */
360                 if (!retryzap) {
361                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
362                         retryzap = 1;
363                         goto zapretry;
364                 }
365         }
366         memset(&ztc, 0, sizeof(ztc));
367         /* Add us to the conference */
368         ztc.chan = 0;   
369         ztc.confno = conf->zapconf;
370         if (confflags & CONFFLAG_MONITOR)
371                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
372         else if (confflags & CONFFLAG_TALKER)
373                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
374         else 
375                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
376
377         if (ioctl(fd, ZT_SETCONF, &ztc)) {
378                 ast_log(LOG_WARNING, "Error setting conference\n");
379                 close(fd);
380                 goto outrun;
381         }
382         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
383         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
384                 firstpass = 1;
385                 if (!(confflags & CONFFLAG_QUIET))
386                         conf_play(conf, ENTER);
387         }
388
389         if (confflags & CONFFLAG_AGI) {
390
391                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
392                   or use default filename of conf-background.agi */
393
394                 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
395                 if (!agifile)
396                         agifile = agifiledefault;
397
398                 if (!strcasecmp(chan->type,"Zap")) {
399                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
400                         x = 1;
401                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
402                 }
403                 /* Find a pointer to the agi app and execute the script */
404                 app = pbx_findapp("agi");
405                 if (app) {
406                         ret = pbx_exec(chan, app, agifile, 1);
407                 } else {
408                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
409                         ret = -2;
410                 }
411                 if (!strcasecmp(chan->type,"Zap")) {
412                         /*  Remove CONFMUTE mode on Zap channel */
413                         x = 0;
414                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
415                 }
416         } else for(;;) {
417                 outfd = -1;
418                 ms = -1;
419                 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
420                 /* trying to add moh for single person conf */
421                 if (confflags & CONFFLAG_MOH) {
422                         if (conf->users == 1) {
423                                 if (musiconhold == 0) {
424                                         ast_moh_start(chan, NULL);
425                                         musiconhold = 1;
426                                 } 
427                         } else {
428                                 if (musiconhold) {
429                                         ast_moh_stop(chan);
430                                         musiconhold = 0;
431                                 }
432                         }
433                 }
434                 /* end modifications */
435
436                 if (c) {
437                         if (c->fds[0] != origfd) {
438                                 if (retryzap) {
439                                         /* Kill old pseudo */
440                                         close(fd);
441                                 }
442                                 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
443                                 retryzap = 0;
444                                 goto zapretry;
445                         }
446                         f = ast_read(c);
447                         if (!f) 
448                                 break;
449                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
450                                 ret = 0;
451                                 break;
452                         } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) {
453                                         if ((confflags & CONFFLAG_ADMIN)) {
454                                         /* Do admin stuff here */
455                                         } else {
456                                         /* Do user menu here */
457                                         }
458
459                         } else if (fd != chan->fds[0]) {
460                                 if (f->frametype == AST_FRAME_VOICE) {
461                                         if (f->subclass == AST_FORMAT_ULAW) {
462                                                 /* Carefully write */
463                                                 careful_write(fd, f->data, f->datalen);
464                                         } else
465                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
466                                 }
467                         }
468                         ast_frfree(f);
469                 } else if (outfd > -1) {
470                         res = read(outfd, buf, CONF_SIZE);
471                         if (res > 0) {
472                                 memset(&fr, 0, sizeof(fr));
473                                 fr.frametype = AST_FRAME_VOICE;
474                                 fr.subclass = AST_FORMAT_ULAW;
475                                 fr.datalen = res;
476                                 fr.samples = res;
477                                 fr.data = buf;
478                                 fr.offset = AST_FRIENDLY_OFFSET;
479                                 if (ast_write(chan, &fr) < 0) {
480                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
481                                         /* break; */
482                                 }
483                         } else 
484                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
485                 }
486         }
487
488         if (fd != chan->fds[0])
489                 close(fd);
490         else {
491                 /* Take out of conference */
492                 /* Add us to the conference */
493                 ztc.chan = 0;   
494                 ztc.confno = 0;
495                 ztc.confmode = 0;
496                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
497                         ast_log(LOG_WARNING, "Error setting conference\n");
498                 }
499         }
500         if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
501                 conf_play(conf, LEAVE);
502
503 outrun:
504
505         ast_mutex_lock(&conflock);
506         /* Clean up */
507         conf->users--;
508         if (!conf->users) {
509                 /* No more users -- close this one out */
510                 cur = confs;
511                 while(cur) {
512                         if (cur == conf) {
513                                 if (prev)
514                                         prev->next = conf->next;
515                                 else
516                                         confs = conf->next;
517                                 break;
518                         }
519                         prev = cur;
520                         cur = cur->next;
521                 }
522                 if (!cur) 
523                         ast_log(LOG_WARNING, "Conference not found\n");
524                 close(conf->fd);
525                 free(conf);
526         }
527         ast_mutex_unlock(&conflock);
528         return ret;
529 }
530
531 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
532 {
533         struct ast_config *cfg;
534         struct ast_variable *var;
535         struct ast_conference *cnf = confs;
536
537         /* Check first in the conference list */
538         ast_mutex_lock(&conflock);
539         while (cnf) {
540                 if (!strcmp(confno, cnf->confno)) 
541                         break;
542                 cnf = cnf->next;
543         }
544         ast_mutex_unlock(&conflock);
545
546         if (!cnf) {
547                 if (dynamic) {
548                         /* No need to parse meetme.conf */
549                         ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
550                         if (dynamic_pin) {
551                                 if (dynamic_pin[0] == 'q') {
552                                         /* Query the user to enter a PIN */
553                                         ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
554                                 }
555                                 cnf = build_conf(confno, dynamic_pin, make, dynamic);
556                         } else {
557                                 cnf = build_conf(confno, "", make, dynamic);
558                         }
559                 } else {
560                         /* Check the config */
561                         cfg = ast_load("meetme.conf");
562                         if (!cfg) {
563                                 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
564                                 return NULL;
565                         }
566                         var = ast_variable_browse(cfg, "rooms");
567                         while(var) {
568                                 if (!strcasecmp(var->name, "conf")) {
569                                         /* Separate the PIN */
570                                         char *pin, *conf;
571
572                                         if ((pin = ast_strdupa(var->value))) {
573                                                 conf = strsep(&pin, "|,");
574                                                 if (!strcasecmp(conf, confno)) {
575                                                         /* Bingo it's a valid conference */
576                                                         if (pin)
577                                                                 cnf = build_conf(confno, pin, make, dynamic);
578                                                         else
579                                                                 cnf = build_conf(confno, "", make, dynamic);
580                                                         break;
581                                                 }
582                                         }
583                                 }
584                                 var = var->next;
585                         }
586                         if (!var) {
587                                 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
588                         }
589                         ast_destroy(cfg);
590                 }
591         }
592         return cnf;
593 }
594
595 static int count_exec(struct ast_channel *chan, void *data)
596 {
597         struct localuser *u;
598         int res = 0;
599         struct ast_conference *conf;
600         int count;
601         char *confnum, *localdata;
602         char val[80] = "0"; 
603
604         if (!data || !strlen(data)) {
605                 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
606                 return -1;
607         }
608         localdata = ast_strdupa(data);
609         LOCAL_USER_ADD(u);
610         confnum = strsep(&localdata,"|");       
611         conf = find_conf(chan, confnum, 0, 0, NULL);
612         if (conf)
613                 count = conf->users;
614         else
615                 count = 0;
616
617         if (localdata && strlen(localdata)){
618                 /* have var so load it and exit */
619                 snprintf(val,sizeof(val), "%i",count);
620                 pbx_builtin_setvar_helper(chan, localdata,val);
621         } else {
622                 if (chan->_state != AST_STATE_UP)
623                         ast_answer(chan);
624                 res = ast_say_number(chan, count, "", chan->language);
625         }
626         LOCAL_USER_REMOVE(u);
627         return res;
628 }
629
630 static int conf_exec(struct ast_channel *chan, void *data)
631 {
632         int res=-1;
633         struct localuser *u;
634         char confno[AST_MAX_EXTENSION] = "";
635         int allowretry = 0;
636         int retrycnt = 0;
637         struct ast_conference *cnf;
638         int confflags = 0;
639         int dynamic = 0;
640         int empty = 0, empty_no_pin = 0;
641         char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
642
643         if (!data || !strlen(data)) {
644                 allowretry = 1;
645                 notdata = "";
646         } else {
647                 notdata = data;
648         }
649         LOCAL_USER_ADD(u);
650         if (chan->_state != AST_STATE_UP)
651                 ast_answer(chan);
652
653         info = ast_strdupa((char *)notdata);
654
655         if (info) {
656                 char *tmp = strsep(&info, "|");
657                 strncpy(confno, tmp, sizeof(confno));
658                 if (strlen(confno) == 0) {
659                         allowretry = 1;
660                 }
661         }
662         if (info)
663                 inflags = strsep(&info, "|");
664         if (info)
665                 inpin = strsep(&info, "|");
666         if (inpin)
667                 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
668
669         if (inflags) {
670                 if (strchr(inflags, 'a'))
671                         confflags |= CONFFLAG_ADMIN;
672                 if (strchr(inflags, 'm'))
673                         confflags |= CONFFLAG_MONITOR;
674                 if (strchr(inflags, 'p'))
675                         confflags |= CONFFLAG_POUNDEXIT;
676                 if (strchr(inflags, 's'))
677                         confflags |= CONFFLAG_STARMENU;
678                 if (strchr(inflags, 't'))
679                         confflags |= CONFFLAG_TALKER;
680                 if (strchr(inflags, 'q'))
681                         confflags |= CONFFLAG_QUIET;
682                 if (strchr(inflags, 'M'))
683                         confflags |= CONFFLAG_MOH;
684                 if (strchr(inflags, 'b'))
685                         confflags |= CONFFLAG_AGI;
686                 if (strchr(inflags, 'd'))
687                         dynamic = 1;
688                 if (strchr(inflags, 'D')) {
689                         dynamic = 1;
690                         if (! inpin) {
691                                 strncpy(the_pin, "q", sizeof(the_pin) - 1);
692                         }
693                 }
694                 if (strchr(inflags, 'e'))
695                         empty = 1;
696                 if (strchr(inflags, 'E')) {
697                         empty = 1;
698                         empty_no_pin = 1;
699                 }
700         }
701
702         do {
703                 if (retrycnt > 3)
704                         allowretry = 0;
705                 if (empty) {
706                         int i, map[1024];
707                         struct ast_config *cfg;
708                         struct ast_variable *var;
709                         int confno_int;
710
711                         memset(map, 0, sizeof(map));
712
713                         ast_mutex_lock(&conflock);
714                         cnf = confs;
715                         while (cnf) {
716                                 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
717                                         /* Disqualify in use conference */
718                                         if (confno_int >= 0 && confno_int < 1024)
719                                                 map[confno_int]++;
720                                 }
721                                 cnf = cnf->next;
722                         }
723                         ast_mutex_unlock(&conflock);
724
725                         /* Disqualify static conferences with pins */
726                         cfg = ast_load("meetme.conf");
727                         if (cfg) {
728                                 var = ast_variable_browse(cfg, "rooms");
729                                 while(var) {
730                                         if (!strcasecmp(var->name, "conf")) {
731                                                 char *stringp = ast_strdupa(var->value);
732                                                 if (stringp) {
733                                                         char *confno_tmp = strsep(&stringp, "|,");
734                                                         int found = 0;
735                                                         if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
736                                                                 if (confno_int >= 0 && confno_int < 1024) {
737                                                                         if (stringp && empty_no_pin) {
738                                                                                 map[confno_int]++;
739                                                                         }
740                                                                 }
741                                                         }
742                                                         if (! dynamic) {
743                                                                 /* For static:  run through the list and see if this conference is empty */
744                                                                 ast_mutex_lock(&conflock);
745                                                                 cnf = confs;
746                                                                 while (cnf) {
747                                                                         if (!strcmp(confno_tmp, cnf->confno)) {
748                                                                                 found = 1;
749                                                                                 break;
750                                                                         }
751                                                                 }
752                                                                 ast_mutex_unlock(&conflock);
753                                                                 if (!found) {
754                                                                         if ((empty_no_pin && (!stringp)) || (!empty_no_pin)) {
755                                                                                 strncpy(confno, confno_tmp, sizeof(confno) - 1);
756                                                                                 break;
757                                                                         }
758                                                                 }
759                                                         }
760                                                 }
761                                         }
762                                         var = var->next;
763                                 }
764                                 ast_destroy(cfg);
765                         }
766
767                         /* Select first conference number not in use */
768                         if (dynamic) {
769                                 for (i=0;i<1024;i++) {
770                                         if (dynamic && (!map[i])) {
771                                                 snprintf(confno, sizeof(confno) - 1, "%d", i);
772                                                 break;
773                                         }
774                                 }
775                         }
776
777                         /* Not found? */
778                         if (!strlen(confno)) {
779                                 res = ast_streamfile(chan, "conf-noempty", chan->language);
780                                 if (!res)
781                                         ast_waitstream(chan, "");
782                         } else {
783                                 if (sscanf(confno, "%d", &confno_int) == 1) {
784                                         res = ast_streamfile(chan, "conf-enteringno", chan->language);
785                                         if (!res) {
786                                                 ast_waitstream(chan, "");
787                                                 res = ast_say_digits(chan, confno_int, "", chan->language);
788                                         }
789                                 }
790                         }
791                 }
792                 while (allowretry && (!strlen(confno)) && (++retrycnt < 4)) {
793                         /* Prompt user for conference number */
794                         res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
795                         if (res < 0) {
796                                 /* Don't try to validate when we catch an error */
797                                 strcpy(confno, "");
798                                 allowretry = 0;
799                                 break;
800                         }
801                 }
802                 if (strlen(confno)) {
803                         /* Check the validity of the conference */
804                         cnf = find_conf(chan, confno, 1, dynamic, the_pin);
805                         if (!cnf) {
806                                 res = ast_streamfile(chan, "conf-invalid", chan->language);
807                                 if (!res)
808                                         ast_waitstream(chan, "");
809                                 res = -1;
810                                 if (allowretry)
811                                         strcpy(confno, "");
812                         } else {
813                                 if (strlen(cnf->pin)) {
814                                         char pin[AST_MAX_EXTENSION];
815
816                                         if (*the_pin) {
817                                                 strncpy(pin, the_pin, sizeof(pin) - 1);
818                                                 res = 0;
819                                         } else {
820                                                 /* Prompt user for pin if pin is required */
821                                                 res = ast_app_getdata(chan, "conf-getpin", pin, sizeof(pin) - 1, 0);
822                                         }
823                                         if (res >= 0) {
824                                                 if (!strcasecmp(pin, cnf->pin)) {
825                                                         /* Pin correct */
826                                                         allowretry = 0;
827                                                         /* Run the conference */
828                                                         res = conf_run(chan, cnf, confflags);
829                                                 } else {
830                                                         /* Pin invalid */
831                                                         res = ast_streamfile(chan, "conf-invalidpin", chan->language);
832                                                         if (!res)
833                                                                 ast_waitstream(chan, "");
834                                                         res = -1;
835                                                         if (allowretry)
836                                                                 strcpy(confno, "");
837                                                 }
838                                         } else {
839                                                 res = -1;
840                                                 allowretry = 0;
841                                         }
842                                 } else {
843                                         /* No pin required */
844                                         allowretry = 0;
845
846                                         /* Run the conference */
847                                         res = conf_run(chan, cnf, confflags);
848                                 }
849                         }
850                 }
851         } while (allowretry);
852         /* Do the conference */
853         LOCAL_USER_REMOVE(u);
854         return res;
855 }
856
857 int unload_module(void)
858 {
859         STANDARD_HANGUP_LOCALUSERS;
860         ast_cli_unregister(&cli_show_confs);
861         ast_unregister_application(app2);
862         return ast_unregister_application(app);
863 }
864
865 int load_module(void)
866 {
867         ast_cli_register(&cli_show_confs);
868         ast_register_application(app2, count_exec, synopsis2, descrip2);
869         return ast_register_application(app, conf_exec, synopsis, descrip);
870 }
871
872 char *description(void)
873 {
874         return tdesc;
875 }
876
877 int usecount(void)
878 {
879         int res;
880         STANDARD_USECOUNT(res);
881         return res;
882 }
883
884 char *key()
885 {
886         return ASTERISK_GPL_KEY;
887 }