b7d488ad0bdc49cae4fc2593908a2f8de3fab1d1
[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.  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 "      'v' -- video mode\n"
56 "      'q' -- quiet mode (don't play enter/leave sounds)\n"
57 "      'M' -- enable music on hold when the conference has a single caller\n"
58 "      'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
59 "             Default: conf-background.agi\n"
60 "             (Note: This does not work with non-Zap channels in the same conference)\n"
61 "      Not implemented yet:\n"
62 "      's' -- send user to admin/user menu if '*' is received\n"
63 "      'a' -- set admin mode\n";
64
65 static char *descrip2 =
66 "  MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
67 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
68 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
69 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
70
71 STANDARD_LOCAL_USER;
72
73 LOCAL_USER_DECL;
74
75 static struct ast_conference {
76         char confno[AST_MAX_EXTENSION];         /* Conference */
77         int fd;                         /* Announcements fd */
78         int zapconf;                    /* Zaptel Conf # */
79         int users;                      /* Number of active users */
80         time_t start;                   /* Start time (s) */
81         int isdynamic;                  /* Created on the fly? */
82         char pin[AST_MAX_EXTENSION];                    /* If protected by a PIN */
83         struct ast_conference *next;
84 } *confs;
85
86 static ast_mutex_t conflock = AST_MUTEX_INITIALIZER;
87
88 #include "enter.h"
89 #include "leave.h"
90
91 #define ENTER   0
92 #define LEAVE   1
93
94 #define CONF_SIZE 160
95
96 #define CONFFLAG_ADMIN  (1 << 1)        /* If set the user has admin access on the conference */
97 #define CONFFLAG_MONITOR (1 << 2)       /* If set the user can only receive audio from the conference */
98 #define CONFFLAG_POUNDEXIT (1 << 3)     /* If set asterisk will exit conference when '#' is pressed */
99 #define CONFFLAG_STARMENU (1 << 4)      /* If set asterisk will provide a menu to the user what '*' is pressed */
100 #define CONFFLAG_TALKER (1 << 5)        /* If set the use can only send audio to the conference */
101 #define CONFFLAG_QUIET (1 << 6)         /* If set there will be no enter or leave sounds */
102 #define CONFFLAG_VIDEO (1 << 7)         /* Set to enable video mode */
103 #define CONFFLAG_AGI (1 << 8)           /* Set to run AGI Script in Background */
104 #define CONFFLAG_MOH (1 << 9)           /* Set to have music on hold when */
105
106
107 static int careful_write(int fd, unsigned char *data, int len)
108 {
109         int res;
110         while(len) {
111                 res = write(fd, data, len);
112                 if (res < 1) {
113                         if (errno != EAGAIN) {
114                                 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
115                                 return -1;
116                         } else
117                                 return 0;
118                 }
119                 len -= res;
120                 data += res;
121         }
122         return 0;
123 }
124
125 static void conf_play(struct ast_conference *conf, int sound)
126 {
127         unsigned char *data;
128         int len;
129         ast_mutex_lock(&conflock);
130         switch(sound) {
131         case ENTER:
132                 data = enter;
133                 len = sizeof(enter);
134                 break;
135         case LEAVE:
136                 data = leave;
137                 len = sizeof(leave);
138                 break;
139         default:
140                 data = NULL;
141                 len = 0;
142         }
143         if (data) 
144                 careful_write(conf->fd, data, len);
145         ast_mutex_unlock(&conflock);
146 }
147
148 static struct ast_conference *build_conf(char *confno, char *pin, int make, int dynamic)
149 {
150         struct ast_conference *cnf;
151         struct zt_confinfo ztc;
152         ast_mutex_lock(&conflock);
153         cnf = confs;
154         while(cnf) {
155                 if (!strcmp(confno, cnf->confno)) 
156                         break;
157                 cnf = cnf->next;
158         }
159         if (!cnf && (make || dynamic)) {
160                 cnf = malloc(sizeof(struct ast_conference));
161                 if (cnf) {
162                         /* Make a new one */
163                         memset(cnf, 0, sizeof(struct ast_conference));
164                         strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
165                         strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
166                         cnf->fd = open("/dev/zap/pseudo", O_RDWR);
167                         if (cnf->fd < 0) {
168                                 ast_log(LOG_WARNING, "Unable to open pseudo channel\n");
169                                 free(cnf);
170                                 cnf = NULL;
171                                 goto cnfout;
172                         }
173                         memset(&ztc, 0, sizeof(ztc));
174                         /* Setup a new zap conference */
175                         ztc.chan = 0;
176                         ztc.confno = -1;
177                         ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
178                         if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
179                                 ast_log(LOG_WARNING, "Error setting conference\n");
180                                 close(cnf->fd);
181                                 free(cnf);
182                                 cnf = NULL;
183                                 goto cnfout;
184                         }
185                         cnf->start = time(NULL);
186                         cnf->zapconf = ztc.confno;
187                         cnf->isdynamic = dynamic;
188                         if (option_verbose > 2)
189                                 ast_verbose(VERBOSE_PREFIX_3 "Created ZapTel conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
190                         cnf->next = confs;
191                         confs = cnf;
192                 } else  
193                         ast_log(LOG_WARNING, "Out of memory\n");
194         }
195 cnfout:
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         conf->users++;
270         if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
271                 if (!ast_streamfile(chan, "conf-onlyperson", chan->language))
272                         ast_waitstream(chan, "");
273         }
274
275         if (confflags & CONFFLAG_VIDEO) {       
276                 /* Set it into linear mode (write) */
277                 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
278                         ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
279                         goto outrun;
280                 }
281
282                 /* Set it into linear mode (read) */
283                 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
284                         ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
285                         goto outrun;
286                 }
287         } else {
288                 /* Set it into U-law mode (write) */
289                 if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) {
290                         ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name);
291                         goto outrun;
292                 }
293
294                 /* Set it into U-law mode (read) */
295                 if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) {
296                         ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name);
297                         goto outrun;
298                 }
299         }
300         ast_indicate(chan, -1);
301         retryzap = strcasecmp(chan->type, "Zap");
302 zapretry:
303         origfd = chan->fds[0];
304         if (retryzap) {
305                 fd = open("/dev/zap/pseudo", O_RDWR);
306                 if (fd < 0) {
307                         ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
308                         goto outrun;
309                 }
310                 /* Make non-blocking */
311                 flags = fcntl(fd, F_GETFL);
312                 if (flags < 0) {
313                         ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
314                         close(fd);
315                         goto outrun;
316                 }
317                 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
318                         ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
319                         close(fd);
320                         goto outrun;
321                 }
322                 /* Setup buffering information */
323                 memset(&bi, 0, sizeof(bi));
324                 bi.bufsize = CONF_SIZE;
325                 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
326                 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
327                 bi.numbufs = 4;
328                 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
329                         ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
330                         close(fd);
331                         goto outrun;
332                 }
333                 if (confflags & CONFFLAG_VIDEO) {       
334                         x = 1;
335                         if (ioctl(fd, ZT_SETLINEAR, &x)) {
336                                 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
337                                 close(fd);
338                                 goto outrun;
339                         }
340                 }
341                 nfds = 1;
342         } else {
343                 /* XXX Make sure we're not running on a pseudo channel XXX */
344                 fd = chan->fds[0];
345                 nfds = 0;
346         }
347         memset(&ztc, 0, sizeof(ztc));
348         /* Check to see if we're in a conference... */
349         ztc.chan = 0;   
350         if (ioctl(fd, ZT_GETCONF, &ztc)) {
351                 ast_log(LOG_WARNING, "Error getting conference\n");
352                 close(fd);
353                 goto outrun;
354         }
355         if (ztc.confmode) {
356                 /* Whoa, already in a conference...  Retry... */
357                 if (!retryzap) {
358                         ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
359                         retryzap = 1;
360                         goto zapretry;
361                 }
362         }
363         memset(&ztc, 0, sizeof(ztc));
364         /* Add us to the conference */
365         ztc.chan = 0;   
366         ztc.confno = conf->zapconf;
367         if (confflags & CONFFLAG_MONITOR)
368                 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
369         else if (confflags & CONFFLAG_TALKER)
370                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
371         else 
372                 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
373
374         if (ioctl(fd, ZT_SETCONF, &ztc)) {
375                 ast_log(LOG_WARNING, "Error setting conference\n");
376                 close(fd);
377                 goto outrun;
378         }
379         ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
380         if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
381                 firstpass = 1;
382                 if (!(confflags & CONFFLAG_QUIET))
383                         conf_play(conf, ENTER);
384         }
385
386         if (confflags & CONFFLAG_AGI) {
387
388                 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
389                   or use default filename of conf-background.agi */
390
391                 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
392                 if (!agifile)
393                         agifile = agifiledefault;
394
395                 if (!strcasecmp(chan->type,"Zap")) {
396                         /*  Set CONFMUTE mode on Zap channel to mute DTMF tones */
397                         x = 1;
398                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
399                 }
400                 /* Find a pointer to the agi app and execute the script */
401                 app = pbx_findapp("agi");
402                 if (app) {
403                         ret = pbx_exec(chan, app, agifile, 1);
404                 } else {
405                         ast_log(LOG_WARNING, "Could not find application (agi)\n");
406                         ret = -2;
407                 }
408                 if (!strcasecmp(chan->type,"Zap")) {
409                         /*  Remove CONFMUTE mode on Zap channel */
410                         x = 0;
411                         ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
412                 }
413         } else for(;;) {
414                 outfd = -1;
415                 ms = -1;
416                 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
417                 /* trying to add moh for single person conf */
418                 if (confflags & CONFFLAG_MOH) {
419                         if (conf->users == 1) {
420                                 if (musiconhold == 0) {
421                                         ast_moh_start(chan, NULL);
422                                         musiconhold = 1;
423                                 } 
424                         } else {
425                                 if (musiconhold) {
426                                         ast_moh_stop(chan);
427                                         musiconhold = 0;
428                                 }
429                         }
430                 }
431                 /* end modifications */
432
433                 if (c) {
434                         if (c->fds[0] != origfd) {
435                                 if (retryzap) {
436                                         /* Kill old pseudo */
437                                         close(fd);
438                                 }
439                                 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
440                                 retryzap = 0;
441                                 goto zapretry;
442                         }
443                         f = ast_read(c);
444                         if (!f) 
445                                 break;
446                         if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
447                                 ret = 0;
448                                 break;
449                         } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) {
450                                         if ((confflags & CONFFLAG_ADMIN)) {
451                                         /* Do admin stuff here */
452                                         } else {
453                                         /* Do user menu here */
454                                         }
455
456                         } else if (fd != chan->fds[0]) {
457                                 if (f->frametype == AST_FRAME_VOICE) {
458                                         if (f->subclass == AST_FORMAT_ULAW) {
459                                                 /* Carefully write */
460                                                 careful_write(fd, f->data, f->datalen);
461                                         } else
462                                                 ast_log(LOG_WARNING, "Huh?  Got a non-ulaw (%d) frame in the conference\n", f->subclass);
463                                 }
464                         }
465                         ast_frfree(f);
466                 } else if (outfd > -1) {
467                         res = read(outfd, buf, CONF_SIZE);
468                         if (res > 0) {
469                                 memset(&fr, 0, sizeof(fr));
470                                 fr.frametype = AST_FRAME_VOICE;
471                                 fr.subclass = AST_FORMAT_ULAW;
472                                 fr.datalen = res;
473                                 fr.samples = res;
474                                 fr.data = buf;
475                                 fr.offset = AST_FRIENDLY_OFFSET;
476                                 if (ast_write(chan, &fr) < 0) {
477                                         ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
478                                         /* break; */
479                                 }
480                         } else 
481                                 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
482                 }
483         }
484
485         if (fd != chan->fds[0])
486                 close(fd);
487         else {
488                 /* Take out of conference */
489                 /* Add us to the conference */
490                 ztc.chan = 0;   
491                 ztc.confno = 0;
492                 ztc.confmode = 0;
493                 if (ioctl(fd, ZT_SETCONF, &ztc)) {
494                         ast_log(LOG_WARNING, "Error setting conference\n");
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                                         char pin[AST_MAX_EXTENSION];
702
703                                         if (inpin && *inpin) {
704                                                 strncpy(pin, inpin, sizeof(pin) - 1);
705                                         } else {
706                                                 /* Prompt user for pin if pin is required */
707                                                 res = ast_app_getdata(chan, "conf-getpin", pin, sizeof(pin) - 1, 0);
708                                         }
709                                         if (res >= 0) {
710                                                 if (!strcasecmp(pin, cnf->pin)) {
711                                                         /* Pin correct */
712                                                         allowretry = 0;
713                                                         /* Run the conference */
714                                                         res = conf_run(chan, cnf, confflags);
715                                                 } else {
716                                                         /* Pin invalid */
717                                                         res = ast_streamfile(chan, "conf-invalidpin", chan->language);
718                                                         if (!res)
719                                                                 ast_waitstream(chan, "");
720                                                         res = -1;
721                                                         if (allowretry)
722                                                                 strcpy(confno, "");
723                                                 }
724                                         } else {
725                                                 res = -1;
726                                                 allowretry = 0;
727                                         }
728                                 } else {
729                                         /* No pin required */
730                                         allowretry = 0;
731
732                                         /* Run the conference */
733                                         res = conf_run(chan, cnf, confflags);
734                                 }
735                         }
736                 }
737         } while (allowretry);
738         /* Do the conference */
739         LOCAL_USER_REMOVE(u);
740         return res;
741 }
742
743 int unload_module(void)
744 {
745         STANDARD_HANGUP_LOCALUSERS;
746         ast_cli_unregister(&cli_show_confs);
747         ast_unregister_application(app2);
748         return ast_unregister_application(app);
749 }
750
751 int load_module(void)
752 {
753         ast_cli_register(&cli_show_confs);
754         ast_register_application(app2, count_exec, synopsis2, descrip2);
755         return ast_register_application(app, conf_exec, synopsis, descrip);
756 }
757
758 char *description(void)
759 {
760         return tdesc;
761 }
762
763 int usecount(void)
764 {
765         int res;
766         STANDARD_USECOUNT(res);
767         return res;
768 }
769
770 char *key()
771 {
772         return ASTERISK_GPL_KEY;
773 }