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 ast_cli(fd,"%d users in that conference.\n",cnf->users);
439 return RESULT_SUCCESS;
441 return RESULT_SHOWUSAGE;
442 ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline);
443 admin_exec(NULL, cmdline);
447 static char *complete_confcmd(char *line, char *word, int pos, int state) {
448 #define CONF_COMMANDS 6
449 int which = 0, x = 0;
450 struct ast_conference *cnf = NULL;
451 struct ast_conf_user *usr = NULL;
454 char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "list"};
459 for (x = 0;x < CONF_COMMANDS; x++) {
460 if (!strncasecmp(cmds[x], word, strlen(word))) {
461 if (++which > state) {
462 return strdup(cmds[x]);
466 } else if (pos == 2) {
467 /* Conference Number */
468 ast_mutex_lock(&conflock);
471 if (!strncasecmp(word, cnf->confno, strlen(word))) {
477 ast_mutex_unlock(&conflock);
478 return cnf ? strdup(cnf->confno) : NULL;
479 } else if (pos == 3) {
480 /* User Number || Conf Command option*/
481 if (strstr(line, "mute") || strstr(line, "kick")) {
482 if ((state == 0) && (strstr(line, "kick") || strstr(line,"mute")) && !(strncasecmp(word, "all", strlen(word)))) {
483 return strdup("all");
486 ast_mutex_lock(&conflock);
489 /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */
490 myline = ast_strdupa(line);
491 if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) {
492 while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0))
497 if (strcmp(confno, cnf->confno) == 0) {
503 /* Search for the user */
504 usr = cnf->firstuser;
506 snprintf(usrno, sizeof(usrno), "%i", usr->user_no);
507 if (!strncasecmp(word, usrno, strlen(word))) {
514 ast_mutex_unlock(&conflock);
515 return usr ? strdup(usrno) : NULL;
521 static char conf_usage[] =
522 "Usage: meetme (un)lock|(un)mute|kick|list <confno> <usernumber>\n"
523 " Executes a command for the conference or on a conferee\n";
525 static struct ast_cli_entry cli_conf = {
526 { "meetme", NULL, NULL }, conf_cmd,
527 "Execute a command on a conference or conferee", conf_usage, complete_confcmd };
529 static int confnonzero(void *ptr)
531 struct ast_conference *conf = ptr;
533 ast_mutex_lock(&conflock);
534 res = (conf->markedusers == 0);
535 ast_mutex_unlock(&conflock);
539 static void conf_flush(int fd)
543 if (ioctl(fd, ZT_FLUSH, &x))
544 ast_log(LOG_WARNING, "Error flushing channel\n");
547 static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int confflags)
549 struct ast_conference *prev=NULL, *cur;
550 struct ast_conf_user *user = malloc(sizeof(struct ast_conf_user));
551 struct ast_conf_user *usr = NULL;
553 struct zt_confinfo ztc;
555 struct ast_channel *c;
570 int using_pseudo = 0;
572 struct ast_dsp *dsp=NULL;
576 char *agifiledefault = "conf-background.agi";
577 char meetmesecs[30] = "";
578 char exitcontext[AST_MAX_EXTENSION] = "";
579 char recordingtmp[AST_MAX_EXTENSION] = "";
583 char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET];
584 char *buf = __buf + AST_FRIENDLY_OFFSET;
587 ast_log(LOG_ERROR, "Out of memory\n");
590 memset(user, 0, sizeof(struct ast_conf_user));
592 if (confflags & CONFFLAG_RECORDCONF && conf->recording !=MEETME_RECORD_ACTIVE) {
593 conf->recordingfilename = pbx_builtin_getvar_helper(chan,"MEETME_RECORDINGFILE");
594 if (!conf->recordingfilename) {
595 snprintf(recordingtmp,sizeof(recordingtmp),"meetme-conf-rec-%s-%s",conf->confno,chan->uniqueid);
596 conf->recordingfilename = ast_strdupa(recordingtmp);
598 conf->recordingformat = pbx_builtin_getvar_helper(chan, "MEETME_RECORDINGFORMAT");
599 if (!conf->recordingformat) {
600 snprintf(recordingtmp,sizeof(recordingtmp), "wav");
601 conf->recordingformat = ast_strdupa(recordingtmp);
603 pthread_attr_init(&conf->attr);
604 pthread_attr_setdetachstate(&conf->attr, PTHREAD_CREATE_DETACHED);
605 ast_verbose(VERBOSE_PREFIX_4 "Starting recording of MeetMe Conference %s into file %s.%s.\n", conf->confno, conf->recordingfilename, conf->recordingformat);
606 ast_pthread_create(&conf->recordthread, &conf->attr, recordthread, conf);
609 user->user_no = 0; /* User number 0 means starting up user! (dead - not in the list!) */
611 time(&user->jointime);
614 /* Sorry, but this confernce is locked! */
615 if (!ast_streamfile(chan, "conf-locked", chan->language))
616 ast_waitstream(chan, "");
621 if (confflags & CONFFLAG_MARKEDUSER)
624 ast_mutex_lock(&conflock);
625 if (conf->firstuser == NULL) {
626 /* Fill the first new User struct */
628 user->nextuser = NULL;
629 user->prevuser = NULL;
630 conf->firstuser = user;
631 conf->lastuser = user;
633 /* Fill the new user struct */
634 user->user_no = conf->lastuser->user_no + 1;
635 user->prevuser = conf->lastuser;
636 user->nextuser = NULL;
637 if (conf->lastuser->nextuser != NULL) {
638 ast_log(LOG_WARNING, "Error in User Management!\n");
639 ast_mutex_unlock(&conflock);
642 conf->lastuser->nextuser = user;
643 conf->lastuser = user;
647 user->userflags = confflags;
648 user->adminflags = 0;
650 ast_mutex_unlock(&conflock);
651 origquiet = confflags & CONFFLAG_QUIET;
652 if (confflags & CONFFLAG_EXIT_CONTEXT) {
653 if ((agifile = pbx_builtin_getvar_helper(chan, "MEETME_EXIT_CONTEXT")))
654 strncpy(exitcontext, agifile, sizeof(exitcontext) - 1);
655 else if (!ast_strlen_zero(chan->macrocontext))
656 strncpy(exitcontext, chan->macrocontext, sizeof(exitcontext) - 1);
658 strncpy(exitcontext, chan->context, sizeof(exitcontext) - 1);
660 snprintf(user->namerecloc,sizeof(user->namerecloc),"%s/meetme-username-%s-%d",AST_SPOOL_DIR,conf->confno,user->user_no);
662 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER))
663 ast_record_review(chan,"vm-rec-name",user->namerecloc, 10,"sln", &duration, NULL);
665 while((confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) {
666 confflags &= ~CONFFLAG_QUIET;
667 confflags |= origquiet;
668 /* XXX Announce that we're waiting on the conference lead to join */
669 if (!(confflags & CONFFLAG_QUIET)) {
670 res = ast_streamfile(chan, "vm-dialout", chan->language);
672 res = ast_waitstream(chan, "");
675 /* If we're waiting with hold music, set to silent mode */
677 confflags |= CONFFLAG_QUIET;
678 ast_moh_start(chan, NULL);
679 res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf);
683 ast_log(LOG_DEBUG, "Got hangup on '%s' already\n", chan->name);
688 if (!(confflags & CONFFLAG_QUIET) && conf->users == 1) {
689 if (!ast_streamfile(chan, "conf-onlyperson", chan->language)) {
690 if (ast_waitstream(chan, "") < 0)
696 /* Set it into linear mode (write) */
697 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
698 ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", chan->name);
702 /* Set it into linear mode (read) */
703 if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
704 ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", chan->name);
707 ast_indicate(chan, -1);
708 retryzap = strcasecmp(chan->type, "Zap");
710 origfd = chan->fds[0];
712 fd = open("/dev/zap/pseudo", O_RDWR);
714 ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
718 /* Make non-blocking */
719 flags = fcntl(fd, F_GETFL);
721 ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno));
725 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
726 ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno));
730 /* Setup buffering information */
731 memset(&bi, 0, sizeof(bi));
732 bi.bufsize = CONF_SIZE/2;
733 bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
734 bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
736 if (ioctl(fd, ZT_SET_BUFINFO, &bi)) {
737 ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno));
742 if (ioctl(fd, ZT_SETLINEAR, &x)) {
743 ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno));
749 /* XXX Make sure we're not running on a pseudo channel XXX */
753 memset(&ztc, 0, sizeof(ztc));
754 /* Check to see if we're in a conference... */
756 if (ioctl(fd, ZT_GETCONF, &ztc)) {
757 ast_log(LOG_WARNING, "Error getting conference\n");
762 /* Whoa, already in a conference... Retry... */
764 ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n");
769 memset(&ztc, 0, sizeof(ztc));
770 /* Add us to the conference */
772 ztc.confno = conf->zapconf;
773 ast_mutex_lock(&conflock);
774 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER) && conf->users > 1) {
775 if (conf->chan && ast_fileexists(user->namerecloc, NULL, NULL)) {
776 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
777 ast_waitstream(conf->chan, "");
778 if (!ast_streamfile(conf->chan, "conf-hasjoin", chan->language))
779 ast_waitstream(conf->chan, "");
783 if (confflags & CONFFLAG_MONITOR)
784 ztc.confmode = ZT_CONF_CONFMON | ZT_CONF_LISTENER;
785 else if (confflags & CONFFLAG_TALKER)
786 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER;
788 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
790 if (ioctl(fd, ZT_SETCONF, &ztc)) {
791 ast_log(LOG_WARNING, "Error setting conference\n");
793 ast_mutex_unlock(&conflock);
796 ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf);
798 manager_event(EVENT_FLAG_CALL, "MeetmeJoin",
803 chan->name, chan->uniqueid, conf->confno, user->user_no);
805 if (!firstpass && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN)) {
807 if (!(confflags & CONFFLAG_QUIET))
808 conf_play(chan, conf, ENTER);
811 ast_mutex_unlock(&conflock);
812 if (confflags & CONFFLAG_AGI) {
814 /* Get name of AGI file to run from $(MEETME_AGI_BACKGROUND)
815 or use default filename of conf-background.agi */
817 agifile = pbx_builtin_getvar_helper(chan,"MEETME_AGI_BACKGROUND");
819 agifile = agifiledefault;
821 if (!strcasecmp(chan->type,"Zap")) {
822 /* Set CONFMUTE mode on Zap channel to mute DTMF tones */
824 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
826 /* Find a pointer to the agi app and execute the script */
827 app = pbx_findapp("agi");
829 ret = pbx_exec(chan, app, agifile, 1);
831 ast_log(LOG_WARNING, "Could not find application (agi)\n");
834 if (!strcasecmp(chan->type,"Zap")) {
835 /* Remove CONFMUTE mode on Zap channel */
837 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
840 if (!strcasecmp(chan->type,"Zap") && (confflags & CONFFLAG_STARMENU)) {
841 /* Set CONFMUTE mode on Zap channel to mute DTMF tones when the menu is enabled */
843 ast_channel_setoption(chan,AST_OPTION_TONE_VERIFY,&x,sizeof(char),0);
845 if (confflags & CONFFLAG_MONITORTALKER && !(dsp = ast_dsp_new())) {
846 ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
852 c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms);
854 /* Update the struct with the actual confflags */
855 user->userflags = confflags;
857 /* trying to add moh for single person conf */
858 if (confflags & CONFFLAG_MOH) {
859 if (conf->users == 1) {
860 if (musiconhold == 0) {
861 ast_moh_start(chan, NULL);
872 /* Leave if the last marked user left */
873 if (conf->markedusers == 0 && confflags & CONFFLAG_MARKEDEXIT) {
878 /* Check if the admin changed my modes */
879 if (user->adminflags) {
880 /* Set the new modes */
881 if ((user->adminflags & ADMINFLAG_MUTED) && (ztc.confmode & ZT_CONF_TALKER)) {
882 ztc.confmode ^= ZT_CONF_TALKER;
883 if (ioctl(fd, ZT_SETCONF, &ztc)) {
884 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
889 if (!(user->adminflags & ADMINFLAG_MUTED) && !(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
890 ztc.confmode |= ZT_CONF_TALKER;
891 if (ioctl(fd, ZT_SETCONF, &ztc)) {
892 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
897 if (user->adminflags & ADMINFLAG_KICKME) {
898 /* You have been kicked. */
899 if (!ast_streamfile(chan, "conf-kicked", chan->language))
900 ast_waitstream(chan, "");
904 } else if (!(confflags & CONFFLAG_MONITOR) && !(ztc.confmode & ZT_CONF_TALKER)) {
905 ztc.confmode |= ZT_CONF_TALKER;
906 if (ioctl(fd, ZT_SETCONF, &ztc)) {
907 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
914 if (c->fds[0] != origfd) {
916 /* Kill old pseudo */
919 ast_log(LOG_DEBUG, "Ooh, something swapped out under us, starting over\n");
927 if ((f->frametype == AST_FRAME_VOICE) && (f->subclass == AST_FORMAT_SLINEAR)) {
928 if (confflags & CONFFLAG_MONITORTALKER) {
930 if (user->talking == -1)
933 res = ast_dsp_silence(dsp, f, &totalsilence);
934 if (!user->talking && totalsilence < MEETME_DELAYDETECTTALK) {
936 manager_event(EVENT_FLAG_CALL, "MeetmeTalking",
941 chan->name, chan->uniqueid, conf->confno, user->user_no);
943 if (user->talking && totalsilence > MEETME_DELAYDETECTENDTALK) {
945 manager_event(EVENT_FLAG_CALL, "MeetmeStopTalking",
950 chan->name, chan->uniqueid, conf->confno, user->user_no);
954 /* Carefully write */
955 careful_write(fd, f->data, f->datalen);
957 } else if ((f->frametype == AST_FRAME_DTMF) && (confflags & CONFFLAG_EXIT_CONTEXT)) {
959 tmp[0] = f->subclass;
961 if (ast_exists_extension(chan, exitcontext, tmp, 1, chan->cid.cid_num)) {
962 strncpy(chan->context, exitcontext, sizeof(chan->context) - 1);
963 strncpy(chan->exten, tmp, sizeof(chan->exten) - 1);
968 } else if ((f->frametype == AST_FRAME_DTMF) && (f->subclass == '#') && (confflags & CONFFLAG_POUNDEXIT)) {
971 } else if (((f->frametype == AST_FRAME_DTMF) && (f->subclass == '*') && (confflags & CONFFLAG_STARMENU)) || ((f->frametype == AST_FRAME_DTMF) && menu_active)) {
973 oldconfmode = ztc.confmode;
975 if (ioctl(fd, ZT_SETCONF, &ztc)) {
976 ast_log(LOG_WARNING, "Error setting conference\n");
978 ast_mutex_unlock(&conflock);
984 if ((confflags & CONFFLAG_ADMIN)) {
988 /* Record this sound! */
989 if (!ast_streamfile(chan, "conf-adminmenu", chan->language))
990 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
997 case '1': /* Un/Mute */
999 if (ztc.confmode & ZT_CONF_TALKER) {
1000 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1001 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1003 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1004 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1006 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1007 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1011 if (ztc.confmode & ZT_CONF_TALKER) {
1012 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1013 ast_waitstream(chan, "");
1015 if (!ast_streamfile(chan, "conf-muted", chan->language))
1016 ast_waitstream(chan, "");
1019 case '2': /* Un/Lock the Conference */
1023 if (!ast_streamfile(chan, "conf-unlockednow", chan->language))
1024 ast_waitstream(chan, "");
1027 if (!ast_streamfile(chan, "conf-lockednow", chan->language))
1028 ast_waitstream(chan, "");
1031 case '3': /* Eject last user */
1033 usr = conf->lastuser;
1034 if ((usr->chan->name == chan->name)||(usr->userflags & CONFFLAG_ADMIN)) {
1035 if(!ast_streamfile(chan, "conf-errormenu", chan->language))
1036 ast_waitstream(chan, "");
1038 usr->adminflags |= ADMINFLAG_KICKME;
1039 ast_stopstream(chan);
1043 /* Play an error message! */
1044 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1045 ast_waitstream(chan, "");
1053 /* Record this sound! */
1054 if (!ast_streamfile(chan, "conf-usermenu", chan->language))
1055 dtmf = ast_waitstream(chan, AST_DIGIT_ANY);
1062 case '1': /* Un/Mute */
1064 if (ztc.confmode & ZT_CONF_TALKER) {
1065 ztc.confmode = ZT_CONF_CONF | ZT_CONF_LISTENER;
1066 confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER;
1067 } else if (!(user->adminflags & ADMINFLAG_MUTED)) {
1068 ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
1069 confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER;
1071 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1072 ast_log(LOG_WARNING, "Error setting conference - Un/Mute \n");
1076 if (ztc.confmode & ZT_CONF_TALKER) {
1077 if (!ast_streamfile(chan, "conf-unmuted", chan->language))
1078 ast_waitstream(chan, "");
1080 if (!ast_streamfile(chan, "conf-muted", chan->language))
1081 ast_waitstream(chan, "");
1086 /* Play an error message! */
1087 if (!ast_streamfile(chan, "conf-errormenu", chan->language))
1088 ast_waitstream(chan, "");
1094 ast_moh_start(chan, NULL);
1096 ztc.confmode = oldconfmode;
1097 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1098 ast_log(LOG_WARNING, "Error setting conference\n");
1100 ast_mutex_unlock(&conflock);
1104 } else if (option_debug) {
1105 ast_log(LOG_DEBUG, "Got unrecognized frame on channel %s, f->frametype=%d,f->subclass=%d\n",chan->name,f->frametype,f->subclass);
1108 } else if (outfd > -1) {
1109 res = read(outfd, buf, CONF_SIZE);
1111 memset(&fr, 0, sizeof(fr));
1112 fr.frametype = AST_FRAME_VOICE;
1113 fr.subclass = AST_FORMAT_SLINEAR;
1117 fr.offset = AST_FRIENDLY_OFFSET;
1118 if (ast_write(chan, &fr) < 0) {
1119 ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno));
1123 ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno));
1130 /* Take out of conference */
1131 /* Add us to the conference */
1135 if (ioctl(fd, ZT_SETCONF, &ztc)) {
1136 ast_log(LOG_WARNING, "Error setting conference\n");
1140 ast_mutex_lock(&conflock);
1141 if (!(confflags & CONFFLAG_QUIET) && !(confflags & CONFFLAG_MONITOR) && !(confflags & CONFFLAG_ADMIN))
1142 conf_play(chan, conf, LEAVE);
1144 if (!(confflags & CONFFLAG_QUIET) && (confflags & CONFFLAG_INTROUSER)) {
1145 if (ast_fileexists(user->namerecloc, NULL, NULL)) {
1146 if ((conf->chan) && (conf->users > 1)) {
1147 if (!ast_streamfile(conf->chan, user->namerecloc, chan->language))
1148 ast_waitstream(conf->chan, "");
1149 if (!ast_streamfile(conf->chan, "conf-hasleft", chan->language))
1150 ast_waitstream(conf->chan, "");
1152 ast_filedelete(user->namerecloc, NULL);
1155 ast_mutex_unlock(&conflock);
1159 ast_mutex_lock(&conflock);
1160 if (confflags & CONFFLAG_MONITORTALKER && dsp)
1163 if (user->user_no) { /* Only cleanup users who really joined! */
1164 manager_event(EVENT_FLAG_CALL, "MeetmeLeave",
1169 chan->name, chan->uniqueid, conf->confno, user->user_no);
1172 if (confflags & CONFFLAG_MARKEDUSER)
1173 conf->markedusers--;
1176 /* No more users -- close this one out */
1180 prev->next = conf->next;
1189 ast_log(LOG_WARNING, "Conference not found\n");
1190 if (conf->recording == MEETME_RECORD_ACTIVE) {
1191 conf->recording = MEETME_RECORD_TERMINATE;
1192 ast_mutex_unlock(&conflock);
1194 ast_mutex_lock(&conflock);
1195 if (conf->recording == MEETME_RECORD_OFF)
1197 ast_mutex_unlock(&conflock);
1201 ast_hangup(conf->chan);
1206 /* Remove the user struct */
1207 if (user == conf->firstuser) {
1208 if (user->nextuser) {
1209 /* There is another entry */
1210 user->nextuser->prevuser = NULL;
1212 /* We are the only entry */
1213 conf->lastuser = NULL;
1215 /* In either case */
1216 conf->firstuser = user->nextuser;
1217 } else if (user == conf->lastuser){
1219 user->prevuser->nextuser = NULL;
1221 ast_log(LOG_ERROR, "Bad bad bad! We're the last, not the first, but nobody before us??\n");
1222 conf->lastuser = user->prevuser;
1225 user->nextuser->prevuser = user->prevuser;
1227 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->nextuser is NULL but we're not the end!\n");
1229 user->prevuser->nextuser = user->nextuser;
1231 ast_log(LOG_ERROR, "Bad! Bad! Bad! user->prevuser is NULL but we're not the beginning!\n");
1234 /* Return the number of seconds the user was in the conf */
1235 snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user->jointime));
1236 pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs);
1239 ast_mutex_unlock(&conflock);
1243 static struct ast_conference *find_conf(struct ast_channel *chan, char *confno, int make, int dynamic, char *dynamic_pin)
1245 struct ast_config *cfg;
1246 struct ast_variable *var;
1247 struct ast_conference *cnf;
1249 /* Check first in the conference list */
1250 ast_mutex_lock(&conflock);
1253 if (!strcmp(confno, cnf->confno))
1257 ast_mutex_unlock(&conflock);
1261 /* No need to parse meetme.conf */
1262 ast_log(LOG_DEBUG, "Building dynamic conference '%s'\n", confno);
1264 if (dynamic_pin[0] == 'q') {
1265 /* Query the user to enter a PIN */
1266 ast_app_getdata(chan, "conf-getpin", dynamic_pin, AST_MAX_EXTENSION - 1, 0);
1268 cnf = build_conf(confno, dynamic_pin, "", make, dynamic);
1270 cnf = build_conf(confno, "", "", make, dynamic);
1273 /* Check the config */
1274 cfg = ast_config_load("meetme.conf");
1276 ast_log(LOG_WARNING, "No meetme.conf file :(\n");
1279 var = ast_variable_browse(cfg, "rooms");
1281 if (!strcasecmp(var->name, "conf")) {
1282 /* Separate the PIN */
1283 char *pin, *pinadmin, *conf;
1285 if ((pinadmin = ast_strdupa(var->value))) {
1286 conf = strsep(&pinadmin, "|,");
1287 pin = strsep(&pinadmin, "|,");
1288 if (!strcasecmp(conf, confno)) {
1289 /* Bingo it's a valid conference */
1292 cnf = build_conf(confno, pin, pinadmin, make, dynamic);
1294 cnf = build_conf(confno, pin, "", make, dynamic);
1297 cnf = build_conf(confno, "", pinadmin, make, dynamic);
1299 cnf = build_conf(confno, "", "", make, dynamic);
1307 ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno);
1309 ast_config_destroy(cfg);
1311 } else if (dynamic_pin) {
1312 /* Correct for the user selecting 'D' instead of 'd' to have
1313 someone join into a conference that has already been created
1315 if (dynamic_pin[0] == 'q')
1316 dynamic_pin[0] = '\0';
1321 /*--- count_exec: The MeetmeCount application */
1322 static int count_exec(struct ast_channel *chan, void *data)
1324 struct localuser *u;
1326 struct ast_conference *conf;
1328 char *confnum, *localdata;
1331 if (!data || ast_strlen_zero(data)) {
1332 ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n");
1335 localdata = ast_strdupa(data);
1337 confnum = strsep(&localdata,"|");
1338 conf = find_conf(chan, confnum, 0, 0, NULL);
1340 count = conf->users;
1344 if (localdata && !ast_strlen_zero(localdata)){
1345 /* have var so load it and exit */
1346 snprintf(val,sizeof(val), "%i",count);
1347 pbx_builtin_setvar_helper(chan, localdata,val);
1349 if (chan->_state != AST_STATE_UP)
1351 res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */
1353 LOCAL_USER_REMOVE(u);
1357 /*--- conf_exec: The meetme() application */
1358 static int conf_exec(struct ast_channel *chan, void *data)
1361 struct localuser *u;
1362 char confno[AST_MAX_EXTENSION] = "";
1365 struct ast_conference *cnf;
1368 int empty = 0, empty_no_pin = 0;
1369 int always_prompt = 0;
1370 char *notdata, *info, *inflags = NULL, *inpin = NULL, the_pin[AST_MAX_EXTENSION] = "";
1372 if (!data || ast_strlen_zero(data)) {
1379 if (chan->_state != AST_STATE_UP)
1382 info = ast_strdupa((char *)notdata);
1385 char *tmp = strsep(&info, "|");
1386 strncpy(confno, tmp, sizeof(confno) - 1);
1387 if (ast_strlen_zero(confno)) {
1392 inflags = strsep(&info, "|");
1394 inpin = strsep(&info, "|");
1396 strncpy(the_pin, inpin, sizeof(the_pin) - 1);
1399 if (strchr(inflags, 'a'))
1400 confflags |= CONFFLAG_ADMIN;
1401 if (strchr(inflags, 'T'))
1402 confflags |= CONFFLAG_MONITORTALKER;
1403 if (strchr(inflags, 'i'))
1404 confflags |= CONFFLAG_INTROUSER;
1405 if (strchr(inflags, 'm'))
1406 confflags |= CONFFLAG_MONITOR;
1407 if (strchr(inflags, 'p'))
1408 confflags |= CONFFLAG_POUNDEXIT;
1409 if (strchr(inflags, 's'))
1410 confflags |= CONFFLAG_STARMENU;
1411 if (strchr(inflags, 't'))
1412 confflags |= CONFFLAG_TALKER;
1413 if (strchr(inflags, 'q'))
1414 confflags |= CONFFLAG_QUIET;
1415 if (strchr(inflags, 'M'))
1416 confflags |= CONFFLAG_MOH;
1417 if (strchr(inflags, 'x'))
1418 confflags |= CONFFLAG_MARKEDEXIT;
1419 if (strchr(inflags, 'X'))
1420 confflags |= CONFFLAG_EXIT_CONTEXT;
1421 if (strchr(inflags, 'A'))
1422 confflags |= CONFFLAG_MARKEDUSER;
1423 if (strchr(inflags, 'b'))
1424 confflags |= CONFFLAG_AGI;
1425 if (strchr(inflags, 'w'))
1426 confflags |= CONFFLAG_WAITMARKED;
1427 if (strchr(inflags, 'r'))
1428 confflags |= CONFFLAG_RECORDCONF;
1429 if (strchr(inflags, 'd'))
1431 if (strchr(inflags, 'D')) {
1434 strncpy(the_pin, "q", sizeof(the_pin) - 1);
1437 if (strchr(inflags, 'e'))
1439 if (strchr(inflags, 'E')) {
1443 if (strchr(inflags, 'P'))
1452 struct ast_config *cfg;
1453 struct ast_variable *var;
1456 memset(map, 0, sizeof(map));
1458 ast_mutex_lock(&conflock);
1461 if (sscanf(cnf->confno, "%d", &confno_int) == 1) {
1462 /* Disqualify in use conference */
1463 if (confno_int >= 0 && confno_int < 1024)
1468 ast_mutex_unlock(&conflock);
1470 /* We only need to load the config file for static and empty_no_pin (otherwise we don't care) */
1471 if ((empty_no_pin) || (!dynamic)) {
1472 cfg = ast_config_load("meetme.conf");
1474 var = ast_variable_browse(cfg, "rooms");
1476 if (!strcasecmp(var->name, "conf")) {
1477 char *stringp = ast_strdupa(var->value);
1479 char *confno_tmp = strsep(&stringp, "|,");
1481 if (sscanf(confno_tmp, "%d", &confno_int) == 1) {
1482 if ((confno_int >= 0) && (confno_int < 1024)) {
1483 if (stringp && empty_no_pin) {
1489 /* For static: run through the list and see if this conference is empty */
1490 ast_mutex_lock(&conflock);
1493 if (!strcmp(confno_tmp, cnf->confno)) {
1494 /* The conference exists, therefore it's not empty */
1500 ast_mutex_unlock(&conflock);
1502 /* At this point, we have a confno_tmp (static conference) that is empty */
1503 if ((empty_no_pin && ((!stringp) || (stringp && (stringp[0] == '\0')))) || (!empty_no_pin)) {
1504 /* Case 1: empty_no_pin and pin is nonexistant (NULL)
1505 * Case 2: empty_no_pin and pin is blank (but not NULL)
1506 * Case 3: not empty_no_pin
1508 strncpy(confno, confno_tmp, sizeof(confno) - 1);
1510 /* XXX the map is not complete (but we do have a confno) */
1515 ast_log(LOG_ERROR, "Out of memory\n");
1520 ast_config_destroy(cfg);
1523 /* Select first conference number not in use */
1524 if (ast_strlen_zero(confno) && dynamic) {
1525 for (i=0;i<1024;i++) {
1527 snprintf(confno, sizeof(confno), "%d", i);
1534 if (ast_strlen_zero(confno)) {
1535 res = ast_streamfile(chan, "conf-noempty", chan->language);
1537 ast_waitstream(chan, "");
1539 if (sscanf(confno, "%d", &confno_int) == 1) {
1540 res = ast_streamfile(chan, "conf-enteringno", chan->language);
1542 ast_waitstream(chan, "");
1543 res = ast_say_digits(chan, confno_int, "", chan->language);
1546 ast_log(LOG_ERROR, "Could not scan confno '%s'\n", confno);
1550 while (allowretry && (ast_strlen_zero(confno)) && (++retrycnt < 4)) {
1551 /* Prompt user for conference number */
1552 res = ast_app_getdata(chan, "conf-getconfno", confno, sizeof(confno) - 1, 0);
1554 /* Don't try to validate when we catch an error */
1560 if (!ast_strlen_zero(confno)) {
1561 /* Check the validity of the conference */
1562 cnf = find_conf(chan, confno, 1, dynamic, the_pin);
1564 res = ast_streamfile(chan, "conf-invalid", chan->language);
1566 ast_waitstream(chan, "");
1571 if ((!ast_strlen_zero(cnf->pin) && ! (confflags & CONFFLAG_ADMIN)) || (!ast_strlen_zero(cnf->pinadmin) && (confflags & CONFFLAG_ADMIN))) {
1572 char pin[AST_MAX_EXTENSION]="";
1575 /* Allow the pin to be retried up to 3 times */
1576 for (j=0; j<3; j++) {
1577 if (*the_pin && (always_prompt==0)) {
1578 strncpy(pin, the_pin, sizeof(pin) - 1);
1581 /* Prompt user for pin if pin is required */
1582 res = ast_app_getdata(chan, "conf-getpin", pin + strlen(pin), sizeof(pin) - 1 - strlen(pin), 0);
1585 if (!strcasecmp(pin, cnf->pin) || (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))) {
1589 if (!ast_strlen_zero(cnf->pinadmin) && !strcasecmp(pin, cnf->pinadmin))
1590 confflags |= CONFFLAG_ADMIN;
1591 /* Run the conference */
1592 res = conf_run(chan, cnf, confflags);
1596 res = ast_streamfile(chan, "conf-invalidpin", chan->language);
1598 ast_waitstream(chan, AST_DIGIT_ANY);
1613 /* Don't retry pin with a static pin */
1614 if (*the_pin && (always_prompt==0)) {
1619 /* No pin required */
1622 /* Run the conference */
1623 res = conf_run(chan, cnf, confflags);
1627 } while (allowretry);
1628 /* Do the conference */
1629 LOCAL_USER_REMOVE(u);
1633 static struct ast_conf_user* find_user(struct ast_conference *conf, char *callerident) {
1634 struct ast_conf_user *user = NULL;
1635 char usrno[1024] = "";
1636 if (conf && callerident) {
1637 user = conf->firstuser;
1639 snprintf(usrno, sizeof(usrno), "%i", user->user_no);
1640 if (strcmp(usrno, callerident) == 0)
1642 user = user->nextuser;
1648 /*--- admin_exec: The MeetMeadmin application */
1649 /* MeetMeAdmin(confno, command, caller) */
1650 static int admin_exec(struct ast_channel *chan, void *data) {
1651 char *params, *command = NULL, *caller = NULL, *conf = NULL;
1652 struct ast_conference *cnf;
1653 struct ast_conf_user *user = NULL;
1655 ast_mutex_lock(&conflock);
1656 /* The param has the conference number the user and the command to execute */
1657 if (data && !ast_strlen_zero(data)) {
1658 params = ast_strdupa((char *) data);
1659 conf = strsep(¶ms, "|");
1660 command = strsep(¶ms, "|");
1661 caller = strsep(¶ms, "|");
1664 ast_log(LOG_WARNING, "MeetmeAdmin requires a command!\n");
1665 ast_mutex_unlock(&conflock);
1670 if (strcmp(cnf->confno, conf) == 0)
1676 user = find_user(cnf, caller);
1679 switch((int) (*command)) {
1680 case 76: /* L: Lock */
1683 case 108: /* l: Unlock */
1686 case 75: /* K: kick all users*/
1687 user = cnf->firstuser;
1689 user->adminflags |= ADMINFLAG_KICKME;
1690 if (user->nextuser) {
1691 user = user->nextuser;
1697 case 101: /* e: Eject last user*/
1698 user = cnf->lastuser;
1699 if (!(user->userflags & CONFFLAG_ADMIN)) {
1700 user->adminflags |= ADMINFLAG_KICKME;
1703 ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n");
1705 case 77: /* M: Mute */
1707 user->adminflags |= ADMINFLAG_MUTED;
1709 ast_log(LOG_NOTICE, "Specified User not found!\n");
1712 case 78: /* N: Mute all users */
1713 user = cnf->firstuser;
1715 if (user && !(user->userflags & CONFFLAG_ADMIN))
1716 user->adminflags |= ADMINFLAG_MUTED;
1717 if (user->nextuser) {
1718 user = user->nextuser;
1724 case 109: /* m: Unmute */
1725 if (user && (user->adminflags & ADMINFLAG_MUTED)) {
1726 user->adminflags ^= ADMINFLAG_MUTED;
1728 ast_log(LOG_NOTICE, "Specified User not found or he muted himself!");
1731 case 110: /* n: Unmute all users */
1732 user = cnf->firstuser;
1734 if (user && (user-> adminflags & ADMINFLAG_MUTED)) {
1735 user->adminflags ^= ADMINFLAG_MUTED;
1737 if (user->nextuser) {
1738 user = user->nextuser;
1744 case 107: /* k: Kick user */
1746 user->adminflags |= ADMINFLAG_KICKME;
1748 ast_log(LOG_NOTICE, "Specified User not found!");
1753 ast_log(LOG_NOTICE, "Conference Number not found\n");
1756 ast_mutex_unlock(&conflock);
1760 static void *recordthread(void *args)
1762 struct ast_conference *cnf;
1763 struct ast_frame *f=NULL;
1765 struct ast_filestream *s;
1768 cnf = (struct ast_conference *)args;
1769 if( !cnf || !cnf->chan ) {
1772 ast_stopstream(cnf->chan);
1773 flags = O_CREAT|O_TRUNC|O_WRONLY;
1774 s = ast_writefile(cnf->recordingfilename, cnf->recordingformat, NULL, flags, 0, 0644);
1777 cnf->recording = MEETME_RECORD_ACTIVE;
1778 while (ast_waitfor(cnf->chan, -1) > -1) {
1779 f = ast_read(cnf->chan);
1784 if (f->frametype == AST_FRAME_VOICE) {
1785 res = ast_writestream(s, f);
1790 if (cnf->recording == MEETME_RECORD_TERMINATE) {
1791 ast_mutex_lock(&conflock);
1792 ast_mutex_unlock(&conflock);
1796 cnf->recording = MEETME_RECORD_OFF;
1802 int unload_module(void)
1804 STANDARD_HANGUP_LOCALUSERS;
1805 ast_cli_unregister(&cli_show_confs);
1806 ast_cli_unregister(&cli_conf);
1807 ast_unregister_application(app3);
1808 ast_unregister_application(app2);
1809 return ast_unregister_application(app);
1812 int load_module(void)
1814 ast_cli_register(&cli_show_confs);
1815 ast_cli_register(&cli_conf);
1816 ast_register_application(app3, admin_exec, synopsis3, descrip3);
1817 ast_register_application(app2, count_exec, synopsis2, descrip2);
1818 return ast_register_application(app, conf_exec, synopsis, descrip);
1822 char *description(void)
1830 STANDARD_USECOUNT(res);
1836 return ASTERISK_GPL_KEY;