2 * Asterisk -- A telephony toolkit for Linux.
4 * Meet me conference bridge
6 * Copyright (C) 1999 - 2005, Digium, Inc.
8 * Mark Spencer <markster@digium.com>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
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/dsp.h>
23 #include <asterisk/musiconhold.h>
24 #include <asterisk/manager.h>
25 #include <asterisk/options.h>
26 #include <asterisk/cli.h>
27 #include <asterisk/say.h>
28 #include <asterisk/utils.h>
34 #include <sys/ioctl.h>
35 #include "../asterisk.h"
36 #include "../astconf.h"
39 #include <linux/zaptel.h>
42 #endif /* __linux__ */
44 static char *tdesc = "MeetMe conference bridge";
46 static char *app = "MeetMe";
47 static char *app2 = "MeetMeCount";
48 static char *app3 = "MeetMeAdmin";
50 static char *synopsis = "MeetMe conference bridge";
51 static char *synopsis2 = "MeetMe participant count";
52 static char *synopsis3 = "MeetMe conference Administration";
54 static char *descrip =
55 " MeetMe([confno][,[options][,pin]]): Enters the user into a specified MeetMe conference.\n"
56 "If the conference number is omitted, the user will be prompted to enter\n"
58 "MeetMe returns 0 if user pressed # to exit (see option 'p'), otherwise -1.\n"
59 "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n"
61 "The option string may contain zero or more of the following characters:\n"
62 " 'm' -- set monitor only mode (Listen only, no talking)\n"
63 " 't' -- set talk only mode. (Talk only, no listening)\n"
64 " 'T' -- set talker detection (sent to manager interface and meetme list)\n"
65 " 'i' -- announce user join/leave\n"
66 " 'p' -- allow user to exit the conference by pressing '#'\n"
67 " 'X' -- allow user to exit the conference by entering a valid single\n"
68 " digit extension ${MEETME_EXIT_CONTEXT} or the current context\n"
69 " if that variable is not defined.\n"
70 " 'd' -- dynamically add conference\n"
71 " 'D' -- dynamically add conference, prompting for a PIN\n"
72 " 'e' -- select an empty conference\n"
73 " 'E' -- select an empty pinless conference\n"
74 " 'v' -- video mode\n"
75 " 'r' -- Record conference (records as ${MEETME_RECORDINGFILE}\n"
76 " using format ${MEETME_RECORDINGFORMAT}). Default filename is\n"
77 " meetme-conf-rec-${CONFNO}-${UNIQUEID} and the default format is wav.\n"
78 " 'q' -- quiet mode (don't play enter/leave sounds)\n"
79 " 'M' -- enable music on hold when the conference has a single caller\n"
80 " 'x' -- close the conference when last marked user exits\n"
81 " 'w' -- wait until the marked user enters the conference\n"
82 " 'b' -- run AGI script specified in ${MEETME_AGI_BACKGROUND}\n"
83 " Default: conf-background.agi\n"
84 " (Note: This does not work with non-Zap channels in the same conference)\n"
85 " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n"
86 " 'a' -- set admin mode\n"
87 " 'A' -- set marked mode\n"
88 " 'P' -- always prompt for the pin even if it is specified\n";
90 static char *descrip2 =
91 " MeetMeCount(confno[|var]): Plays back the number of users in the specifiedi\n"
92 "MeetMe conference. If var is specified, playback will be skipped and the value\n"
93 "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n"
94 "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n";
96 static char *descrip3 =
97 " MeetMeAdmin(confno,command[,user]): Run admin command for conference\n"
98 " 'K' -- Kick all users out of conference\n"
99 " 'k' -- Kick one user out of conference\n"
100 " 'e' -- Eject last user that joined\n"
101 " 'L' -- Lock conference\n"
102 " 'l' -- Unlock conference\n"
103 " 'M' -- Mute conference\n"
104 " 'm' -- Unmute conference\n"
105 " 'N' -- Mute entire conference (except admin)\n"
106 " 'n' -- Unmute entire conference (except admin)\n"
113 static struct ast_conference {
114 char confno[AST_MAX_EXTENSION]; /* Conference */
115 struct ast_channel *chan; /* Announcements channel */
116 int fd; /* Announcements fd */
117 int zapconf; /* Zaptel Conf # */
118 int users; /* Number of active users */
119 int markedusers; /* Number of marked users */
120 struct ast_conf_user *firstuser; /* Pointer to the first user struct */
121 struct ast_conf_user *lastuser; /* Pointer to the last user struct */
122 time_t start; /* Start time (s) */
123 int recording; /* recording status */
124 int isdynamic; /* Created on the fly? */
125 int locked; /* Is the conference locked? */
126 pthread_t recordthread; /* thread for recording */
127 pthread_attr_t attr; /* thread attribute */
128 char *recordingfilename; /* Filename to record the Conference into */
129 char *recordingformat; /* Format to record the Conference in */
130 char pin[AST_MAX_EXTENSION]; /* If protected by a PIN */
131 char pinadmin[AST_MAX_EXTENSION]; /* If protected by a admin PIN */
132 struct ast_conference *next;
135 struct ast_conf_user {
136 int user_no; /* User Number */
137 struct ast_conf_user *prevuser; /* Pointer to the previous user */
138 struct ast_conf_user *nextuser; /* Pointer to the next user */
139 int userflags; /* Flags as set in the conference */
140 int adminflags; /* Flags set by the Admin */
141 struct ast_channel *chan; /* Connected channel */
142 int talking; /* Is user talking */
143 char usrvalue[50]; /* Custom User Value */
144 char namerecloc[AST_MAX_EXTENSION]; /* Name Recorded file Location */
145 time_t jointime; /* Time the user joined the conference */
148 #define ADMINFLAG_MUTED (1 << 1) /* User is muted */
149 #define ADMINFLAG_KICKME (1 << 2) /* User is kicked */
150 #define MEETME_DELAYDETECTTALK 300
151 #define MEETME_DELAYDETECTENDTALK 1000
153 AST_MUTEX_DEFINE_STATIC(conflock);
155 static int admin_exec(struct ast_channel *chan, void *data);
157 static void *recordthread(void *args);
165 #define MEETME_RECORD_OFF 0
166 #define MEETME_RECORD_ACTIVE 1
167 #define MEETME_RECORD_TERMINATE 2
169 #define CONF_SIZE 320
171 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */
172 #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */
173 #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */
174 #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */
175 #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */
176 #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */
177 #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */
178 #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */
179 #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */
180 #define CONFFLAG_MARKEDEXIT (1 << 10) /* If set the MeetMe will return if all marked with this flag left */
181 #define CONFFLAG_WAITMARKED (1 << 11) /* If set, the MeetMe will wait until a marked user enters */
182 #define CONFFLAG_EXIT_CONTEXT (1 << 12) /* If set, the MeetMe will exit to the specified context */
183 #define CONFFLAG_MARKEDUSER (1 << 13) /* If set, the user will be marked */
184 #define CONFFLAG_INTROUSER (1 << 14) /* If set, user will be ask record name on entry of conference */
185 #define CONFFLAG_RECORDCONF (1<< 15) /* If set, the MeetMe will be recorded */
186 #define CONFFLAG_MONITORTALKER (1 << 16) /* If set, the user will be monitored if the user is talking or not */
188 static char *istalking(int x)
193 return "(unmonitored)";
195 return "(not talking)";
198 static int careful_write(int fd, unsigned char *data, int len)
203 x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT;
204 res = ioctl(fd, ZT_IOMUX, &x);
206 res = write(fd, data, len);
208 if (errno != EAGAIN) {
209 ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno));
220 static void conf_play(struct ast_channel *chan, struct ast_conference *conf, int sound)
225 if (!chan->_softhangup)
226 res = ast_autoservice_start(chan);
227 ast_mutex_lock(&conflock);
242 careful_write(conf->fd, data, len);
243 ast_mutex_unlock(&conflock);
245 ast_autoservice_stop(chan);
248 static struct ast_conference *build_conf(char *confno, char *pin, char *pinadmin, int make, int dynamic)
250 struct ast_conference *cnf;
251 struct zt_confinfo ztc;
252 ast_mutex_lock(&conflock);
255 if (!strcmp(confno, cnf->confno))
259 if (!cnf && (make || dynamic)) {
260 cnf = malloc(sizeof(struct ast_conference));
263 memset(cnf, 0, sizeof(struct ast_conference));
264 strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
265 strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
266 strncpy(cnf->pinadmin, pinadmin, sizeof(cnf->pinadmin) - 1);
267 cnf->markedusers = 0;
268 cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
270 cnf->fd = cnf->chan->fds[0]; /* for use by conf_play() */
272 ast_log(LOG_WARNING, "Unable to open pseudo channel - trying device\n");
273 cnf->fd = open("/dev/zap/pseudo", O_RDWR);
275 ast_log(LOG_WARNING, "Unable to open pseudo device\n");
281 memset(&ztc, 0, sizeof(ztc));
282 /* Setup a new zap conference */
285 ztc.confmode = ZT_CONF_CONFANN | ZT_CONF_CONFANNMON;
286 if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) {
287 ast_log(LOG_WARNING, "Error setting conference\n");
289 ast_hangup(cnf->chan);
296 /* Fill the conference struct */
297 cnf->start = time(NULL);
298 cnf->zapconf = ztc.confno;
299 cnf->isdynamic = dynamic;
300 cnf->firstuser = NULL;
301 cnf->lastuser = NULL;
303 if (option_verbose > 2)
304 ast_verbose(VERBOSE_PREFIX_3 "Created MeetMe conference %d for conference '%s'\n", cnf->zapconf, cnf->confno);
308 ast_log(LOG_WARNING, "Out of memory\n");
311 ast_mutex_unlock(&conflock);
315 static int confs_show(int fd, int argc, char **argv)
317 ast_cli(fd, "Deprecated! Please use 'meetme' instead.\n");
318 return RESULT_SUCCESS;
321 static char show_confs_usage[] =
322 "Deprecated! Please use 'meetme' instead.\n";
324 static struct ast_cli_entry cli_show_confs = {
325 { "show", "conferences", NULL }, confs_show,
326 "Show status of conferences", show_confs_usage, NULL };
328 static int conf_cmd(int fd, int argc, char **argv) {
329 /* Process the command */
330 struct ast_conference *cnf;
331 struct ast_conf_user *user;
333 int i = 0, total = 0;
335 char *header_format = "%-14s %-14s %-10s %-8s %-8s\n";
336 char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n";
337 char cmdline[1024] = "";
340 ast_cli(fd, "Invalid Arguments.\n");
341 /* Check for length so no buffer will overflow... */
342 for (i = 0; i < argc; i++) {
343 if (strlen(argv[i]) > 100)
344 ast_cli(fd, "Invalid Arguments.\n");
347 /* 'MeetMe': List all the conferences */
351 ast_cli(fd, "No active MeetMe conferences.\n");
352 return RESULT_SUCCESS;
354 ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Creation");
356 if (cnf->markedusers == 0)
357 strncpy(cmdline, "N/A ", sizeof(cmdline) - 1);
359 snprintf(cmdline, sizeof(cmdline), "%4.4d", cnf->markedusers);
360 hr = (now - cnf->start) / 3600;
361 min = ((now - cnf->start) % 3600) / 60;
362 sec = (now - cnf->start) % 60;
364 ast_cli(fd, data_format, cnf->confno, cnf->users, cmdline, hr, min, sec, cnf->isdynamic ? "Dynamic" : "Static");
369 ast_cli(fd, "* Total number of MeetMe users: %d\n", total);
370 return RESULT_SUCCESS;
373 return RESULT_SHOWUSAGE;
374 strncpy(cmdline, argv[2], sizeof(cmdline) - 1); /* Argv 2: conference number */
375 if (strstr(argv[1], "lock")) {
376 if (strcmp(argv[1], "lock") == 0) {
378 strncat(cmdline, "|L", sizeof(cmdline) - strlen(cmdline) - 1);
381 strncat(cmdline, "|l", sizeof(cmdline) - strlen(cmdline) - 1);
383 } else if (strstr(argv[1], "mute")) {
385 return RESULT_SHOWUSAGE;
386 if (strcmp(argv[1], "mute") == 0) {
388 if (strcmp(argv[3], "all") == 0) {
389 strncat(cmdline, "|N", sizeof(cmdline) - strlen(cmdline) - 1);
391 strncat(cmdline, "|M|", sizeof(cmdline) - strlen(cmdline) - 1);
392 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
396 if (strcmp(argv[3], "all") == 0) {
397 strncat(cmdline, "|n", sizeof(cmdline) - strlen(cmdline) - 1);
399 strncat(cmdline, "|m|", sizeof(cmdline) - strlen(cmdline) - 1);
400 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
403 } else if (strcmp(argv[1], "kick") == 0) {
405 return RESULT_SHOWUSAGE;
406 if (strcmp(argv[3], "all") == 0) {
408 strncat(cmdline, "|K", sizeof(cmdline) - strlen(cmdline) - 1);
410 /* Kick a single user */
411 strncat(cmdline, "|k|", sizeof(cmdline) - strlen(cmdline) - 1);
412 strncat(cmdline, argv[3], sizeof(cmdline) - strlen(cmdline) - 1);
414 } else if(strcmp(argv[1], "list") == 0) {
415 /* List all the users in a conference */
417 ast_cli(fd, "No active conferences.\n");
418 return RESULT_SUCCESS;
421 /* Find the right conference */
423 if (strcmp(cnf->confno, argv[2]) == 0)
428 ast_cli(fd, "No such conference: %s.\n",argv[2]);
429 return RESULT_SUCCESS;
432 /* Show all the users */
433 user = cnf->firstuser;
435 ast_cli(fd, "User #: %i Channel: %s %s %s %s %s\n", user->user_no, user->chan->name, (user->userflags & CONFFLAG_ADMIN) ? "(Admin)" : "", (user->userflags & CONFFLAG_MONITOR) ? "(Listen only)" : "", (user->adminflags & ADMINFLAG_MUTED) ? "(Admn Muted)" : "", istalking(user->talking));
436 user = user->nextuser;
438 return RESULT_SUCCESS;
440 return RESULT_SHOWUSAGE;
441 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
442 admin_exec(NULL, cmdline);
446 static char *complete_confcmd(char *line, char *word, int pos, int state) {
447 #define CONF_COMMANDS 6
448 int which = 0, x = 0;
449 struct ast_conference *cnf = NULL;
450 struct ast_conf_user *usr = NULL;
453 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
458 for (x = 0;x < CONF_COMMANDS; x++) {
459 if (!strncasecmp(cmds[x], word, strlen(word))) {
460 if (++which > state) {
461 return strdup(cmds[x]);
465 } else if (pos == 2) {
466 /* Conference Number */
467 ast_mutex_lock(&conflock);
470 if (!strncasecmp(word, cnf->confno, strlen(word))) {
476 ast_mutex_unlock(&conflock);
477 return cnf ? strdup(cnf->confno) : NULL;
478 } else if (pos == 3) {
479 /* User Number || Conf Command option*/
480 if (strstr(line, "mute") || strstr(line, "kick")) {
481 if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
482 return strdup("all");
485 ast_mutex_lock(&conflock);
488 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
489 myline = ast_strdupa(line);
490 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
491 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
496 if (strcmp(confno, cnf->confno) == 0) {
502 /* Search for the user */
503 usr = cnf->firstuser;
505 snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
506 if (!strncasecmp(word, usrno, strlen(word))) {
513 ast_mutex_unlock(&conflock);
514 return usr ? strdup(usrno) : NULL;
520 static char conf_usage[] =
521 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
522 " Executes a command for the conference or on a conferee\n";
524 static struct ast_cli_entry cli_conf = {
525 { "meetme", NULL, NULL }, conf_cmd,
526 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
528 static int confnonzero(void *ptr)
530 struct ast_conference *conf = ptr;
532 ast_mutex_lock(&conflock);
533 res = (conf->markedusers == 0);
534 ast_mutex_unlock(&conflock);
538 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
540 struct ast_conference *prev=NULL, *cur;
541 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
542 struct ast_conf_user *usr = NULL;
544 struct zt_confinfo ztc;
546 struct ast_channel *c;
561 int using_pseudo = 0;
563 struct ast_dsp *dsp=NULL;
567 char *agifiledefault = "conf-background.agi";
568 char meetmesecs[30] = "";
569 char exitcontext[AST_MAX_EXTENSION] = "";
570 char recordingtmp[AST_MAX_EXTENSION] = "";
574 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
575 char *buf = __buf + AST_FRIENDLY_OFFSET;
578 ast_log(LOG_ERROR, "Out of memory\n");
581 memset(user, 0, sizeof(struct ast_conf_user));
583 if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
584 conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE");
585 if (!conf->recordingfilename) {
586 snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid);
587 conf->recordingfilename = ast_strdupa(recordingtmp);
589 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
590 if (!conf->recordingformat) {
591 snprintf(recordingtmp,sizeof(recordingtmp), "wav");
592 conf->recordingformat = ast_strdupa(recordingtmp);
594 pthread_attr_init(&conf->attr);
595 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
596 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat);
597 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
600 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
602 time(&user->jointime);
605 /* Sorry, but this confernce is locked! */
606 if (!ast_streamfile(chan, "conf-locked", chan->language))
607 ast_waitstream(chan, "");
612 if (confflags & CONFFLAG_MARKEDUSER)
615 ast_mutex_lock(&conflock);
616 if (conf->firstuser == NULL) {
617 /* Fill the first new User struct */
619 user->nextuser = NULL;
620 user->prevuser = NULL;
621 conf->firstuser = user;
622 conf->lastuser = user;
624 /* Fill the new user struct */
625 user->user_no = conf->lastuser->user_no + 1;
626 user->prevuser = conf->lastuser;
627 user->nextuser = NULL;
628 if (conf->lastuser->nextuser != NULL) {
629 ast_log(LOG_WARNING, "Error in User Management!\n");
630 ast_mutex_unlock(&conflock);
633 conf->lastuser->nextuser = user;
634 conf->lastuser = user;
638 user->userflags = confflags;
639 user->adminflags = 0;
641 ast_mutex_unlock(&conflock);
642 origquiet = confflags & CONFFLAG_QUIET;
643 if (confflags & CONFFLAG_EXIT_CONTEXT) {
644 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
645 strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
646 else if (!ast_strlen_zero(chan->macrocontext))
647 strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
649 strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
651 snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme-username-%s-%d",AST_SPOOL_DIR,conf->confno,user->user_no);
653 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER))
654 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
656 while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
657 confflags &= ~CONFFLAG_QUIET;
658 confflags |= origquiet;
659 /* XXX Announce that we're waiting on the conference lead to join */
660 if (!(confflags & CONFFLAG_QUIET)) {
661 res = ast_streamfile(chan, "vm-dialout", chan->language);
663 res = ast_waitstream(chan, "");
666 /* If we're waiting with hold music, set to silent mode */
668 confflags |= CONFFLAG_QUIET;
669 ast_moh_start(chan, NULL);
670 res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
674 ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
679 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
680 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
681 if (ast_waitstream(chan, "") < 0)
687 /* Set it into linear mode (write) */
688 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
689 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
693 /* Set it into linear mode (read) */
694 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
695 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
698 ast_indicate(chan, -1);
699 retryzap = strcasecmp(chan->type, "Zap");
701 origfd = chan->fds[0];
703 fd = open("/dev/zap/pseudo", O_RDWR);
705 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
709 /* Make non-blocking */
710 flags = fcntl(fd, F_GETFL);
712 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
716 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
717 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
721 /* Setup buffering information */
722 memset(&bi, 0, sizeof(bi));
723 bi.bufsize = CONF_SIZE/2;
724 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
725 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
727 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
728 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
733 if (ioctl(fd, ZT_SETLINEAR, &x)) {
734 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
740 /* XXX Make sure we're not running on a pseudo channel XXX */
744 memset(&ztc, 0, sizeof(ztc));
745 /* Check to see if we're in a conference... */
747 if (ioctl(fd, ZT_GETCONF, &ztc)) {
748 ast_log(LOG_WARNING, "Error getting conference\n");
753 /* Whoa, already in a conference... Retry... */
755 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
760 memset(&ztc, 0, sizeof(ztc));
761 /* Add us to the conference */
763 ztc.confno = conf->zapconf;
764 ast_mutex_lock(&conflock);
765 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
766 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
767 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
768 ast_waitstream(conf->chan, "");
769 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
770 ast_waitstream(conf->chan, "");
774 if (confflags & CONFFLAG_MONITOR)
775 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
776 else if (confflags & CONFFLAG_TALKER)
777 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
779 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
781 if (ioctl(fd, ZT_SETCONF, &ztc)) {
782 ast_log(LOG_WARNING, "Error setting conference\n");
784 ast_mutex_unlock(&conflock);
787 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
789 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
794 chan->name, chan->uniqueid, conf->confno, user->user_no);
796 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
798 if (!(confflags & CONFFLAG_QUIET))
799 conf_play(chan, conf, ENTER);
801 ast_mutex_unlock(&conflock);
802 if (confflags & CONFFLAG_AGI) {
804 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
805 or use default filename of conf-background.agi */
807 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
809 agifile = agifiledefault;
811 if (!strcasecmp(chan->type,"Zap")) {
812 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
814 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
816 /* Find a pointer to the agi app and execute the script */
817 app = pbx_findapp("agi");
819 ret = pbx_exec(chan, app, agifile, 1);
821 ast_log(LOG_WARNING, "Could not find application (agi)\n");
824 if (!strcasecmp(chan->type,"Zap")) {
825 /* Remove CONFMUTE mode on Zap channel */
827 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
830 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
831 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
833 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
835 if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
836 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
842 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
844 /* Update the struct with the actual confflags */
845 user->userflags = confflags;
847 /* trying to add moh for single person conf */
848 if (confflags & CONFFLAG_MOH) {
849 if (conf->users == 1) {
850 if (musiconhold == 0) {
851 ast_moh_start(chan, NULL);
862 /* Leave if the last marked user left */
863 if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
868 /* Check if the admin changed my modes */
869 if (user->adminflags) {
870 /* Set the new modes */
871 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
872 ztc.confmode ^= ZT_CONF_TALKER;
873 if (ioctl(fd, ZT_SETCONF, &ztc)) {
874 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
879 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
880 ztc.confmode |= ZT_CONF_TALKER;
881 if (ioctl(fd, ZT_SETCONF, &ztc)) {
882 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
887 if (user->adminflags & ADMINFLAG_KICKME) {
888 /* You have been kicked. */
889 if (!ast_streamfile(chan, "conf-kicked", chan->language))
890 ast_waitstream(chan, "");
894 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
895 ztc.confmode |= ZT_CONF_TALKER;
896 if (ioctl(fd, ZT_SETCONF, &ztc)) {
897 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
904 if (c->fds[0] != origfd) {
906 /* Kill old pseudo */
909 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
917 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
918 if (confflags & CONFFLAG_MONITORTALKER) {
920 if (user->talking == -1)
923 res = ast_dsp_silence(dsp, f, &totalsilence);
924 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
926 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
931 chan->name, chan->uniqueid, conf->confno, user->user_no);
933 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
935 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
940 chan->name, chan->uniqueid, conf->confno, user->user_no);
944 /* Carefully write */
945 careful_write(fd, f->data, f->datalen);
947 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
949 tmp[0] = f->subclass;
951 if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
952 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
953 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
958 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
961 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
965 if ((confflags & CONFFLAG_ADMIN)) {
969 /* Record this sound! */
970 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
971 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
978 case '1': /* Un/Mute */
980 if (ztc.confmode & ZT_CONF_TALKER) {
981 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
982 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
984 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
985 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
987 if (ioctl(fd, ZT_SETCONF, &ztc)) {
988 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
992 if (ztc.confmode & ZT_CONF_TALKER) {
993 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
994 ast_waitstream(chan, "");
996 if (!ast_streamfile(chan, "conf-muted", chan->language))
997 ast_waitstream(chan, "");
1000 case '2': /* Un/Lock the Conference */
1004 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1005 ast_waitstream(chan, "");
1008 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1009 ast_waitstream(chan, "");
1012 case '3': /* Eject last user */
1014 usr = conf->lastuser;
1015 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1016 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1017 ast_waitstream(chan, "");
1019 usr->adminflags |= ADMINFLAG_KICKME;
1020 ast_stopstream(chan);
1024 /* Play an error message! */
1025 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1026 ast_waitstream(chan, "");
1034 /* Record this sound! */
1035 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1036 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1043 case '1': /* Un/Mute */
1045 if (ztc.confmode & ZT_CONF_TALKER) {
1046 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1047 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1048 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1049 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1050 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1052 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1053 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1057 if (ztc.confmode & ZT_CONF_TALKER) {
1058 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1059 ast_waitstream(chan, "");
1061 if (!ast_streamfile(chan, "conf-muted", chan->language))
1062 ast_waitstream(chan, "");
1067 /* Play an error message! */
1068 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1069 ast_waitstream(chan, "");
1075 ast_moh_start(chan, NULL);
1077 } else if (option_debug) {
1078 ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1081 } else if (outfd > -1) {
1082 res = read(outfd, buf, CONF_SIZE);
1084 memset(&fr, 0, sizeof(fr));
1085 fr.frametype = AST_FRAME_VOICE;
1086 fr.subclass = AST_FORMAT_SLINEAR;
1090 fr.offset = AST_FRIENDLY_OFFSET;
1091 if (ast_write(chan, &fr) < 0) {
1092 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1096 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1103 /* Take out of conference */
1104 /* Add us to the conference */
1108 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1109 ast_log(LOG_WARNING, "Error setting conference\n");
1113 ast_mutex_lock(&conflock);
1114 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1115 conf_play(chan, conf, LEAVE);
1117 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1118 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1119 if ((conf->chan) && (conf->users > 1)) {
1120 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1121 ast_waitstream(conf->chan, "");
1122 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1123 ast_waitstream(conf->chan, "");
1125 ast_filedelete(user->namerecloc, NULL);
1128 ast_mutex_unlock(&conflock);
1132 ast_mutex_lock(&conflock);
1133 if (confflags & CONFFLAG_MONITORTALKER && dsp)
1136 if (user->user_no) { /* Only cleanup users who really joined! */
1137 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1142 chan->name, chan->uniqueid, conf->confno, user->user_no);
1145 if (confflags & CONFFLAG_MARKEDUSER)
1146 conf->markedusers--;
1149 /* No more users -- close this one out */
1153 prev->next = conf->next;
1162 ast_log(LOG_WARNING, "Conference not found\n");
1163 if (conf->recording == MEETME_RECORD_ACTIVE) {
1164 conf->recording = MEETME_RECORD_TERMINATE;
1165 ast_mutex_unlock(&conflock);
1167 ast_mutex_lock(&conflock);
1168 if (conf->recording == MEETME_RECORD_OFF)
1170 ast_mutex_unlock(&conflock);
1174 ast_hangup(conf->chan);
1179 /* Remove the user struct */
1180 if (user == conf->firstuser) {
1181 if (user->nextuser) {
1182 /* There is another entry */
1183 user->nextuser->prevuser = NULL;
1185 /* We are the only entry */
1186 conf->lastuser = NULL;
1188 /* In either case */
1189 conf->firstuser = user->nextuser;
1190 } else if (user == conf->lastuser){
1192 user->prevuser->nextuser = NULL;
1194 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1195 conf->lastuser = user->prevuser;
1198 user->nextuser->prevuser = user->prevuser;
1200 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1202 user->prevuser->nextuser = user->nextuser;
1204 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1207 /* Return the number of seconds the user was in the conf */
1208 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1209 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1212 ast_mutex_unlock(&conflock);
1216 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1218 struct ast_config *cfg;
1219 struct ast_variable *var;
1220 struct ast_conference *cnf;
1222 /* Check first in the conference list */
1223 ast_mutex_lock(&conflock);
1226 if (!strcmp(confno, cnf->confno))
1230 ast_mutex_unlock(&conflock);
1234 /* No need to parse meetme.conf */
1235 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1237 if (dynamic_pin[0] == 'q') {
1238 /* Query the user to enter a PIN */
1239 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1241 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1243 cnf = build_conf(confno, "", "", make, dynamic);
1246 /* Check the config */
1247 cfg = ast_config_load("meetme.conf");
1249 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1252 var = ast_variable_browse(cfg, "rooms");
1254 if (!strcasecmp(var->name, "conf")) {
1255 /* Separate the PIN */
1256 char *pin, *pinadmin, *conf;
1258 if ((pinadmin = ast_strdupa(var->value))) {
1259 conf = strsep(&pinadmin, "|,");
1260 pin = strsep(&pinadmin, "|,");
1261 if (!strcasecmp(conf, confno)) {
1262 /* Bingo it's a valid conference */
1265 cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1267 cnf = build_conf(confno, pin, "", make, dynamic);
1270 cnf = build_conf(confno, "", pinadmin, make, dynamic);
1272 cnf = build_conf(confno, "", "", make, dynamic);
1280 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1282 ast_config_destroy(cfg);
1284 } else if (dynamic_pin) {
1285 /* Correct for the user selecting 'D' instead of 'd' to have
1286 someone join into a conference that has already been created
1288 if (dynamic_pin[0] == 'q')
1289 dynamic_pin[0] = '\0';
1294 /*--- count_exec: The MeetmeCount application */
1295 static int count_exec(struct ast_channel *chan, void *data)
1297 struct localuser *u;
1299 struct ast_conference *conf;
1301 char *confnum, *localdata;
1304 if (!data || ast_strlen_zero(data)) {
1305 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1308 localdata = ast_strdupa(data);
1310 confnum = strsep(&localdata,"|");
1311 conf = find_conf(chan, confnum, 0, 0, NULL);
1313 count = conf->users;
1317 if (localdata && !ast_strlen_zero(localdata)){
1318 /* have var so load it and exit */
1319 snprintf(val,sizeof(val), "%i",count);
1320 pbx_builtin_setvar_helper(chan, localdata,val);
1322 if (chan->_state != AST_STATE_UP)
1324 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1326 LOCAL_USER_REMOVE(u);
1330 /*--- conf_exec: The meetme() application */
1331 static int conf_exec(struct ast_channel *chan, void *data)
1334 struct localuser *u;
1335 char confno[AST_MAX_EXTENSION] = "";
1338 struct ast_conference *cnf;
1341 int empty = 0, empty_no_pin = 0;
1342 int always_prompt = 0;
1343 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1345 if (!data || ast_strlen_zero(data)) {
1352 if (chan->_state != AST_STATE_UP)
1355 info = ast_strdupa((char *)notdata);
1358 char *tmp = strsep(&info, "|");
1359 strncpy(confno, tmp, sizeof(confno) - 1);
1360 if (ast_strlen_zero(confno)) {
1365 inflags = strsep(&info, "|");
1367 inpin = strsep(&info, "|");
1369 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1372 if (strchr(inflags, 'a'))
1373 confflags |= CONFFLAG_ADMIN;
1374 if (strchr(inflags, 'T'))
1375 confflags |= CONFFLAG_MONITORTALKER;
1376 if (strchr(inflags, 'i'))
1377 confflags |= CONFFLAG_INTROUSER;
1378 if (strchr(inflags, 'm'))
1379 confflags |= CONFFLAG_MONITOR;
1380 if (strchr(inflags, 'p'))
1381 confflags |= CONFFLAG_POUNDEXIT;
1382 if (strchr(inflags, 's'))
1383 confflags |= CONFFLAG_STARMENU;
1384 if (strchr(inflags, 't'))
1385 confflags |= CONFFLAG_TALKER;
1386 if (strchr(inflags, 'q'))
1387 confflags |= CONFFLAG_QUIET;
1388 if (strchr(inflags, 'M'))
1389 confflags |= CONFFLAG_MOH;
1390 if (strchr(inflags, 'x'))
1391 confflags |= CONFFLAG_MARKEDEXIT;
1392 if (strchr(inflags, 'X'))
1393 confflags |= CONFFLAG_EXIT_CONTEXT;
1394 if (strchr(inflags, 'A'))
1395 confflags |= CONFFLAG_MARKEDUSER;
1396 if (strchr(inflags, 'b'))
1397 confflags |= CONFFLAG_AGI;
1398 if (strchr(inflags, 'w'))
1399 confflags |= CONFFLAG_WAITMARKED;
1400 if (strchr(inflags, 'r'))
1401 confflags |= CONFFLAG_RECORDCONF;
1402 if (strchr(inflags, 'd'))
1404 if (strchr(inflags, 'D')) {
1407 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1410 if (strchr(inflags, 'e'))
1412 if (strchr(inflags, 'E')) {
1416 if (strchr(inflags, 'P'))
1425 struct ast_config *cfg;
1426 struct ast_variable *var;
1429 memset(map, 0, sizeof(map));
1431 ast_mutex_lock(&conflock);
1434 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1435 /* Disqualify in use conference */
1436 if (confno_int >= 0 && confno_int < 1024)
1441 ast_mutex_unlock(&conflock);
1443 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1444 if ((empty_no_pin) || (!dynamic)) {
1445 cfg = ast_config_load("meetme.conf");
1447 var = ast_variable_browse(cfg, "rooms");
1449 if (!strcasecmp(var->name, "conf")) {
1450 char *stringp = ast_strdupa(var->value);
1452 char *confno_tmp = strsep(&stringp, "|,");
1454 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1455 if ((confno_int >= 0) && (confno_int < 1024)) {
1456 if (stringp && empty_no_pin) {
1462 /* For static: run through the list and see if this conference is empty */
1463 ast_mutex_lock(&conflock);
1466 if (!strcmp(confno_tmp, cnf->confno)) {
1467 /* The conference exists, therefore it's not empty */
1473 ast_mutex_unlock(&conflock);
1475 /* At this point, we have a confno_tmp (static conference) that is empty */
1476 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1477 /* Case 1: empty_no_pin and pin is nonexistant (NULL)
1478 * Case 2: empty_no_pin and pin is blank (but not NULL)
1479 * Case 3: not empty_no_pin
1481 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1483 /* XXX the map is not complete (but we do have a confno) */
1488 ast_log(LOG_ERROR, "Out of memory\n");
1493 ast_config_destroy(cfg);
1496 /* Select first conference number not in use */
1497 if (ast_strlen_zero(confno) && dynamic) {
1498 for (i=0;i<1024;i++) {
1500 snprintf(confno, sizeof(confno), "%d", i);
1507 if (ast_strlen_zero(confno)) {
1508 res = ast_streamfile(chan, "conf-noempty", chan->language);
1510 ast_waitstream(chan, "");
1512 if (sscanf(confno, "%d", &confno_int) == 1) {
1513 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1515 ast_waitstream(chan, "");
1516 res = ast_say_digits(chan, confno_int, "", chan->language);
1519 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1523 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1524 /* Prompt user for conference number */
1525 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1527 /* Don't try to validate when we catch an error */
1533 if (!ast_strlen_zero(confno)) {
1534 /* Check the validity of the conference */
1535 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1537 res = ast_streamfile(chan, "conf-invalid", chan->language);
1539 ast_waitstream(chan, "");
1544 if ((!ast_strlen_zero(cnf->pin) && ! (confflags & CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && (confflags & CONFFLAG_ADMIN))) {
1545 char pin[AST_MAX_EXTENSION]="";
1548 /* Allow the pin to be retried up to 3 times */
1549 for (j=0; j<3; j++) {
1550 if (*the_pin && (always_prompt==0)) {
1551 strncpy(pin, the_pin, sizeof(pin) - 1);
1554 /* Prompt user for pin if pin is required */
1555 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1558 if (!strcasecmp(pin, cnf->pin) || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1562 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
1563 confflags |= CONFFLAG_ADMIN;
1564 /* Run the conference */
1565 res = conf_run(chan, cnf, confflags);
1569 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1571 ast_waitstream(chan, AST_DIGIT_ANY);
1586 /* Don't retry pin with a static pin */
1587 if (*the_pin && (always_prompt==0)) {
1592 /* No pin required */
1595 /* Run the conference */
1596 res = conf_run(chan, cnf, confflags);
1600 } while (allowretry);
1601 /* Do the conference */
1602 LOCAL_USER_REMOVE(u);
1606 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1607 struct ast_conf_user *user = NULL;
1608 char usrno[1024] = "";
1609 if (conf && callerident) {
1610 user = conf->firstuser;
1612 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1613 if (strcmp(usrno, callerident) == 0)
1615 user = user->nextuser;
1621 /*--- admin_exec: The MeetMeadmin application */
1622 /* MeetMeAdmin(confno, command, caller) */
1623 static int admin_exec(struct ast_channel *chan, void *data) {
1624 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1625 struct ast_conference *cnf;
1626 struct ast_conf_user *user = NULL;
1628 ast_mutex_lock(&conflock);
1629 /* The param has the conference number the user and the command to execute */
1630 if (data && !ast_strlen_zero(data)) {
1631 params = ast_strdupa((char *) data);
1632 conf = strsep(¶ms, "|");
1633 command = strsep(¶ms, "|");
1634 caller = strsep(¶ms, "|");
1637 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1638 ast_mutex_unlock(&conflock);
1643 if (strcmp(cnf->confno, conf) == 0)
1649 user = find_user(cnf, caller);
1652 switch((int) (*command)) {
1653 case 76: /* L: Lock */
1656 case 108: /* l: Unlock */
1659 case 75: /* K: kick all users*/
1660 user = cnf->firstuser;
1662 user->adminflags |= ADMINFLAG_KICKME;
1663 if (user->nextuser) {
1664 user = user->nextuser;
1670 case 101: /* e: Eject last user*/
1671 user = cnf->lastuser;
1672 if (!(user->userflags & CONFFLAG_ADMIN)) {
1673 user->adminflags |= ADMINFLAG_KICKME;
1676 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1678 case 77: /* M: Mute */
1680 user->adminflags |= ADMINFLAG_MUTED;
1682 ast_log(LOG_NOTICE, "Specified User not found!\n");
1685 case 78: /* N: Mute all users */
1686 user = cnf->firstuser;
1688 if (user && !(user->userflags & CONFFLAG_ADMIN))
1689 user->adminflags |= ADMINFLAG_MUTED;
1690 if (user->nextuser) {
1691 user = user->nextuser;
1697 case 109: /* m: Unmute */
1698 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1699 user->adminflags ^= ADMINFLAG_MUTED;
1701 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1704 case 110: /* n: Unmute all users */
1705 user = cnf->firstuser;
1707 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1708 user->adminflags ^= ADMINFLAG_MUTED;
1710 if (user->nextuser) {
1711 user = user->nextuser;
1717 case 107: /* k: Kick user */
1719 user->adminflags |= ADMINFLAG_KICKME;
1721 ast_log(LOG_NOTICE, "Specified User not found!");
1726 ast_log(LOG_NOTICE, "Conference Number not found\n");
1729 ast_mutex_unlock(&conflock);
1733 static void *recordthread(void *args)
1735 struct ast_conference *cnf;
1736 struct ast_frame *f=NULL;
1738 struct ast_filestream *s;
1741 cnf = (struct ast_conference *)args;
1742 if( !cnf || !cnf->chan ) {
1745 ast_stopstream(cnf->chan);
1746 flags = O_CREAT|O_TRUNC|O_WRONLY;
1747 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1750 cnf->recording = MEETME_RECORD_ACTIVE;
1751 while (ast_waitfor(cnf->chan, -1) > -1) {
1752 f = ast_read(cnf->chan);
1757 if (f->frametype == AST_FRAME_VOICE) {
1758 res = ast_writestream(s, f);
1763 if (cnf->recording == MEETME_RECORD_TERMINATE) {
1764 ast_mutex_lock(&conflock);
1765 ast_mutex_unlock(&conflock);
1769 cnf->recording = MEETME_RECORD_OFF;
1775 int unload_module(void)
1777 STANDARD_HANGUP_LOCALUSERS;
1778 ast_cli_unregister(&cli_show_confs);
1779 ast_cli_unregister(&cli_conf);
1780 ast_unregister_application(app3);
1781 ast_unregister_application(app2);
1782 return ast_unregister_application(app);
1785 int load_module(void)
1787 ast_cli_register(&cli_show_confs);
1788 ast_cli_register(&cli_conf);
1789 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1790 ast_register_application(app2, count_exec, synopsis2, descrip2);
1791 return ast_register_application(app, conf_exec, synopsis, descrip);
1795 char *description(void)
1803 STANDARD_USECOUNT(res);
1809 return ASTERISK_GPL_KEY;