diff options
author | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2002-05-13 22:29:39 +0000 |
---|---|---|
committer | markster <markster@f38db490-d61c-443f-a65b-d21fe96a405b> | 2002-05-13 22:29:39 +0000 |
commit | 1642b09aba30d6f0b95a11bf079f99db94fdac02 (patch) | |
tree | 64c5de0a1afb1872ee474057eb3f244015f2801c /apps/app_meetme.c | |
parent | 1c5b980de51d4caaa1d463fd0f702305b87aec4e (diff) |
Version 0.1.12 from FTP
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@446 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'apps/app_meetme.c')
-rwxr-xr-x | apps/app_meetme.c | 501 |
1 files changed, 501 insertions, 0 deletions
diff --git a/apps/app_meetme.c b/apps/app_meetme.c new file mode 100755 index 000000000..7a0ad67cf --- /dev/null +++ b/apps/app_meetme.c @@ -0,0 +1,501 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Meet me conference bridge + * + * Copyright (C) 1999, Mark Spencer + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <asterisk/lock.h> +#include <asterisk/file.h> +#include <asterisk/logger.h> +#include <asterisk/channel.h> +#include <asterisk/pbx.h> +#include <asterisk/module.h> +#include <asterisk/config.h> +#include <asterisk/app.h> +#include <asterisk/options.h> +#include <asterisk/cli.h> +#include <asterisk/say.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <sys/ioctl.h> + +#include <pthread.h> +#include <linux/zaptel.h> + +static char *tdesc = "Simple MeetMe conference bridge"; + +static char *app = "MeetMe"; +static char *app2 = "MeetMeCount"; + +static char *synopsis = "Simple MeetMe conference bridge"; +static char *synopsis2 = "MeetMe participant count"; + +static char *descrip = +" MeetMe(confno): Enters the user into a specified MeetMe conference.\n" +"If the conference number is omitted, the user will be prompted to enter\n" +"one. This application always returns -1. A ZAPTEL INTERFACE MUST BE\n" +"INSTALLED FOR CONFERENCING FUNCTIONALITY.\n"; + +static char *descrip2 = +" MeetMe2(confno): Plays back the number of users in the specified MeetMe\n" +"conference. Returns 0 on success or -1 on a hangup. A ZAPTEL INTERFACE\n" +"MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static struct conf { + char confno[80]; /* Conference */ + int fd; /* Announcements fd */ + int zapconf; /* Zaptel Conf # */ + int users; /* Number of active users */ + time_t start; /* Start time (s) */ + struct conf *next; +} *confs; + +static pthread_mutex_t conflock = AST_MUTEX_INITIALIZER; + +#include "enter.h" +#include "leave.h" + +#define ENTER 0 +#define LEAVE 1 + +#define CONF_SIZE 160 + +static int careful_write(int fd, unsigned char *data, int len) +{ + int res; + while(len) { + res = write(fd, data, len); + if (res < 1) { + if (errno != EAGAIN) { + ast_log(LOG_WARNING, "Failed to write audio data to conference: %s\n", strerror(errno)); + return -1; + } else + return 0; + } + len -= res; + data += res; + } + return 0; +} + +static void conf_play(struct conf *conf, int sound) +{ + unsigned char *data; + int len; + ast_pthread_mutex_lock(&conflock); + switch(sound) { + case ENTER: + data = enter; + len = sizeof(enter); + break; + case LEAVE: + data = leave; + len = sizeof(leave); + break; + default: + data = NULL; + len = 0; + } + if (data) + careful_write(conf->fd, data, len); + pthread_mutex_unlock(&conflock); +} + +static struct conf *build_conf(char *confno, int make) +{ + struct conf *cnf; + struct zt_confinfo ztc; + ast_pthread_mutex_lock(&conflock); + cnf = confs; + while(cnf) { + if (!strcmp(confno, cnf->confno)) + break; + cnf = cnf->next; + } + if (!cnf && make) { + cnf = malloc(sizeof(struct conf)); + if (cnf) { + /* Make a new one */ + memset(cnf, 0, sizeof(struct conf)); + strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1); + cnf->fd = open("/dev/zap/pseudo", O_RDWR); + if (cnf->fd < 0) { + ast_log(LOG_WARNING, "Unable to open pseudo channel\n"); + free(cnf); + cnf = NULL; + goto cnfout; + } + memset(&ztc, 0, sizeof(ztc)); + /* Setup a new zap conference */ + ztc.chan = 0; + ztc.confno = -1; + ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; + if (ioctl(cnf->fd, ZT_SETCONF, &ztc)) { + ast_log(LOG_WARNING, "Error setting conference\n"); + close(cnf->fd); + free(cnf); + cnf = NULL; + goto cnfout; + } + cnf->start = time(NULL); + cnf->zapconf = ztc.confno; + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Crated ZapTel conference %d for conference '%s'\n", cnf->zapconf, cnf->confno); + cnf->next = confs; + confs = cnf; + } else + ast_log(LOG_WARNING, "Out of memory\n"); + } +cnfout: + if (cnf && make) + cnf->users++; + ast_pthread_mutex_unlock(&conflock); + return cnf; +} + +static int confs_show(int fd, int argc, char **argv) +{ + struct conf *conf; + int hr, min, sec; + time_t now; + + now = time(NULL); + if (argc != 2) + return RESULT_SHOWUSAGE; + conf = confs; + if (!conf) { + ast_cli(fd, "No active conferences.\n"); + return RESULT_SUCCESS; + } + ast_cli(fd, "Conf Num Parties Activity\n"); + while(conf) { + hr = (now - conf->start) / 3600; + min = ((now - conf->start) % 3600) / 60; + sec = (now - conf->start) % 60; + + ast_cli(fd, "%-12.12s %4.4d %02d:%02d:%02d\n", + conf->confno, conf->users, hr, min, sec); + conf = conf->next; + } + return RESULT_SUCCESS; +} + +static char show_confs_usage[] = +"Usage: show conferences\n" +" Provides summary information on conferences with active\n" +" participation.\n"; + +static struct ast_cli_entry cli_show_confs = { + { "show", "conferences", NULL }, confs_show, + "Show status of conferences", show_confs_usage, NULL }; + +static void conf_run(struct ast_channel *chan, struct conf *conf) +{ + struct conf *prev=NULL, *cur; + int fd; + struct zt_confinfo ztc; + struct ast_frame *f; + struct ast_channel *c; + struct ast_frame fr; + int outfd; + int ms; + int nfds; + int res; + int flags; + int retryzap=0; + + ZT_BUFFERINFO bi; + char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; + char *buf = __buf + AST_FRIENDLY_OFFSET; + + /* Set it into U-law mode (write) */ + if (ast_set_write_format(chan, AST_FORMAT_ULAW) < 0) { + ast_log(LOG_WARNING, "Unable to set '%s' to write ulaw mode\n", chan->name); + goto outrun; + } + + /* Set it into U-law mode (read) */ + if (ast_set_read_format(chan, AST_FORMAT_ULAW) < 0) { + ast_log(LOG_WARNING, "Unable to set '%s' to read ulaw mode\n", chan->name); + goto outrun; + } +zapretry: + + if (retryzap || strcasecmp(chan->type, "Zap")) { + fd = open("/dev/zap/pseudo", O_RDWR); + if (fd < 0) { + ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); + goto outrun; + } + /* Make non-blocking */ + flags = fcntl(fd, F_GETFL); + if (flags < 0) { + ast_log(LOG_WARNING, "Unable to get flags: %s\n", strerror(errno)); + close(fd); + goto outrun; + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { + ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); + close(fd); + goto outrun; + } + /* Setup buffering information */ + memset(&bi, 0, sizeof(bi)); + bi.bufsize = CONF_SIZE; + bi.txbufpolicy = ZT_POLICY_IMMEDIATE; + bi.rxbufpolicy = ZT_POLICY_IMMEDIATE; + bi.numbufs = 4; + if (ioctl(fd, ZT_SET_BUFINFO, &bi)) { + ast_log(LOG_WARNING, "Unable to set buffering information: %s\n", strerror(errno)); + close(fd); + goto outrun; + } + nfds = 1; + } else { + /* XXX Make sure we're not running on a pseudo channel XXX */ + fd = chan->fds[0]; + nfds = 0; + } + memset(&ztc, 0, sizeof(ztc)); + /* Check to see if we're in a conference... */ + ztc.chan = 0; + if (ioctl(fd, ZT_GETCONF, &ztc)) { + ast_log(LOG_WARNING, "Error getting conference\n"); + close(fd); + goto outrun; + } + if (ztc.confmode) { + /* Whoa, already in a conference... Retry... */ + if (!retryzap) { + ast_log(LOG_DEBUG, "Zap channel is in a conference already, retrying with pseudo\n"); + retryzap = 1; + goto zapretry; + } + } + memset(&ztc, 0, sizeof(ztc)); + /* Add us to the conference */ + ztc.chan = 0; + ztc.confno = conf->zapconf; + ztc.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; + if (ioctl(fd, ZT_SETCONF, &ztc)) { + ast_log(LOG_WARNING, "Error setting conference\n"); + close(fd); + goto outrun; + } + ast_log(LOG_DEBUG, "Placed channel %s in ZAP conf %d\n", chan->name, conf->zapconf); + /* Run the conference enter tone... */ + conf_play(conf, ENTER); + + for(;;) { + outfd = -1; + ms = -1; + c = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms); + if (c) { + f = ast_read(c); + if (!f) + break; + if (fd != chan->fds[0]) { + if (f->frametype == AST_FRAME_VOICE) { + if (f->subclass == AST_FORMAT_ULAW) { + /* Carefully write */ + careful_write(fd, f->data, f->datalen); + } else + ast_log(LOG_WARNING, "Huh? Got a non-ulaw (%d) frame in the conference\n", f->subclass); + } + } + ast_frfree(f); + } else if (outfd > -1) { + res = read(outfd, buf, CONF_SIZE); + if (res > 0) { + memset(&fr, 0, sizeof(fr)); + fr.frametype = AST_FRAME_VOICE; + fr.subclass = AST_FORMAT_ULAW; + fr.datalen = res; + fr.timelen = res / 8; + fr.data = buf; + fr.offset = AST_FRIENDLY_OFFSET; + if (ast_write(chan, &fr) < 0) { + ast_log(LOG_WARNING, "Unable to write frame to channel: %s\n", strerror(errno)); + /* break; */ + } + } else + ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno)); + } + } + if (fd != chan->fds[0]) + close(fd); + + conf_play(conf, LEAVE); + +outrun: + + ast_pthread_mutex_lock(&conflock); + /* Clean up */ + conf->users--; + if (!conf->users) { + /* No more users -- close this one out */ + cur = confs; + while(cur) { + if (cur == conf) { + if (prev) + prev->next = conf->next; + else + confs = conf->next; + break; + } + prev = cur; + cur = cur->next; + } + if (!cur) + ast_log(LOG_WARNING, "Conference not found\n"); + close(conf->fd); + free(conf); + } + pthread_mutex_unlock(&conflock); +} + +static struct conf *find_conf(char *confno, int make) +{ + struct ast_config *cfg; + struct ast_variable *var; + struct conf *cnf = NULL; + cfg = ast_load("meetme.conf"); + if (!cfg) { + ast_log(LOG_WARNING, "No meetme.conf file :(\n"); + return NULL; + } + var = ast_variable_browse(cfg, "rooms"); + while(var) { + if (!strcasecmp(var->name, "conf") && + !strcasecmp(var->value, confno)) { + /* Bingo it's a valid conference */ + cnf = build_conf(confno, make); + break; + } + var = var->next; + } + if (!var) { + ast_log(LOG_DEBUG, "%s isn't a valid conference\n", confno); + } + ast_destroy(cfg); + return cnf; +} + +static int count_exec(struct ast_channel *chan, void *data) +{ + struct localuser *u; + int res = 0; + struct conf *conf; + int cnt; + if (!data || !strlen(data)) { + ast_log(LOG_WARNING, "MeetMeCount requires an argument (conference number)\n"); + return -1; + } + LOCAL_USER_ADD(u); + conf = find_conf(data, 0); + if (conf) + cnt = conf->users; + else + cnt = 0; + if (chan->state != AST_STATE_UP) + ast_answer(chan); + res = ast_say_number(chan, cnt, "", chan->language); + LOCAL_USER_REMOVE(u); + return res; +} + +static int conf_exec(struct ast_channel *chan, void *data) +{ + int res=-1; + struct localuser *u; + char confno[80] = ""; + int allowretry = 0; + int retrycnt = 0; + struct conf *cnf; + + if (!data || !strlen(data)) { + allowretry = 1; + data = ""; + } + LOCAL_USER_ADD(u); + if (chan->state != AST_STATE_UP) + ast_answer(chan); +retry: + /* Parse out the stuff */ + strncpy(confno, data, sizeof(confno) - 1); + + while(!strlen(confno) && (++retrycnt < 4)) { + /* Prompt user for conference number */ + res = ast_app_getdata(chan, "conf-getconfno",confno, sizeof(confno) - 1, 0); + if (res <0) goto out; + } + if (strlen(confno)) { + /* Check the validity of the conference */ + cnf = find_conf(confno, 1); + if (!cnf) { + res = ast_streamfile(chan, "conf-invalid", chan->language); + if (res < 0) + goto out; + if (!res) + res = ast_waitstream(chan, ""); + res = -1; + if (allowretry) { + strcpy(confno, ""); + goto retry; + } + } else { + /* Run the conference */ + conf_run(chan, cnf); + res = -1; + } + } +out: + /* Do the conference */ + LOCAL_USER_REMOVE(u); + return res; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + ast_cli_unregister(&cli_show_confs); + ast_unregister_application(app2); + return ast_unregister_application(app); +} + +int load_module(void) +{ + ast_cli_register(&cli_show_confs); + ast_register_application(app2, count_exec, synopsis2, descrip2); + return ast_register_application(app, conf_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} |