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