should be != :)
[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 "      'm' -- set monitor only mode (Listen only, no talking\n"
51 "      't' -- set talk only mode. (Talk only, no listening)\n"
52 "      'p' -- allow user to exit the conference by pressing '#'\n"
53 "      'd' -- dynamically add conference\n"
54 "      'v' -- video mode\n"
55 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
56 "      'M' -- enable music on hold when the conference has a single caller\n"
57 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
58 "             Default: conf-background.agi\n"
59 "             (Note: This does not work with non-Zap channels in the same conference)\n"
60 "      Not implemented yet:\n"
61 "      's' -- send user to admin/user menu if '*' is received\n"
62 "      'a' -- set admin mode\n";
63
64 static char *descrip2 =
65 "  MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
66 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
67 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
68 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
69
70 STANDARD_LOCAL_USER;
71
72 LOCAL_USER_DECL;
73
74 static struct ast_conference {
75         char confno[AST_MAX_EXTENSION];         /* Conference */
76         int fd;                         /* Announcements fd */
77         int zapconf;                    /* Zaptel Conf # */
78         int users;                      /* Number of active users */
79         time_t start;                   /* Start time (s) */
80         int isdynamic;                  /* Created on the fly? */
81         char pin[AST_MAX_EXTENSION];                    /* If protected by a PIN */
82         struct ast_conference *next;
83 } *confs;
84
85 static ast_mutex_t conflock = AST_MUTEX_INITIALIZER;
86
87 #include "enter.h"
88 #include "leave.h"
89
90 #define ENTER   0
91 #define LEAVE   1
92
93 #define CONF_SIZE 160
94
95 #define CONFFLAG_ADMIN  (1 << 1)        /* If set the user has admin access on the conference */
96 #define CONFFLAG_MONITOR (1 << 2)       /* If set the user can only receive audio from the conference */
97 #define CONFFLAG_POUNDEXIT (1 << 3)     /* If set asterisk will exit conference when '#' is pressed */
98 #define CONFFLAG_STARMENU (1 << 4)      /* If set asterisk will provide a menu to the user what '*' is pressed */
99 #define CONFFLAG_TALKER (1 << 5)        /* If set the use can only send audio to the conference */
100 #define CONFFLAG_QUIET (1 << 6)         /* If set there will be no enter or leave sounds */
101 #define CONFFLAG_VIDEO (1 << 7)         /* Set to enable video mode */
102 #define CONFFLAG_AGI (1 << 8)           /* Set to run AGI Script in Background */
103 #define CONFFLAG_MOH (1 << 9)           /* Set to have music on hold when */
104
105
106 static int careful_write(int fd, unsigned char *data, int len)
107 {
108         int res;
109         while(len) {
110                 res = write(fd, data, len);
111                 if (res < 1) {
112                         if (errno != EAGAIN) {
113                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
114                                 return -1;
115                         } else
116                                 return 0;
117                 }
118                 len -= res;
119                 data += res;
120         }
121         return 0;
122 }
123
124 static void conf_play(struct ast_conference *conf, int sound)
125 {
126         unsigned char *data;
127         int len;
128         ast_mutex_lock(&conflock);
129         switch(sound) {
130         case ENTER:
131                 data = enter;
132                 len = sizeof(enter);
133                 break;
134         case LEAVE:
135                 data = leave;
136                 len = sizeof(leave);
137                 break;
138         default:
139                 data = NULL;
140                 len = 0;
141         }
142         if (data) 
143                 careful_write(conf->fd, data, len);
144         ast_mutex_unlock(&conflock);
145 }
146
147 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
148 {
149         struct ast_conference *cnf;
150         struct zt_confinfo ztc;
151         ast_mutex_lock(&conflock);
152         cnf = confs;
153         while(cnf) {
154                 if (!strcmp(confno, cnf->confno)) 
155                         break;
156                 cnf = cnf->next;
157         }
158         if (!cnf && (make || dynamic)) {
159                 cnf = malloc(sizeof(struct ast_conference));
160                 if (cnf) {
161                         /* Make a new one */
162                         memset(cnf, 0, sizeof(struct ast_conference));
163                         strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
164                         strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
165                         cnf->fd = open("/dev/zap/pseudo", O_RDWR);
166                         if (cnf->fd < 0) {
167                                 ast_log(LOG_WARNING, "Unable to open pseudo channel\n");
168                                 free(cnf);
169                                 cnf = NULL;
170                                 goto cnfout;
171                         }
172                         memset(&ztc, 0, sizeof(ztc));
173                         /* Setup a new zap conference */
174                         ztc.chan = 0;
175                         ztc.confno = -1;
176                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
177                         if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
178                                 ast_log(LOG_WARNING, "Error setting conference\n");
179                                 close(cnf->fd);
180                                 free(cnf);
181                                 cnf = NULL;
182                                 goto cnfout;
183                         }
184                         cnf->start = time(NULL);
185                         cnf->zapconf = ztc.confno;
186                         cnf->isdynamic = dynamic;
187                         if (option_verbose > 2)
188                                 ast_verbose(VERBOSE_PREFIX_3 "Created ZapTel conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
189                         cnf->next = confs;
190                         confs = cnf;
191                 } else  
192                         ast_log(LOG_WARNING, "Out of memory\n");
193         }
194 cnfout:
195         ast_mutex_unlock(&conflock);
196         return cnf;
197 }
198
199 static int confs_show(int fd, int argc, char **argv)
200 {
201         struct ast_conference *conf;
202         int hr, min, sec;
203         time_t now;
204         char *header_format = "%14s %-14s %-8s  %-8s\n";
205         char *data_format = "%-12.12s   %4.4d          %02d:%02d:%02d  %-8s\n";
206
207         now = time(NULL);
208         if (argc != 2)
209                 return RESULT_SHOWUSAGE;
210         conf = confs;
211         if (!conf) {
212                 ast_cli(fd, "No active conferences.\n");
213                 return RESULT_SUCCESS;
214         }
215         ast_cli(fd, header_format, "Conf Num", "Parties", "Activity", "Creation");
216         while(conf) {
217                 hr = (now - conf->start) / 3600;
218                 min = ((now - conf->start) % 3600) / 60;
219                 sec = (now - conf->start) % 60;
220
221                 if (conf->isdynamic)
222                         ast_cli(fd, data_format, conf->confno, conf->users, hr, min, sec, "Dynamic");
223                 else
224                         ast_cli(fd, data_format, conf->confno, conf->users, hr, min, sec, "Static");
225
226                 conf = conf->next;
227         }
228         return RESULT_SUCCESS;
229 }
230
231 static char show_confs_usage[] = 
232 "Usage: show conferences\n"
233 "       Provides summary information on conferences with active\n"
234 "       participation.\n";
235
236 static struct ast_cli_entry cli_show_confs = {
237         { "show", "conferences", NULL }, confs_show, 
238         "Show status of conferences", show_confs_usage, NULL };
239
240 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
241 {
242         struct ast_conference *prev=NULL, *cur;
243         int fd;
244         struct zt_confinfo ztc;
245         struct ast_frame *f;
246         struct ast_channel *c;
247         struct ast_frame fr;
248         int outfd;
249         int ms;
250         int nfds;
251         int res;
252         int flags;
253         int retryzap;
254         int origfd;
255         int musiconhold = 0;
256         int firstpass = 0;
257         int ret = -1;
258         int x;
259
260         struct ast_app *app;
261         char *agifile;
262         char *agifiledefault = "conf-background.agi";
263
264         ZT_BUFFERINFO bi;
265         char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
266         char *buf = __buf + AST_FRIENDLY_OFFSET;
267         
268         conf->users++;
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 }