/* * app_confcall.c * Copyright (c) 2004-2007 Anthony Minessale II * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision: 1.115 $") #ifdef __linux__ #include #else #include #endif /* __linux__ */ static char *tdesc = "Conference Call Application"; static char *app = "ConfCall"; static char *app2 = "ConfCallCount"; static char *app3 = "ConfCallAdmin"; static char *synopsis = "ConfCall conference bridge"; static char *synopsis2 = "ConfCall participant count"; static char *synopsis3 = "ConfCall conference Administration"; static char *global_conf_file = "confcall.conf"; static struct ast_app *global_monitor_app = NULL; static char *global_record_format = "WAV"; static char *global_record_filename = "confcall"; static char *global_good_confirm = "confcall/beep"; static char *global_bad_confirm = "confcall/beeperr"; static char *global_admin_menu = "confcall/admin-menu"; static char *global_user_menu = "confcall/user-menu"; static char *global_admin = "confcall/admin"; static char *global_user = "confcall/user"; static char *global_locked = "confcall/locked"; static char *global_unlocked = "confcall/unlocked"; static char *global_marked = "confcall/marked"; static char *global_unmarked = "confcall/unmarked"; static char *global_muted = "confcall/muted"; static char *global_unmuted = "confcall/unmuted"; static char *global_hasjoined = "confcall/hasjoined"; static char *global_hasleft = "confcall/hasleft"; static char *global_menerror = "confcall/menerror"; static char *global_admin_pin = "confcall/admin-pin"; static char *global_invalid_admin_pin = "confcall/invalid-admin-pin"; static char *global_kicked = "confcall/youhavebeenkicked"; static char *global_alone_sound = "confcall/yactopitc"; static char *global_locked_warning = "confcall/confislocked"; static char *global_wait_to_join = "confcall/waittojoin"; static char *global_unknown = "confcall/unknown-caller"; static char *descrip = " ConfCall([confno][, [options]]): Enters the user into a specified ConfCall conference.\n" "If the conference number is omitted, the user will be prompted to enter\n" "one. \n" "ConfCall returns 0\n" "Please note: A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING TO WORK!\n\n" "The option string may contain zero or more of the following characters:\n" " 'm' -- enter the conference muted if you have 's' set you can turn it off later.\n" " 'i' -- announce user join/leave\n" " 'X' -- allow user to exit the conference by entering a valid single\n" " digit extension ${CONFCALL_EXIT_CONTEXT} or the current context\n" " if that variable is not defined.\n" " 'q' -- quiet mode (don't play enter/leave sounds)\n" " 'r' -- Record The Conference.\n" " 'M' -- enable music on hold when the conference has a single caller\n" " 'x' -- close the conference when last marked user exits\n" " 'w' -- wait until the marked user enters the conference\n" " 's' -- Present menu (user or admin) when '*' is received ('send' to menu)\n" " 'a' -- set admin mode\n" " 'A' -- set marked mode\n" " 'P' -- No Pin\n" " 'd' -- Dynamic\n" " '1' -- Close when 1 left"; static char *descrip2 = " ConfCallCount(confno[|var]): Plays back the number of users in the specifiedi\n" "ConfCall conference. If var is specified, playback will be skipped and the value\n" "will be returned in the variable. Returns 0 on success or -1 on a hangup.\n" "A ZAPTEL INTERFACE MUST BE INSTALLED FOR CONFERENCING FUNCTIONALITY.\n"; static char *descrip3 = " ConfCallAdmin(confno, command[, user]): Run admin command for conference\n" " 'K' -- Kick all users out of conference\n" " 'k' -- Kick one user out of conference\n" " 'e' -- Eject last user that joined\n" " 'L' -- Lock conference\n" " 'l' -- Unlock conference\n" " 'M' -- Mute conference\n" " 'm' -- Unmute conference\n" " 'N' -- Mute entire conference (except admin)\n" " 'n' -- Unmute entire conference (except admin)\n" ""; STANDARD_LOCAL_USER; LOCAL_USER_DECL; #define CONFCALL_FLAG_LOCKED (1 << 1) /* Is the conference locked? */ #define CONFCALL_FLAG_RECORDING (1 << 2) /* Is the conference being recorded? */ #define CONFCALL_FLAG_THREAD_FREECONF (1 << 3) /* does the recording thread need to free the data*/ #define CONFCALL_FLAG_PRUNE (1 << 4) /* am i tagged to be destroyed ?*/ #define CONFCALL_FLAG_ACTIVE (1 << 5) /* am i active */ #define CONFCALL_FLAG_REALTIME (1 << 6) /* am i realtime */ #define CONFCALL_FLAG_KILLSOUND (1 << 7) /* cancel current sounds */ #define CONFCALL_FLAG_ENDCONF (1 << 10) /* 1 exit */ #define CONFCALL_FLAG_ONE (1 << 11) /* 1 exit */ #define CONFCALL_FLAG_DYNAMIC (1 << 12) /* 1 exit */ #define CONFCALL_USER_FLAG_AUTOSILENCE (1 << 0) #define ast_fit_in_short(in) (in < -32768 ? -32768 : in > 32767 ? 32767 : in) #define CONFCALL_STRLEN 256 #define CONFCALL_ARGS 23 /* the number of char[] args between confno and flags to copy as defaults */ static struct ast_conference { char confno[CONFCALL_STRLEN]; /* Conference */ char options[CONFCALL_STRLEN]; char pin[CONFCALL_STRLEN]; char admin_pin[CONFCALL_STRLEN]; char cid_num[CONFCALL_STRLEN]; char cid_name[CONFCALL_STRLEN]; char enter_sound[CONFCALL_STRLEN]; char exit_sound[CONFCALL_STRLEN]; char alone_sound[CONFCALL_STRLEN]; char record_format[CONFCALL_STRLEN]; char record_filename[CONFCALL_STRLEN]; char good_confirm[CONFCALL_STRLEN]; char bad_confirm[CONFCALL_STRLEN]; char sound_dir[CONFCALL_STRLEN]; char admin_menu[CONFCALL_STRLEN]; char user_menu[CONFCALL_STRLEN]; char outbound_context[CONFCALL_STRLEN]; char kicked_sound[CONFCALL_STRLEN]; char silence_sample[CONFCALL_STRLEN]; char silence_factor[CONFCALL_STRLEN]; char silence_hangover[CONFCALL_STRLEN]; char silence_params[CONFCALL_STRLEN]; char silence_ceiling[CONFCALL_STRLEN]; char silence_itterations[CONFCALL_STRLEN]; unsigned int flags; int fd; int zapconf; int users; int markedusers; int verbose; int sndcnt; int silence_itt; time_t start; ast_mutex_t lock; ast_mutex_t soundlock; struct ast_channel *chan; struct ast_channel *record_chan; struct ast_conf_user *firstuser; struct ast_conf_user *lastuser; struct ast_conference *next; } *global_conference_list; struct confcall_outbound { struct ast_conference *conf; char exten[CONFCALL_STRLEN]; char cid_num[CONFCALL_STRLEN]; char cid_name[CONFCALL_STRLEN]; }; struct ast_conf_user { int user_no; /* User Number */ struct ast_conf_user *prevuser; /* Pointer to the previous user */ struct ast_conf_user *nextuser; /* Pointer to the next user */ int confflags; /* Flags as set in the conference */ unsigned int adminflags; /* Flags set by the Admin */ unsigned int flags; int zapflags; /* zap i/o related flags */ struct ast_channel *chan; /* Connected channel */ struct ast_conference *conf; char usrvalue[50]; /* Custom User Value */ char namerecloc[CONFCALL_STRLEN]; /* Name Recorded file Location */ char dtmf[CONFCALL_STRLEN]; /* dtmf buffer */ time_t jointime; /* Time the user joined the conference */ int vol; int silence_factor; int working_silence_factor; int max_silence_factor; int silence_ceiling; int silence; int lastmap; }; #define ADMINFLAG_MUTE_ME (1 << 1) #define ADMINFLAG_UNMUTE_ME (1 << 2) #define ADMINFLAG_KICK_ME (1 << 3) #define ADMINFLAG_ADMIN_ME (1 << 4) #define ADMINFLAG_UNADMIN_ME (1 << 5) #define ADMINFLAG_MARK_ME (1 << 6) #define ADMINFLAG_UNMARK_ME (1 << 7) #define ADMINFLAG_DTMF (1 << 8) AST_MUTEX_DEFINE_STATIC(conflock); static int confcall_adminflag_apply_all(int fd, struct ast_conference *conf, unsigned int newflags, unsigned int exception_flags); static int confcall_adminflag_apply_user(int fd, struct ast_conf_user *uptr, unsigned int newflags, unsigned int exception_flags); static void confcall_dup_vars(struct ast_channel *chan, struct ast_channel *new_chan); static void confcall_careful_stream(struct ast_conference *conf, char *filename); static int confcall_admin_exec(struct ast_channel *chan, void *data); static int confcall_exec(struct ast_channel *chan, void *data); static int confcall_join(struct ast_channel *chan, struct ast_conference *conf, int confflags); static int careful_write(int fd, unsigned char *data, int len); static int confcall_deactivate_conference(struct ast_conference *conf); static int confcall_activate_conference(struct ast_conference *conf); static int confcall_parse_options(struct ast_conference *conf, struct ast_variable *var); static struct ast_conference *confcall_new_conf(char *confno); static int confcall_cli_exec(int fd, int argc, char **argv); static char *confcall_complete_helper(char *line, char *word, int pos, int state); static int confnonzero(void *ptr); static void confcall_join_conference(struct ast_channel *chan, struct ast_conference *conf, unsigned int confflags); static void *confcall_playsound_thread(void *ptr); static void confcall_playsound(struct ast_conference *conf, ...); static void *confcall_monitor_conference_thread(void *ptr); static void confcall_monitor_conference(struct ast_conference *conf); static int confcall_activate_zap_conference(int fd, int confno, int flags); static int confcall_deactivate_zap_conference(int fd); static int confcall_set_buffering(int fd); static int confcall_open_pseudo_fd(void); static int confcall_copy_audio(struct ast_channel *chan, int fd, int format); static int confcall_stream_file(ast_mutex_t *lock, struct ast_channel *chan, const char *filename); static int confcall_stream_and_wait(struct ast_channel *chan, const char *filename, int to); static int confcall_handle_dtmf(struct ast_conf_user *user, int dtmf, int *fd); static int confcall_bridge(struct ast_conf_user *user); static void *confcall_join_conference_thread(void *ptr); static void confcall_join_conference(struct ast_channel *chan, struct ast_conference *conf, unsigned int confflags); static struct ast_conference *confcall_find_realtime_conf(char *confno); static struct ast_conference *confcall_find_conf(char *confno); static int count_exec(struct ast_channel *chan, void *data); static int confcall_parse_flags(char *inflags); static void confcall_install_user(struct ast_conference *conf, struct ast_channel *chan, int confflags, struct ast_conf_user *user); static void confcall_uninstall_user(struct ast_conf_user *user); static int confcall_join(struct ast_channel *chan, struct ast_conference *conf, int confflags); static int confcall_exec(struct ast_channel *chan, void *data); static struct ast_conf_user *confcall_find_user(struct ast_conference *conf, char *callerident); static struct ast_conf_user *confcall_find_user_int(struct ast_conference *conf, int callerident); static int confcall_admin_exec(struct ast_channel *chan, void *data); static int confcall_load_config(int reload); static void launch_outbound_thread(struct confcall_outbound *ob); static void *confcall_outbound_thread(void *ptr); static int confcall_soundcount(struct ast_conference *conf, int modifier); static int confcall_send_digits(struct ast_conf_user *user, char *dtmf); #define CONF_SIZE 320 #define CONFFLAG_ADMIN (1 << 1) /* If set the user has admin access on the conference */ #define CONFFLAG_MONITOR (1 << 2) /* If set the user can only receive audio from the conference */ #define CONFFLAG_POUNDEXIT (1 << 3) /* If set asterisk will exit conference when '#' is pressed */ #define CONFFLAG_STARMENU (1 << 4) /* If set asterisk will provide a menu to the user what '*' is pressed */ #define CONFFLAG_TALKER (1 << 5) /* If set the use can only send audio to the conference */ #define CONFFLAG_QUIET (1 << 6) /* If set there will be no enter or leave sounds */ #define CONFFLAG_VIDEO (1 << 7) /* Set to enable video mode */ #define CONFFLAG_AGI (1 << 8) /* Set to run AGI Script in Background */ #define CONFFLAG_MOH (1 << 9) /* Set to have music on hold when user is alone in conference */ #define CONFFLAG_MOH_MUTED (1 << 10) /* Set to have music on hold when muted */ #define CONFFLAG_MARKEDEXIT (1 << 11) /* If set the ConfCall will return if all marked with this flag left */ #define CONFFLAG_WAITMARKED (1 << 12) /* If set, the ConfCall will wait until a marked user enters */ #define CONFFLAG_EXIT_CONTEXT (1 << 13) /* If set, the ConfCall will exit to the specified context */ #define CONFFLAG_MARKED (1 << 14) /* If set, the user will be marked */ #define CONFFLAG_INTROUSER (1 << 15) /* If set, user will be ask record name on entry of conference */ #define CONFFLAG_RECORD (1 << 16) /* Auto Record */ #define CONFFLAG_ALONE (1 << 17) /* am i alone */ #define CONFFLAG_MUTED (1 << 18) #define CONFFLAG_NOPIN (1 << 19) #define CONFFLAG_ONE (1 << 20) /* 1 left */ #define CONFFLAG_DYNAMIC (1 << 21) /* Dynamic */ static int confcall_send_digits(struct ast_conf_user *user, char *dtmf) { int moh = 0, res = 0; struct ast_channel *chan; chan = user->chan; if (ast_test_flag(chan, AST_FLAG_MOH)) { ast_moh_stop(chan); moh = 1; } res = ast_dtmf_stream(chan, NULL, dtmf, 100); if (res >= 0 && moh) { ast_moh_start(chan, NULL); } return res; } static int confcall_soundcount(struct ast_conference *conf, int modifier) { int ret = 0; ast_mutex_lock(&conf->lock); if (modifier) { conf->sndcnt += modifier; } ret = conf->sndcnt; ast_mutex_unlock(&conf->lock); return ret; } static void confcall_dup_vars(struct ast_channel *chan, struct ast_channel *new_chan) { struct ast_var_t *new_variable, *variable, *fresh_variable; struct varshead *headp, *new_headp; int ok = 0; headp = &chan->varshead; AST_LIST_TRAVERSE(headp, variable, entries) { new_headp=&new_chan->varshead; ok = 1; AST_LIST_TRAVERSE (new_headp, new_variable, entries) { if (strcasecmp(ast_var_name(new_variable), ast_var_name(variable)) == 0) { /* there is already such a variable */ ok = 0; break; } } if (ok) { fresh_variable = ast_var_assign(ast_var_name(variable), ast_var_value(variable)); AST_LIST_INSERT_HEAD(new_headp, fresh_variable, entries); } } } static int careful_write(int fd, unsigned char *data, int len) { int res; int x; while(len) { x = ZT_IOMUX_WRITE | ZT_IOMUX_SIGEVENT; res = ioctl(fd, ZT_IOMUX, &x); if (res >= 0) 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 confcall_prune(ast_mutex_t *lock) { struct ast_conference *cptr = NULL, *clast = NULL, *ctmp = NULL; if (lock) { ast_mutex_lock(lock); } for(cptr = global_conference_list; cptr ; cptr = cptr->next) { if (ast_test_flag(cptr, CONFCALL_FLAG_PRUNE) && !ast_test_flag(cptr, CONFCALL_FLAG_ACTIVE)) { ctmp = cptr; if (clast) { clast->next = ctmp->next; } else { global_conference_list = ctmp->next; } ast_mutex_lock(&ctmp->lock); ast_mutex_unlock(&ctmp->lock); ast_mutex_lock(&ctmp->soundlock); ast_mutex_unlock(&ctmp->soundlock); ast_mutex_destroy(&ctmp->lock); ast_mutex_destroy(&ctmp->soundlock); free(ctmp); ctmp = NULL; } clast = cptr; } if (lock) { ast_mutex_unlock(lock); } } static int confcall_deactivate_conference(struct ast_conference *conf) { int res = 0; int soundcount = 0; int tries = 0; struct zt_confinfo ztc; if (!conf) return -1; if (!ast_test_flag(conf, CONFCALL_FLAG_ACTIVE)) { ast_log(LOG_ERROR, "Conference %s already deactivated\n", conf->confno); return 0; } ast_clear_flag(conf, CONFCALL_FLAG_RECORDING); ast_softhangup(conf->chan, AST_SOFTHANGUP_EXPLICIT); ast_clear_flag(conf, CONFCALL_FLAG_ACTIVE); while((soundcount = confcall_soundcount(conf, 0)) > 0) { ast_log(LOG_WARNING, "DOH! Threads still open playing sounds! [%d]\n", tries); usleep(100); tries++; if (tries == 2000) { break; } } ast_mutex_lock(&conf->lock); memset(&ztc, 0, sizeof(ztc)); if (ioctl(conf->fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); res = -1; } if (conf->chan) { ast_mutex_lock(&conf->chan->lock); ast_mutex_unlock(&conf->chan->lock); ast_mutex_lock(&conf->soundlock); ast_hangup(conf->chan); ast_mutex_unlock(&conf->soundlock); conf->chan = NULL; } else if (conf->fd) { close(conf->fd); conf->fd = 0; } conf->start = 0; conf->zapconf = 0; conf->firstuser = NULL; conf->lastuser = NULL; if (option_verbose > 2){ ast_verbose(VERBOSE_PREFIX_3 "deactivated conference '%s'\n", conf->confno); } ast_mutex_unlock(&conf->lock); if (ast_test_flag(conf, CONFCALL_FLAG_DYNAMIC)) { ast_set_flag(conf, CONFCALL_FLAG_PRUNE); confcall_prune(&conflock); } return res; } static int confcall_activate_conference(struct ast_conference *conf) { struct zt_confinfo ztc; int res = 0; char tmp[CONFCALL_STRLEN]; ast_mutex_lock(&conf->lock); while(!ast_test_flag(conf, CONFCALL_FLAG_ACTIVE)) { conf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL); /* we insist on psuedo channel cos we have too many goodies to live without it!*/ if (conf->chan) { conf->fd = conf->chan->fds[0]; /* for use by conf_play() */ } else { ast_log(LOG_WARNING, "Unable to open pseudo channel - no go!\n"); res = -1; break; } memset(&ztc, 0, sizeof(ztc)); ztc.chan = 0; ztc.confno = -1; ztc.confmode = ZT_CONF_CONFANN; if (ioctl(conf->fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); confcall_deactivate_conference(conf); if (conf->chan) { ast_hangup(conf->chan); } res = -1; break; } snprintf(tmp, sizeof(tmp), "ConfPseudo/%s", conf->confno); ast_copy_string(conf->chan->name, tmp, sizeof(conf->chan->name)); conf->chan->_state = AST_STATE_UP; conf->chan->appl = app; conf->chan->data = conf->confno; /* Fill the conference struct */ conf->start = time(NULL); conf->zapconf = ztc.confno; conf->firstuser = NULL; conf->lastuser = NULL; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Activated ConfCall conference %d for conference '%s'\n", conf->zapconf, conf->confno); ast_set_flag(conf, CONFCALL_FLAG_ACTIVE); break; } ast_mutex_unlock(&conf->lock); return res; } static int confcall_parse_options(struct ast_conference *conf, struct ast_variable *var) { struct ast_variable *vp = NULL; struct ast_conference *dft = NULL; char *dptr = NULL, *cptr = NULL; ast_mutex_lock(&conf->lock); if (strcmp(conf->confno,"default") && (dft = confcall_find_conf("default"))) {/* set defaults */ dptr = (char *) dft + CONFCALL_STRLEN; cptr = (char *) conf + CONFCALL_STRLEN; ast_mutex_lock(&dft->lock); memset(cptr, 0, CONFCALL_STRLEN * CONFCALL_ARGS); memcpy(cptr,dptr, CONFCALL_STRLEN * CONFCALL_ARGS); ast_mutex_unlock(&dft->lock); } for(vp = var ; vp ; vp = vp->next) { if (!strcmp(vp->name, "options")) ast_copy_string(conf->options, vp->value, sizeof(conf->options) ); else if (!strcmp(vp->name, "pin")) ast_copy_string(conf->pin, vp->value, sizeof(conf->pin) ); else if (!strcmp(vp->name, "admin_pin")) ast_copy_string(conf->admin_pin, vp->value, sizeof(conf->admin_pin) ); else if (!strcmp(vp->name, "cid_num")) ast_copy_string(conf->cid_num, vp->value, sizeof(conf->cid_num) ); else if (!strcmp(vp->name, "cid_name")) ast_copy_string(conf->cid_name, vp->value, sizeof(conf->cid_name) ); else if (!strcmp(vp->name, "enter_sound")) ast_copy_string(conf->enter_sound, vp->value, sizeof(conf->enter_sound) ); else if (!strcmp(vp->name, "exit_sound")) ast_copy_string(conf->exit_sound, vp->value, sizeof(conf->exit_sound) ); else if (!strcmp(vp->name, "alone_sound")) ast_copy_string(conf->alone_sound, vp->value, sizeof(conf->alone_sound) ); else if (!strcmp(vp->name, "kicked_sound")) ast_copy_string(conf->kicked_sound, vp->value, sizeof(conf->kicked_sound) ); else if (!strcmp(vp->name, "record_format")) ast_copy_string(conf->record_format, vp->value, sizeof(conf->record_format) ); else if (!strcmp(vp->name, "record_filename")) ast_copy_string(conf->record_filename, vp->value, sizeof(conf->record_filename) ); else if (!strcmp(vp->name, "good_confirm")) ast_copy_string(conf->good_confirm, vp->value, sizeof(conf->good_confirm) ); else if (!strcmp(vp->name, "bad_confirm")) ast_copy_string(conf->bad_confirm, vp->value, sizeof(conf->bad_confirm) ); else if (!strcmp(vp->name, "sound_dir")) ast_copy_string(conf->sound_dir, vp->value, sizeof(conf->sound_dir) ); else if (!strcmp(vp->name, "user_menu")) ast_copy_string(conf->user_menu, vp->value, sizeof(conf->user_menu) ); else if (!strcmp(vp->name, "admin_menu")) ast_copy_string(conf->admin_menu, vp->value, sizeof(conf->admin_menu) ); else if (!strcmp(vp->name, "outbound_context")) ast_copy_string(conf->outbound_context, vp->value, sizeof(conf->outbound_context) ); else if (!strcmp(vp->name, "silence_sample")) ast_copy_string(conf->silence_sample, vp->value, sizeof(conf->silence_sample) ); else if (!strcmp(vp->name, "silence_factor")) ast_copy_string(conf->silence_factor, vp->value, sizeof(conf->silence_factor) ); else if (!strcmp(vp->name, "silence_hangover")) ast_copy_string(conf->silence_hangover, vp->value, sizeof(conf->silence_hangover) ); else if (!strcmp(vp->name, "silence_params")) ast_copy_string(conf->silence_params, vp->value, sizeof(conf->silence_params) ); else if (!strcmp(vp->name, "silence_ceiling")) ast_copy_string(conf->silence_ceiling, vp->value, sizeof(conf->silence_ceiling) ); else if (!strcmp(vp->name, "silence_itterations")) ast_copy_string(conf->silence_itterations, vp->value, sizeof(conf->silence_itterations) ); } if (!ast_strlen_zero(conf->silence_itterations)) { conf->silence_itt = atoi(conf->silence_itterations); } else { conf->silence_itt = 5; } ast_mutex_unlock(&conf->lock); return 0; } static struct ast_conference *confcall_new_conf(char *confno) { struct ast_conference *conf = NULL; if ((conf = malloc(sizeof(struct ast_conference)))) { /* Make a new one */ memset(conf, 0, sizeof(struct ast_conference)); ast_mutex_init(&conf->lock); ast_mutex_init(&conf->soundlock); ast_copy_string(conf->confno, confno, sizeof(conf->confno) ); } return conf; } static int confcall_cli_exec(int fd, int argc, char **argv) { /* Process the command */ struct ast_conference *conf; struct ast_conf_user *user; int hr = 0, min = 0, sec = 0; int i = 0, total = 0; time_t now; char *header_format = "%-14s %-14s %-10s %-8s %-8s\n"; char *data_format = "%-12.12s %4.4d %4.4s %02d:%02d:%02d %-8s\n"; char cmdline[1024] = ""; struct ast_conf_user *uptr = NULL; if (argc > 8) ast_cli(fd, "Invalid Arguments.\n"); /* Check for length so no buffer will overflow... */ for (i = 0; i < argc; i++) { if (strlen(argv[i]) > 100) ast_cli(fd, "Invalid Arguments.\n"); } if (argc == 1) { /* 'ConfCall': List all the conferences */ now = time(NULL); conf = global_conference_list; if (!conf) { ast_cli(fd, "No active ConfCall conferences.\n"); return RESULT_SUCCESS; } ast_cli(fd, header_format, "Conf Num", "Parties", "Marked", "Activity", "Status"); while(conf) { if (conf->markedusers == 0) ast_copy_string(cmdline, "N/A ", sizeof(cmdline) ); else snprintf(cmdline, sizeof(cmdline), "%4.4d", conf->markedusers); if (ast_test_flag(conf, CONFCALL_FLAG_ACTIVE)) { hr = (now - conf->start) / 3600; min = ((now - conf->start) % 3600) / 60; sec = (now - conf->start) % 60; } ast_cli(fd, data_format, conf->confno, conf->users, cmdline, hr, min, sec, ast_test_flag(conf, CONFCALL_FLAG_ACTIVE) ? "Active" : "Inactive"); total += conf->users; conf = conf->next; } ast_cli(fd, "* Total number of ConfCall users: %d\n", total); return RESULT_SUCCESS; } if (argc < 3) return RESULT_SHOWUSAGE; /* Find the right conference */ if (!(conf = confcall_find_conf(argv[2]))) { ast_cli(fd, "No such conference: %s.\n", argv[2]); return RESULT_SUCCESS; } if (strcmp(argv[1], "dial") && !ast_test_flag(conf, CONFCALL_FLAG_ACTIVE)) { ast_cli(fd, "Conference: %s is not active\n", argv[2]); return RESULT_SUCCESS; } if (!strcmp(argv[1], "killsound")) { int soundcount = 0; int sane = 0; if (!global_conference_list) { ast_cli(fd, "No active conferences.\n"); return RESULT_SUCCESS; } if (!(conf = confcall_find_conf(argv[2]))) { ast_cli(fd, "Invalid conference %s.\n",argv[3]); return RESULT_SUCCESS; } ast_set_flag(conf, CONFCALL_FLAG_KILLSOUND); while((soundcount = confcall_soundcount(conf, 0)) > 0) { sane++; if (sane > 2000) { break; } usleep(200); } ast_clear_flag(conf, CONFCALL_FLAG_KILLSOUND); ast_cli(fd, "OK SOUNDS KILLED\n"); return RESULT_SUCCESS; } else if (strstr(argv[1], "lock")) { if (strcmp(argv[1], "lock") == 0) { /* Lock */ if (!ast_test_flag(conf, CONFCALL_FLAG_LOCKED)) { ast_set_flag(conf, CONFCALL_FLAG_LOCKED); confcall_playsound(conf, global_locked, NULL); ast_cli(fd, "Conference: %s is now locked\n", argv[2]); } else { ast_cli(fd, "Conference: %s is already locked\n", argv[2]); } } else { /* Unlock */ if (ast_test_flag(conf, CONFCALL_FLAG_LOCKED)) { ast_clear_flag(conf, CONFCALL_FLAG_LOCKED); confcall_playsound(conf, global_unlocked, NULL); ast_cli(fd, "Conference: %s is now unlocked\n", argv[2]); } else { ast_cli(fd, "Conference: %s is already unlocked\n", argv[2]); } } return RESULT_SUCCESS; } else if (strstr(argv[1], "mute")) { if (argc < 4) return RESULT_SHOWUSAGE; if (strcmp(argv[1], "mute") == 0) { /* Mute */ if (strcmp(argv[3], "all") == 0) { confcall_adminflag_apply_all(fd, conf, ADMINFLAG_MUTE_ME, 0); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (!confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_MUTE_ME, 0)) { ast_cli(fd, "OK %s muted\n", argv[3]); } else { ast_cli(fd, "Sorry! %s already muted\n", argv[3]); } return RESULT_SUCCESS; } else { ast_cli(fd, "No such user %s\n", argv[3]); return RESULT_SUCCESS; } ast_cli(fd, "OK\n"); return RESULT_SUCCESS; } else { /* Unmute */ if (strcmp(argv[3], "all") == 0) { confcall_adminflag_apply_all(fd, conf, ADMINFLAG_UNMUTE_ME, 0); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (!confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_UNMUTE_ME, 0)) { ast_cli(fd, "OK %s unmuted\n", argv[3]); } else { ast_cli(fd, "Sorry! %s already unmuted\n", argv[3]); } return RESULT_SUCCESS; } else { ast_cli(fd, "No such user %s\n", argv[3]); return RESULT_SUCCESS; } ast_cli(fd, "OK\n"); return RESULT_SUCCESS; } } else if (strstr(argv[1], "dtmf") && argc > 4) { if (strcmp(argv[3], "all") == 0) { ast_mutex_lock(&conf->lock); for(uptr = conf->firstuser; uptr; uptr = uptr->nextuser) { ast_copy_string(uptr->dtmf, argv[4], sizeof(uptr->dtmf)); confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_DTMF, 0); ast_cli(fd, "OK %d dtmf=%s\n", uptr->user_no, argv[4]); } ast_mutex_unlock(&conf->lock); } else if ((uptr = confcall_find_user(conf, argv[3]))) { ast_copy_string(uptr->dtmf, argv[4], sizeof(uptr->dtmf)); confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_DTMF, 0); ast_cli(fd, "OK %d dtmf=%s\n", uptr->user_no, argv[4]); } } else if (strstr(argv[1], "vol") && argc > 3) { if (strcmp(argv[3], "all") == 0) { ast_mutex_lock(&conf->lock); for(uptr = conf->firstuser; uptr; uptr = uptr->nextuser) { if (argc > 4) { uptr->vol = atoi(argv[4]); if (uptr->vol > 16) { uptr->vol = 16; } else if (uptr->vol < -16) { uptr->vol = -16; } } ast_cli(fd, "OK %d vol=%d\n", uptr->user_no, uptr->vol); } ast_mutex_unlock(&conf->lock); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (argc > 4) { uptr->vol = atoi(argv[4]); if (uptr->vol > 16) { uptr->vol = 16; } else if (uptr->vol < -16) { uptr->vol = -16; } } ast_cli(fd, "OK %s vol=%d\n", argv[3],uptr->vol); } } else if (strstr(argv[1], "silence") && argc > 3) { ast_cli(fd, "user\tfactor\tlast\taverage\tceiling\titt\tflags\n"); if (strcmp(argv[3], "all") == 0) { int flags = 0; ast_mutex_lock(&conf->lock); for(uptr = conf->firstuser; uptr; uptr = uptr->nextuser) { uptr->flags |= flags; if (argc > 4) { uptr->working_silence_factor = atoi(argv[4]); if (strchr(argv[4], 'a')) { ast_set_flag(uptr, CONFCALL_USER_FLAG_AUTOSILENCE); } else if (strchr(argv[4], 'n')) { ast_clear_flag(uptr, CONFCALL_USER_FLAG_AUTOSILENCE); } } if (argc > 5) { uptr->silence_ceiling = atoi(argv[5]); } ast_cli(fd, "%d\t%d\t%d\t%d\t%d\t%d\t%s\n", uptr->user_no, uptr->working_silence_factor, uptr->lastmap, uptr->silence, uptr->silence_ceiling, conf->silence_itt, ast_test_flag(uptr, CONFCALL_USER_FLAG_AUTOSILENCE) ? "(auto)" : ""); } ast_cli(fd,"\nOK\n"); ast_mutex_unlock(&conf->lock); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (argc > 4) { uptr->working_silence_factor = atoi(argv[4]); if (strchr(argv[4], 'a')) { ast_set_flag(uptr, CONFCALL_USER_FLAG_AUTOSILENCE); } else if (strchr(argv[4], 'n')) { ast_clear_flag(uptr, CONFCALL_USER_FLAG_AUTOSILENCE); } } if (argc > 5) { uptr->silence_ceiling = atoi(argv[5]); } ast_cli(fd, "%s\t%d\t%d\t%d\t%d\t%d\t%s\n", argv[3], uptr->working_silence_factor, uptr->lastmap, uptr->silence, uptr->silence_ceiling, conf->silence_itt, ast_test_flag(uptr, CONFCALL_USER_FLAG_AUTOSILENCE) ? "(auto)" : ""); ast_cli(fd,"\nOK\n"); } } else if (strstr(argv[1], "mark")) { if (argc < 4) return RESULT_SHOWUSAGE; if (strcmp(argv[1], "mark") == 0) { /* Mark */ if (strcmp(argv[3], "all") == 0) { confcall_adminflag_apply_all(fd, conf, ADMINFLAG_MARK_ME, 0); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (!confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_MARK_ME, 0)) { ast_cli(fd, "OK %s marked\n", argv[3]); } else { ast_cli(fd, "Sorry! %s already marked\n", argv[3]); } return RESULT_SUCCESS; } else { ast_cli(fd, "No such user %s\n", argv[3]); return RESULT_SUCCESS; } ast_cli(fd, "OK\n"); return RESULT_SUCCESS; } else { /* Unmark */ if (strcmp(argv[3], "all") == 0) { confcall_adminflag_apply_all(fd, conf, ADMINFLAG_UNMARK_ME, CONFFLAG_ADMIN); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (!confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_UNMARK_ME, CONFFLAG_ADMIN)) { ast_cli(fd, "OK %s unmarked\n", argv[3]); } else { ast_cli(fd, "Sorry! %s already unmarked\n", argv[3]); } return RESULT_SUCCESS; } else { ast_cli(fd, "No such user %s\n", argv[3]); return RESULT_SUCCESS; } ast_cli(fd, "OK\n"); return RESULT_SUCCESS; } } else if (strstr(argv[1], "admin")) { if (argc < 4) return RESULT_SHOWUSAGE; if (strcmp(argv[1], "admin") == 0) { /* Admin */ if (strcmp(argv[3], "all") == 0) { confcall_adminflag_apply_all(fd, conf, ADMINFLAG_ADMIN_ME, 0); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (!confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_ADMIN_ME, 0)) { ast_cli(fd, "OK %s is an admin.\n", argv[3]); } else { ast_cli(fd, "Sorry! %s already is an admin.\n", argv[3]); } return RESULT_SUCCESS; } else { ast_cli(fd, "No such user %s.\n", argv[3]); return RESULT_SUCCESS; } ast_cli(fd, "OK.\n"); return RESULT_SUCCESS; } else { /* Unadmin */ if (strcmp(argv[3], "all") == 0) { confcall_adminflag_apply_all(fd, conf, ADMINFLAG_UNADMIN_ME, CONFFLAG_ADMIN); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (!confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_UNADMIN_ME,0)) { ast_cli(fd, "OK %s is no longer an admin.\n", argv[3]); } else { ast_cli(fd, "Sorry! %s already is no longer an admin.\n", argv[3]); } return RESULT_SUCCESS; } else { ast_cli(fd, "No such user %s.\n", argv[3]); return RESULT_SUCCESS; } ast_cli(fd, "OK\n"); return RESULT_SUCCESS; } } else if (strcmp(argv[1], "dial") == 0 && argv[3] && !ast_strlen_zero(argv[3])) { if (strchr(argv[3], '/') || ast_exists_extension(NULL, conf->outbound_context, argv[3], 1, NULL)) { struct confcall_outbound *ob; if ((ob = malloc(sizeof(struct confcall_outbound)))) { memset(ob, 0, sizeof(struct confcall_outbound)); ob->conf = conf; if (strchr(argv[3], '/')) { ast_copy_string(ob->exten, argv[3], sizeof(ob->exten) ); } else { snprintf(ob->exten, sizeof(ob->exten), "%s@%s", argv[3], conf->outbound_context); } if (argc > 4) { ast_copy_string(ob->cid_num, argv[4], sizeof(ob->cid_num)); } if (argc > 5) { ast_copy_string(ob->cid_name, argv[5], sizeof(ob->cid_name)); } launch_outbound_thread(ob); } } else { ast_cli(fd, "Invalid Number!\n"); } return RESULT_SUCCESS; } else if (strcmp(argv[1], "kick") == 0) { /* Kick */ if (argc > 3) { if (strcmp(argv[3], "all") == 0) { confcall_adminflag_apply_all(fd, conf, ADMINFLAG_KICK_ME, 0); } else if ((uptr = confcall_find_user(conf, argv[3]))) { if (!confcall_adminflag_apply_user(fd, uptr, ADMINFLAG_KICK_ME, 0)) { ast_cli(fd, "OK %s kicked\n", argv[3]); } else { ast_cli(fd, "Sorry! %s already kicked\n", argv[3]); } return RESULT_SUCCESS; } else { ast_cli(fd, "No such user %s\n", argv[3]); return RESULT_SUCCESS; } ast_cli(fd, "OK\n"); return RESULT_SUCCESS; } else { ast_cli(fd, "Invalid Arguement!\n"); } } else if (argc > 2 && strcmp(argv[1], "verbose") == 0) { if (!global_conference_list) { ast_cli(fd, "No active conferences.\n"); return RESULT_SUCCESS; } if (!(conf = confcall_find_conf(argv[2]))) { ast_cli(fd, "Invalid conference %s.\n",argv[3]); return RESULT_SUCCESS; } if (argc > 3) { conf->verbose = atoi(argv[3]); } ast_cli(fd, "Conference %s verbose is now %d\n",argv[2], conf->verbose); } else if (strcmp(argv[1], "list") == 0) { /* List all the users in a conference */ if (!global_conference_list) { ast_cli(fd, "No active conferences.\n"); return RESULT_SUCCESS; } /* Show all the users */ for(user = conf->firstuser; user; user = user->nextuser) { cmdline[0] = '\0'; total = 0; char *fmt_a = "%i (%s <%s>)@%s (%s)\n"; char *fmt_b = "%i|%s|%s|%s|%s\n"; if (user->confflags & CONFFLAG_ADMIN) { snprintf(cmdline, sizeof(cmdline), "admin"); total++; } if (user->confflags & CONFFLAG_MUTED) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline), "%smuted",total ? "|" : ""); total++; } if (user->confflags & CONFFLAG_MARKED) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline), "%smarked",total ? "|" : ""); total++; } if (!total) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline), "%snone",total ? "|" : ""); } ast_cli(fd, argc > 3 ? fmt_b : fmt_a , user->user_no, user->chan->cid.cid_name ? user->chan->cid.cid_name : "N/A", user->chan->cid.cid_num ? user->chan->cid.cid_num : "N/A", user->chan->name,cmdline); } return RESULT_SUCCESS; } else if (strcmp(argv[1], "play") == 0) { if (argc > 3 && !ast_strlen_zero(argv[3])) { char file_path[512] = ""; char *sound_dir = NULL; char *file = NULL; if (argv[3][0] == '/') { file = argv[3]; } else { sound_dir = ast_strlen_zero(conf->sound_dir) ? "/var/lib/asterisk/sounds" : conf->sound_dir; snprintf(file_path, sizeof(file_path), "%s/%s", sound_dir, argv[3]); file = file_path; } if (ast_fileexists(file, NULL, NULL) > 0) { confcall_playsound(conf, file, NULL); } else { ast_cli(fd, "No such file: %s.\n", argv[3]); return RESULT_SUCCESS; } } return RESULT_SUCCESS; } else return RESULT_SHOWUSAGE; //ast_log(LOG_DEBUG, "Cmdline: %s\n", cmdline); //confcall_admin_exec(NULL, cmdline); return 0; } static char *confcall_complete_helper(char *line, char *word, int pos, int state) { #define CONF_COMMANDS 16 #define USER_COMMANDS 3 int which = 0, x = 0; struct ast_conference *conf = NULL; struct ast_conf_user *usr = NULL; char *confno = NULL; char usrno[50] = ""; char cmds[CONF_COMMANDS][20] = {"lock", "unlock", "mute", "unmute", "kick", "mark", "list", "killsound", "play", "dial", "admin", "unadmin", "vol", "silence", "verbose", "dtmf"}; char ucmds[USER_COMMANDS][20] = {"first","last","all"}; char *myline; if (pos == 1) { /* Command */ for (x = 0;x < CONF_COMMANDS; x++) { if (!strncasecmp(cmds[x], word, strlen(word))) { if (++which > state) { return strdup(cmds[x]); } } } } else if (pos == 2) { /* Conference Number */ ast_mutex_lock(&conflock); conf = global_conference_list; while(conf) { if (ast_test_flag(conf, CONFCALL_FLAG_ACTIVE) && !strncasecmp(word, conf->confno, strlen(word))) { if (++which > state) break; } conf = conf->next; } ast_mutex_unlock(&conflock); return conf ? strdup(conf->confno) : NULL; } else if (pos == 3) { /* User Number || Conf Command option*/ if (strstr(line, "mute") || strstr(line, "kick") || strstr(line, "mark") || strstr(line, "admin") || strstr(line, "vol") || strstr(line, "dtmf") || strstr(line, "silence")) { which++; ast_mutex_lock(&conflock); conf = global_conference_list; /* TODO: Find the conf number from the cmdline (ignore spaces) <- test this and make it fail-safe! */ myline = ast_strdupa(line); if (strsep(&myline, " ") && strsep(&myline, " ") && !confno) { while((confno = strsep(&myline, " ")) && (strcmp(confno, " ") == 0)) ; } while(conf) { if (strcmp(confno, conf->confno) == 0) { break; } conf = conf->next; } if (conf) { /* Search for the user */ usr = conf->firstuser; while(usr) { snprintf(usrno, sizeof(usrno), "%i", usr->user_no); if (!strncasecmp(word, usrno, strlen(word))) { if (++which > state) break; } usr = usr->nextuser; } } ast_mutex_unlock(&conflock); if (usr) return strdup(usrno); if (!conf->firstuser) return NULL; for (x = 0;x < USER_COMMANDS; x++) { if (!strncasecmp(ucmds[x], word, strlen(word))) { if (++which > state) { return strdup(ucmds[x]); } } } return NULL; } } return NULL; } static char conf_usage[] = "Usage: confcall (un)lock|(un)mute|kick|list \n" " Executes a command for the conference or on a conferee\n"; static struct ast_cli_entry cli_confcall = { { "confcall", NULL, NULL }, confcall_cli_exec, "Execute a command on a conference or conferee", conf_usage, confcall_complete_helper }; static int confnonzero(void *ptr) { struct ast_conference *conf = ptr; int res; ast_mutex_lock(&conf->lock); res = (conf->markedusers == 0); ast_mutex_unlock(&conf->lock); return res; } static void confcall_join_conference(struct ast_channel *chan, struct ast_conference *conf, unsigned int confflags); struct confcall_playsound_obj { struct ast_conference *conf; char *sounds[100]; int numsounds; }; static void confcall_careful_stream(struct ast_conference *conf, char *filename) { struct ast_frame *f = NULL; struct ast_trans_pvt *trans = NULL; struct ast_filestream *stream; int res = 0; confcall_soundcount(conf, 1); if (conf->chan) { if (ast_fileexists(filename, NULL, NULL) > 0) { ast_mutex_lock(&conf->chan->lock); stream = ast_openstream_full(conf->chan, filename, conf->chan->language, 1); ast_mutex_unlock(&conf->chan->lock); conf->chan->stream = NULL; if (stream) { while(! ast_check_hangup(conf->chan) && ast_test_flag(conf, CONFCALL_FLAG_ACTIVE) && !ast_test_flag(conf, CONFCALL_FLAG_KILLSOUND) && ((res = ast_waitfor(conf->chan, -1))) > -1 && (f = ast_readframe(stream)) ) { if (!trans && f->subclass != conf->chan->readformat) trans = ast_translator_build_path(conf->chan->readformat, f->subclass); if (trans) { f = ast_translate(trans, f, 1); } careful_write(conf->fd, f->data, f->datalen); ast_frfree(f); } if (trans) { ast_translator_free_path(trans); trans = NULL; } if (stream) { ast_closestream(stream); stream = NULL; } } } } else { ast_log(LOG_ERROR, "No Channel To Stream on!\n"); } res = confcall_soundcount(conf, -1); } static void *confcall_outbound_thread(void *ptr) { struct confcall_outbound *ob = ptr; struct ast_conference *conf = ob->conf; struct ast_channel *newchan = NULL; int outstate = 0; char *proto; char *dialstr; if (!(proto = ast_strdupa(ob->exten))) { free(ob); return NULL; } if ((dialstr = strchr(proto, '/'))) { *dialstr = '\0'; dialstr++; } else { dialstr = proto; proto = "Local"; } if ((newchan = ast_request_and_dial(proto, AST_FORMAT_ULAW, dialstr, 30000, &outstate, ast_strlen_zero(ob->cid_num) ? conf->cid_num : ob->cid_num, ast_strlen_zero(ob->cid_name) ? conf->cid_name : ob->cid_name ))) { ast_set_callerid(newchan, dialstr, "Outbound Call", NULL); newchan->appl = app; newchan->data = conf->confno; if (!newchan->cdr) { if ((newchan->cdr = ast_cdr_alloc())) { ast_cdr_init(newchan->cdr, newchan); ast_cdr_start(newchan->cdr); } } ast_answer(newchan); confcall_join(newchan, conf, 0); if (newchan) ast_hangup(newchan); } free(ob); return NULL; } static void launch_outbound_thread(struct confcall_outbound *ob) { pthread_attr_t attr; int result = 0; pthread_t thread; result = pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); result = ast_pthread_create(&thread, &attr, confcall_outbound_thread, ob); result = pthread_attr_destroy(&attr); } static void *confcall_playsound_thread(void *ptr) { struct confcall_playsound_obj *sound = ptr; char *filename = NULL; char tmpfile[80]; int i = 0, x = 0, len = 0; ast_mutex_lock(&sound->conf->soundlock); for(i = 0; i < sound->numsounds ; i++) { if ((filename = sound->sounds[i])) { if (!strcmp(filename, "num")) { free(filename); if (!(filename = sound->sounds[++i])) continue; len = strlen(filename); for(x = 0 ; x < len; x++) { snprintf(tmpfile,sizeof(tmpfile), "digits/%c", filename[x]); confcall_careful_stream(sound->conf, tmpfile); } } else { confcall_careful_stream(sound->conf, filename); } free(filename); filename = NULL; } } ast_mutex_unlock(&sound->conf->soundlock); free(sound); return NULL; } static void confcall_playsound(struct ast_conference *conf, ...) { pthread_attr_t attr; int result = 0; pthread_t thread; char *data = NULL; va_list ap; struct confcall_playsound_obj *sound = NULL; if (!conf || !ast_test_flag(conf, CONFCALL_FLAG_ACTIVE)) { ast_log(LOG_WARNING,"Trying to play sound in an inactive or unallocated confernece!\n"); return; } if ((sound = malloc(sizeof(struct confcall_playsound_obj)))) { memset(sound, 0, sizeof(struct confcall_playsound_obj)); va_start(ap, conf); while((data = va_arg(ap, char *))) { if (!ast_strlen_zero(data)) { sound->sounds[sound->numsounds++] = strdup(data); } else { sound->numsounds++; } } va_end(ap); sound->conf = conf; result = pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); result = ast_pthread_create(&thread, &attr, confcall_playsound_thread, sound); result = pthread_attr_destroy(&attr); } } static void *confcall_monitor_conference_thread(void *ptr) { struct ast_conference *conf = ptr; char monitor_args[80]; struct ast_frame *f = NULL; char *confno = ast_strdupa(conf->confno); char *record_format = NULL, *record_filename = NULL; char tmp[CONFCALL_STRLEN]; if (!(conf->record_chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL))) { ast_log(LOG_WARNING, "Unable to open pseudo channel - no go!\n"); return NULL; } confcall_soundcount(conf, 1); confcall_activate_zap_conference(conf->record_chan->fds[0], conf->zapconf, ZT_CONF_CONF | ZT_CONF_LISTENER); snprintf(tmp, sizeof(tmp), "ConfMonitor/%s", conf->confno); ast_copy_string(conf->record_chan->name, tmp,sizeof(conf->record_chan->name)); conf->record_chan->_state = AST_STATE_UP; record_format = ast_strlen_zero(conf->record_format) ? global_record_format : conf->record_format; record_filename = ast_strlen_zero(conf->record_filename) ? global_record_filename : conf->record_filename; ast_verbose(VERBOSE_PREFIX_2 "Recording Conference %s\n", confno); snprintf(monitor_args, sizeof(monitor_args), "%s|%s-%s-%ld|m", record_format, record_filename, conf->confno, time(NULL)); pbx_exec(conf->record_chan, global_monitor_app, monitor_args, 1); conf->chan->appl = app; conf->chan->data = conf->confno; while (conf && ast_test_flag(conf, CONFCALL_FLAG_RECORDING) && conf->record_chan && conf->record_chan->monitor && ast_waitfor(conf->record_chan, 1000) > -1) { if ((f = ast_read(conf->record_chan))) ast_frfree(f); else break; } confcall_deactivate_zap_conference(conf->record_chan->fds[0]); ast_monitor_stop(conf->record_chan, 1); ast_verbose(VERBOSE_PREFIX_2 "Done Recording Conference %s\n", confno); ast_hangup(conf->record_chan); conf->record_chan = NULL; confcall_soundcount(conf, -1); return NULL; } static void confcall_monitor_conference(struct ast_conference *conf) { pthread_attr_t attr; int result = 0; pthread_t thread; result = pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); result = ast_pthread_create(&thread, &attr, confcall_monitor_conference_thread, conf); result = pthread_attr_destroy(&attr); } static int confcall_activate_zap_conference(int fd, int confno, int flags) { struct zt_confinfo ztc; memset(&ztc, 0, sizeof(ztc)); ztc.chan = 0; ztc.confno = confno; ztc.confmode = flags; if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); return -1; } return 0; } static int confcall_deactivate_zap_conference(int fd) { struct zt_confinfo ztc; if (fd) { memset(&ztc, 0, sizeof(ztc)); if (ioctl(fd, ZT_SETCONF, &ztc)) { ast_log(LOG_WARNING, "Error setting conference\n"); return -1; } } return 0; } static int confcall_set_buffering(int fd) { /* Setup buffering information */ ZT_BUFFERINFO bi; int x = 1; memset(&bi, 0, sizeof(bi)); bi.bufsize = CONF_SIZE / 2; 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)); return -1; } if (ioctl(fd, ZT_SETLINEAR, &x)) { ast_log(LOG_WARNING, "Unable to set linear mode: %s\n", strerror(errno)); return -1; } return 0; } static int confcall_open_pseudo_fd(void) { int fd = 0, flags = 0; fd = open("/dev/zap/pseudo", O_RDWR); if (fd < 0) { ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno)); return 0; } /* 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); return 0; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); close(fd); return 0; } /* 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); return 0; } if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { ast_log(LOG_WARNING, "Unable to set flags: %s\n", strerror(errno)); close(fd); return 0; } if (confcall_set_buffering(fd)) { close(fd); return 0; } return fd; } static int confcall_copy_audio(struct ast_channel *chan, int fd, int format) { char __buf[CONF_SIZE + AST_FRIENDLY_OFFSET]; char *buf = __buf + AST_FRIENDLY_OFFSET; struct ast_frame write_frame; int res = 0; res = read(fd, buf, CONF_SIZE); if (res > 0) { memset(&write_frame, 0, sizeof(write_frame)); write_frame.frametype = AST_FRAME_VOICE; write_frame.subclass = format; write_frame.datalen = res; write_frame.samples = res; write_frame.data = buf; write_frame.offset = AST_FRIENDLY_OFFSET; ast_write(chan, &write_frame); } else { ast_log(LOG_WARNING, "Failed to read frame: %s\n", strerror(errno)); } return res; } static int confcall_stream_file(ast_mutex_t *lock, struct ast_channel *chan, const char *filename) { int res = 0, moh = 0, rf=0,wf=0; if (lock) { ast_mutex_lock(lock); } if (ast_test_flag(chan, AST_FLAG_MOH)) { ast_moh_stop(chan); moh = 1; } rf = chan->readformat; wf = chan->writeformat; if ((res = ast_streamfile(chan, filename, chan->language))) { if (moh) { ast_moh_start(chan, NULL); } return res; } res = ast_waitstream(chan, AST_DIGIT_ANY); if (chan->stream) { ast_stopstream(chan); } if (chan->readformat != rf) { ast_set_read_format(chan, rf); ast_log(LOG_WARNING, "Someone didnt restore my read format >=|\n"); } if (chan->writeformat != wf) { ast_set_write_format(chan, wf); ast_log(LOG_WARNING, "Someone didnt restore my write format >=|\n"); } if (res >= 0 && moh) { ast_moh_start(chan, NULL); } if (lock) { ast_mutex_unlock(lock); } return res; } static int confcall_stream_and_wait(struct ast_channel *chan, const char *filename, int to) { int res = 0; if ((res = confcall_stream_file(NULL, chan, filename))) { return res; } else { return ast_waitfordigit(chan, to); } } static int confcall_handle_dtmf(struct ast_conf_user *user, int dtmf, int *fd) { struct ast_conf_user *uptr = NULL; struct ast_channel *newchan = NULL, *chan = user->chan; struct ast_conference *conf = user->conf; char newext[CONFCALL_STRLEN], dialstr[CONFCALL_STRLEN]; char *file = NULL, *value = NULL, *confcall_outbound_context = NULL, *cid_num = NULL, *cid_name = NULL, *good_confirm = NULL, *bad_confirm = NULL, *admin_menu = NULL, *user_menu = NULL; int outstate = 0, res = 0, running = 1, moh = 0; struct ast_bridge_config bconfig; good_confirm = ast_strlen_zero(conf->good_confirm) ? global_good_confirm : conf->good_confirm; bad_confirm = ast_strlen_zero(conf->bad_confirm) ? global_bad_confirm : conf->bad_confirm; admin_menu = ast_strlen_zero(conf->admin_menu) ? global_admin_menu : conf->admin_menu; user_menu = ast_strlen_zero(conf->user_menu) ? global_user_menu : conf->user_menu; if ((user->confflags & CONFFLAG_EXIT_CONTEXT)) { char ext[2]; char *exitcontext = NULL; if (!(exitcontext = pbx_builtin_getvar_helper(chan, "CONFCALL_EXIT_CONTEXT"))) exitcontext = chan->context; ext[0] = dtmf; ext[1] = '\0'; if (ast_exists_extension(chan, exitcontext, ext, 1, chan->cid.cid_num)) { ast_explicit_goto(chan, exitcontext, ext, 0); return -1; } } if (ast_test_flag(chan, AST_FLAG_MOH)) { moh++; ast_moh_stop(chan); } if (*fd && dtmf == '*' && (user->confflags & CONFFLAG_STARMENU)) { confcall_deactivate_zap_conference(*fd); for(res = 0; running && res >= 0; dtmf = 0) { if (!res) { if (!(res = ast_streamfile(chan, (user->confflags & CONFFLAG_ADMIN) ? admin_menu : user_menu, chan->language))) { if ((res = ast_waitstream(chan, AST_DIGIT_ANY)) < 0) break; } } if (!res && (res = ast_waitfordigit(chan, 10000)) < 0) break; if ((dtmf = res)) { if (chan) { ast_stopstream(chan); } if (dtmf != '*' && dtmf != '#') { dtmf += (user->confflags & CONFFLAG_ADMIN) ? 16 : 48; } running = 0; /* set it to 1 in the case: unless you are happy exiting the menu */ switch(dtmf) { /* A-I = admin 0-9 ; a-i = luser 0-9 */ /* 0 volume */ case 64: case 96: if ((res = ast_waitfordigit(chan, 20000)) > 0) { int i = res, neg = 0; res = 0; if (i == '*') { if ((i = ast_waitfordigit(chan, 20000)) > 0) { neg = 1; } else { res = i; break; } } i -= 48; /* convert to int */ if (i >= 0 && i < 10) { user->vol = i; if (neg) { user->vol *= -1; } } res = ast_say_digits(chan, user->vol ,"", chan->language); manager_event(EVENT_FLAG_USER, "confcall_volume", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n" "Volume: %d\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num, user->vol ); } break; case 'a': /* 1 Un/Mute */ case 'A': if (user->zapflags & ZT_CONF_TALKER) { user->zapflags = ZT_CONF_CONF | ZT_CONF_LISTENER; user->confflags |= CONFFLAG_MUTED; if ((user->confflags & CONFFLAG_MOH_MUTED) && !ast_test_flag(chan, AST_FLAG_MOH)) ast_moh_start(chan, NULL); } else { user->zapflags = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; user->confflags &= ~CONFFLAG_MUTED; if ((user->confflags & CONFFLAG_MOH_MUTED)) { moh = 0; ast_moh_stop(chan); } } res = confcall_stream_file(NULL, chan, (user->zapflags & ZT_CONF_TALKER) ? global_unmuted : global_muted); manager_event(EVENT_FLAG_USER, (user->zapflags & ZT_CONF_TALKER) ? "confcall_unmute" : "confcall_mute", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n" "Voluntary: Yes\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num ); break; case 'B': /* 2 Un/Lock the Conference */ if (ast_test_flag(conf, CONFCALL_FLAG_LOCKED)) { ast_clear_flag(conf, CONFCALL_FLAG_LOCKED); file = global_locked; } else { ast_set_flag(conf, CONFCALL_FLAG_LOCKED); file = global_unlocked; } confcall_playsound(conf, file, NULL); res = 0; //if ((res = confcall_stream_file(NULL, chan, file))) //break; break; case 'b': /* 2 Become Admin */ if (ast_strlen_zero(conf->admin_pin)) { if ((res = confcall_stream_file(NULL, chan, global_menerror))) break; } else { ast_app_getdata(chan, global_admin_pin, newext, CONFCALL_STRLEN - 1, 20000); if (!strcmp(newext, conf->admin_pin)) { user->confflags |= CONFFLAG_ADMIN; if ((res = confcall_stream_file(NULL, chan, good_confirm))) break; } else { if ((res = confcall_stream_file(NULL, chan, global_invalid_admin_pin))) break; } } break; /* Eject last user */ case 'C': uptr = conf->lastuser; if ((uptr->chan->name == chan->name)||(uptr->confflags & CONFFLAG_ADMIN)) { if ((res = confcall_stream_file(NULL, chan, global_menerror))) break; } else { uptr->adminflags |= ADMINFLAG_KICK_ME; if ((res = confcall_stream_file(NULL, chan, good_confirm))) break; } break; case 'D': /* 4 Record */ if (global_monitor_app) { if (ast_test_flag(conf, CONFCALL_FLAG_RECORDING)) { ast_clear_flag(conf, CONFCALL_FLAG_RECORDING); if ((res = confcall_stream_file(NULL, chan, good_confirm))) break; } else { ast_set_flag(conf, CONFCALL_FLAG_RECORDING); if ((res = confcall_stream_file(NULL, chan, good_confirm))) break; if ((value=pbx_builtin_getvar_helper(chan, "MONITOR_EXEC"))) pbx_builtin_setvar_helper(conf->chan, "MONITOR_EXEC", value); if ((value=pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS"))) pbx_builtin_setvar_helper(conf->chan, "MONITOR_EXEC_ARGS", value); pbx_builtin_setvar_helper(chan, "RECORDED_CONFERENCE", conf->confno); confcall_monitor_conference(conf); } } break; case 'E': /* 5 Outbound */ if (!(confcall_outbound_context = pbx_builtin_getvar_helper(chan, "CONFCALL_OUTBOUND_CONTEXT"))) { confcall_outbound_context = ast_strlen_zero(conf->outbound_context) ? chan->context : conf->outbound_context; } res = ast_app_dtget(chan, confcall_outbound_context, newext, sizeof(newext), 100, 10000); cid_num = chan->cid.cid_num; cid_name = chan->cid.cid_name; if (ast_exists_extension(chan, confcall_outbound_context, newext, 1, cid_num)) { snprintf(dialstr, sizeof(dialstr), "%s@%s/n", newext, confcall_outbound_context); ast_indicate(chan, AST_CONTROL_RINGING); if ((newchan = ast_request_and_dial("Local", AST_FORMAT_SLINEAR, dialstr, 30000, &outstate, conf->cid_num, conf->cid_name))) { res = ast_channel_make_compatible(chan, newchan); if (res < 0) { ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", chan->name, newchan->name); ast_hangup(newchan); if ((res = confcall_stream_file(NULL, chan, bad_confirm))) break; } memset(&bconfig, 0, sizeof(struct ast_bridge_config)); ast_set_flag(&(bconfig.features_caller), AST_FEATURE_DISCONNECT); ast_set_flag(&(bconfig.features_callee), AST_FEATURE_DISCONNECT); if (ast_set_write_format(newchan, AST_FORMAT_SLINEAR) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to write linear mode\n", newchan->name); break; } if (ast_set_read_format(newchan, AST_FORMAT_SLINEAR) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to read linear mode\n", newchan->name); break; } ast_indicate(chan, -1); res = ast_bridge_call(chan, newchan, &bconfig); ast_indicate(chan, -1); if (!newchan->_softhangup && newchan->_state == AST_STATE_UP) { if (ast_autoservice_start(newchan)) break; if ((res = confcall_stream_file(NULL, chan, good_confirm))) break; res = ast_waitfordigit(chan, 20000); } else res = -1; if (res != '1') { ast_hangup(newchan); if ((res = confcall_stream_file(NULL, chan, bad_confirm))) break; } else { if ((res = confcall_stream_file(NULL, chan, good_confirm))) break; confcall_dup_vars(chan, newchan); confcall_join_conference(newchan, conf, user->confflags & ~(CONFFLAG_ADMIN|CONFFLAG_QUIET)); } } else { if ((res = confcall_stream_file(NULL, chan, bad_confirm))) break; } } else { if ((res = confcall_stream_file(NULL, chan, bad_confirm))) break; } break; case 'F': /* 6 sounds */ if (!(res = ast_app_getdata(chan, good_confirm, newext, CONFCALL_STRLEN - 1, 20000))) { if (!ast_strlen_zero(newext)) { char file_path[512] = ""; char *sound_dir = NULL; sound_dir = ast_strlen_zero(conf->sound_dir) ? "/var/lib/asterisk/sounds" : conf->sound_dir; snprintf(file_path, sizeof(file_path), "%s/%s", sound_dir, newext); if (ast_fileexists(file_path, NULL, NULL) > 0) { confcall_playsound(conf, file_path, NULL); } else { if ((res = confcall_stream_file(NULL, chan, bad_confirm))) break; } } } break; case 'G': /* 7 toggle marked */ ast_mutex_lock(&conf->lock); if (user->confflags & CONFFLAG_MARKED) { user->confflags &= ~CONFFLAG_MARKED; conf->markedusers--; } else { user->confflags |= CONFFLAG_MARKED; conf->markedusers++; } ast_mutex_unlock(&conf->lock); if ((res = confcall_stream_file(NULL, chan, good_confirm))) break; break; case 'H': /* 8 list */ if ((res = ast_waitfordigit(chan, 20000)) > 0) { int i = res; res = 0; switch(i) { case '1': case '2': snprintf(newext, sizeof(newext), "%d", conf->users); confcall_playsound(conf, "num", newext, NULL); for (uptr = conf->firstuser; uptr; uptr = uptr->nextuser) { char cmdline[80] = ""; int total = 0; if (uptr->confflags & CONFFLAG_ADMIN) { snprintf(cmdline, sizeof(cmdline), "admin"); total++; } if (uptr->confflags & CONFFLAG_MUTED) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline), "%smuted",total ? "|" : ""); total++; } if (uptr->confflags & CONFFLAG_MARKED) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline), "%smarked",total ? "|" : ""); total++; } if (!total) { snprintf(cmdline + strlen(cmdline), sizeof(cmdline), "%snone",total ? "|" : ""); } if (i == '2') { int cid = 0; snprintf(newext, sizeof(newext), "%d", uptr->user_no); cid = (uptr->chan->cid.cid_num && !ast_strlen_zero(uptr->chan->cid.cid_num)) ? 1 : 0; confcall_playsound(conf, "num", newext, uptr->namerecloc, cid ? "vm-from" : "", cid ? "num" : global_unknown, cid ? uptr->chan->cid.cid_num : "", (uptr->confflags & CONFFLAG_MUTED) ? global_muted : global_unmuted, (uptr->confflags & CONFFLAG_MARKED) ? global_marked : global_unmarked, (uptr->confflags & CONFFLAG_ADMIN) ? global_admin : global_user, "confcall/silence", NULL); } manager_event(EVENT_FLAG_USER, "confcall_roster", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n" "Flags: %s\r\n", conf->confno, uptr->user_no, uptr->chan->name, uptr->chan->cid.cid_name, uptr->chan->cid.cid_num, cmdline ); } break; default: res = confcall_stream_file(NULL, chan, bad_confirm); break; } } else if (!res) { res = confcall_stream_file(NULL, chan, bad_confirm); } break; case 'I': /* 9 dial a mute or kick or admin */ if (!(res = ast_app_getdata(chan, good_confirm, newext, CONFCALL_STRLEN - 1, 20000))) { int i = 0; if ((i = atoi(newext)) && (uptr = confcall_find_user_int(conf, i))) { if ((res = confcall_stream_and_wait(chan, good_confirm, 20000)) > 0) { switch(res) { case '1': uptr->adminflags |= ADMINFLAG_KICK_ME; break; case '2': uptr->adminflags |= (uptr->confflags & CONFFLAG_MUTED) ? ADMINFLAG_UNMUTE_ME : ADMINFLAG_MUTE_ME; break; case '3': uptr->adminflags |= (uptr->confflags & CONFFLAG_ADMIN) ? ADMINFLAG_UNADMIN_ME : ADMINFLAG_ADMIN_ME; break; case '4': uptr->adminflags |= (uptr->confflags & CONFFLAG_MARKED) ? ADMINFLAG_UNMARK_ME : ADMINFLAG_MARK_ME; break; } res = confcall_stream_file(NULL, chan, good_confirm); } else if (!res) res = confcall_stream_file(NULL, chan, bad_confirm); } else if (!res) res = confcall_stream_file(NULL, chan, bad_confirm); } else if (!res) { res = confcall_stream_file(NULL, chan, bad_confirm); } break; case '#': res = -1; break; default: /* Play an error message! */ res = confcall_stream_file(NULL, chan, global_menerror); break; } } } if (!res) res = confcall_activate_zap_conference(*fd, conf->zapconf, user->zapflags); } if (!res && moh) ast_moh_start(chan, NULL); return res; } static int confcall_bridge(struct ast_conf_user *user) { int fd = 0, myfd = 0, nfds = 0, outfd = 0, ms = 0, res = 0, users = 0, markedusers = 0, framecount = 0, silence = 0, talking = 0, talkon = 0, silence_sample = 0, silence_hangover = 0, thang = 10, transmit = 1, talkcount = 0, silence_itt = 0; unsigned long rsilence = 0; short *fdata; struct ast_frame *read_frame = NULL; struct ast_channel *active_channel; int confcall_format = AST_FORMAT_SLINEAR; struct ast_channel *chan = user->chan; struct ast_conference *conf = user->conf; short null_data[1024]; int null_data_len = 0; memset(&null_data, 255, sizeof(null_data)); if (!ast_strlen_zero(conf->silence_sample)) { silence_sample = atoi(conf->silence_sample); } if (!ast_strlen_zero(conf->silence_factor)) { user->silence_factor = user->working_silence_factor = atoi(conf->silence_factor); if (strchr(conf->silence_params, 'a')) { ast_set_flag(user, CONFCALL_USER_FLAG_AUTOSILENCE); } else { ast_clear_flag(user, CONFCALL_USER_FLAG_AUTOSILENCE); } } if (!ast_strlen_zero(conf->silence_hangover)) { silence_hangover = atoi(conf->silence_hangover); } if (!ast_strlen_zero(conf->silence_ceiling)) { user->silence_ceiling = atoi(conf->silence_ceiling); } if (!conf || !chan || !ast_test_flag(conf, CONFCALL_FLAG_ACTIVE)) { ast_log(LOG_ERROR, "Invalid conference....\n"); return -1; } /* Set it into linear mode (write) */ if (ast_set_write_format(chan, confcall_format) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to write correct audio codec mode[%d]\n", chan->name, confcall_format); return -1; } /* Set it into linear mode (read) */ if (ast_set_read_format(chan, confcall_format) < 0) { ast_log(LOG_WARNING, "Unable to set '%s' to read correct audio codec mode[%d]\n", chan->name, confcall_format); return -1; } ast_indicate(chan, -1); if (strcasecmp(chan->type, "Zap")) { if (!(myfd = confcall_open_pseudo_fd())) { ast_log(LOG_ERROR, "Can't create pseudo channel...\n"); return -1; } fd = myfd; nfds = 1; } else { fd = chan->fds[0]; nfds = 0; } /* in case they are in any other conference, unlink it */ if (confcall_deactivate_zap_conference(fd)) { close(fd); fd = 0; return -1; } /* if (user->confflags & CONFFLAG_MONITOR) user->zapflags = ZT_CONF_CONFMON | ZT_CONF_LISTENER; else if (user->confflags & CONFFLAG_TALKER) user->zapflags = ZT_CONF_CONF | ZT_CONF_TALKER; else */ user->zapflags = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; if (confcall_activate_zap_conference(fd, conf->zapconf, user->zapflags)) { close(fd); fd = 0; return -1; } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "User %d (%s) Entering Conference %s (%d)\n", user->user_no, chan->name, conf->confno, conf->zapconf); manager_event(EVENT_FLAG_USER, "confcall_enter_conf", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num ); if (!(user->confflags & CONFFLAG_QUIET) && (user->confflags & CONFFLAG_INTROUSER) && conf->users > 1 && ast_fileexists(user->namerecloc, NULL, NULL) > 0) { confcall_playsound(conf, user->namerecloc, global_hasjoined, NULL); } else if (conf->users > 1 && !(user->confflags & CONFFLAG_QUIET) && !ast_strlen_zero(conf->enter_sound)) { confcall_playsound(conf, conf->enter_sound, NULL); } for (;;) { outfd = -1; ms = -1; ast_mutex_lock(&conf->lock); users = conf->users; markedusers = conf->markedusers; ast_mutex_unlock(&conf->lock); if (user->confflags & CONFFLAG_RECORD) { char *value = NULL; ast_set_flag(conf, CONFCALL_FLAG_RECORDING); if ((value=pbx_builtin_getvar_helper(chan, "MONITOR_EXEC"))) pbx_builtin_setvar_helper(conf->chan, "MONITOR_EXEC", value); if ((value=pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS"))) pbx_builtin_setvar_helper(conf->chan, "MONITOR_EXEC_ARGS", value); pbx_builtin_setvar_helper(chan, "RECORDED_CONFERENCE", conf->confno); confcall_monitor_conference(conf); } if (ast_test_flag(chan, AST_FLAG_MOH) && !chan->generatordata) { ast_moh_start(chan, NULL); } if (ast_test_flag(conf, CONFCALL_FLAG_ENDCONF)) { break; } /* Check if the admin changed my modes */ if (user->adminflags) { /* Set the new modes */ if (user->adminflags & ADMINFLAG_ADMIN_ME) { user->confflags |= CONFFLAG_ADMIN; confcall_stream_file(NULL, chan, global_admin); } if (user->adminflags & ADMINFLAG_UNADMIN_ME) { confcall_stream_file(NULL, chan, global_user); user->confflags &= ~CONFFLAG_ADMIN; } if (user->adminflags & ADMINFLAG_MARK_ME) { confcall_stream_file(NULL, chan, global_marked); user->confflags |= CONFFLAG_MARKED; } if (user->adminflags & ADMINFLAG_DTMF) { confcall_send_digits(user, user->dtmf); } if (user->adminflags & ADMINFLAG_UNMARK_ME) { confcall_stream_file(NULL, chan, global_unmarked); user->confflags &= ~CONFFLAG_MARKED; } if (user->adminflags & ADMINFLAG_KICK_ME) { /* You have been kicked. */ confcall_stream_file(NULL, chan, ast_strlen_zero(conf->kicked_sound) ? global_kicked : conf->kicked_sound); res = -1; break; } if ((user->adminflags & ADMINFLAG_MUTE_ME) && (user->zapflags & ZT_CONF_TALKER)) { user->zapflags = ZT_CONF_CONF | ZT_CONF_LISTENER; user->confflags |= CONFFLAG_MONITOR ^ CONFFLAG_TALKER; user->confflags |= CONFFLAG_MUTED; if (!res && (user->confflags & CONFFLAG_MOH_MUTED) && !ast_test_flag(chan, AST_FLAG_MOH)) { ast_moh_start(chan, NULL); } res = confcall_stream_file(NULL, chan, global_muted); manager_event(EVENT_FLAG_USER, "confcall_mute", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n" "Voluntary: No\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num ); } if ((user->adminflags & ADMINFLAG_UNMUTE_ME) && (!(user->zapflags & ZT_CONF_TALKER))) { user->zapflags = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER; user->confflags ^= CONFFLAG_MONITOR | CONFFLAG_TALKER; user->confflags &= ~CONFFLAG_MUTED; if ((user->confflags & CONFFLAG_MOH_MUTED) && ast_test_flag(chan, AST_FLAG_MOH)) ast_moh_stop(chan); res = confcall_stream_file(NULL, chan, global_unmuted); manager_event(EVENT_FLAG_USER, "confcall_unmute", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n" "Voluntary: No\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num ); } if ((res = confcall_activate_zap_conference(fd, conf->zapconf, user->zapflags))) break; user->adminflags = 0; } if ((active_channel = ast_waitfor_nandfds(&chan, 1, &fd, nfds, NULL, &outfd, &ms))) { if ((read_frame = ast_read(active_channel))) { if ((read_frame->frametype == AST_FRAME_DTMF)) { if (confcall_handle_dtmf(user, read_frame->subclass, &fd)) { break; } } else if (fd != chan->fds[0] && read_frame->frametype == AST_FRAME_VOICE) { int map = 0; if (read_frame->subclass == confcall_format) { fdata = (short *) read_frame->data; framecount++; transmit = 1; if (framecount == 1) { null_data_len = read_frame->datalen; } if (user->vol) { int count = 0; for(count=0; count < read_frame->datalen; count++) { if (user->vol > 0) { fdata[count] *= user->vol; } else if (user->vol < 0) { fdata[count] /= user->vol; } } } while(user->silence_factor && framecount > 1) { int count = 0, ismuted = 0; ismuted = (user->confflags & CONFFLAG_MUTED) ? 1 : 0; for(count=0; count < read_frame->datalen; count++) { map += abs(fdata[count]); } if (count) { map /= count; } if (framecount < silence_sample) { silence += map; break; } else if (framecount == silence_sample){ silence /= silence_sample; break; } if (framecount == 1000) { framecount = 0; silence = rsilence = user->silence; if (ast_test_flag(user, CONFCALL_USER_FLAG_AUTOSILENCE)) { silence_itt++; if (silence_itt > conf->silence_itt) { silence_itt = 0; ast_clear_flag(user, CONFCALL_USER_FLAG_AUTOSILENCE); } if (user->silence >= user->silence_factor && user->silence <= user->silence_ceiling) { user->working_silence_factor = user->silence; } } } else { rsilence += map; silence = rsilence / framecount; } talking = (map > (silence + user->working_silence_factor)) ? 1 : 0; if (thang > 0) { thang--; if (thang == 0) { talkon = 0; if (talkcount > silence_hangover) { if (!ismuted && conf->verbose) { ast_verbose(VERBOSE_PREFIX_2 "User %d %s (%s) Stop Talking in Conference %s\n", user->user_no, chan->cid.cid_name, chan->name, conf->confno); } if (!ismuted) { manager_event(EVENT_FLAG_USER, "confcall_stop_talking", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num ); } } talkcount = 0; } } if (talking) { thang = silence_hangover; if (talkcount < silence_hangover) { talkcount++; } if (talkon) { if (talkcount == silence_hangover) { talkcount++; if (!ismuted && conf->verbose) { ast_verbose(VERBOSE_PREFIX_2 "User %d %s (%s) Start Talking in Conference %s\n", user->user_no, chan->cid.cid_name, chan->name, conf->confno); } if (!ismuted) { manager_event(EVENT_FLAG_USER, "confcall_start_talking", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num ); } } } else { talkon = 1; } } if (ismuted) { transmit = 0; } else { transmit = talkon; } break; } if (transmit) { user->silence = silence; user->lastmap = map; careful_write(fd, read_frame->data, read_frame->datalen); } else { careful_write(fd, (void *)null_data, null_data_len); } } else { ast_log(LOG_WARNING, "Huh? Got a non-linear (%d) frame in the conference\n", read_frame->subclass); } } ast_frfree(read_frame); read_frame = NULL; } else { break; } } else if (outfd > -1) { res = confcall_copy_audio(chan, outfd, confcall_format); } if (users == 1 && !(user->confflags & CONFFLAG_ALONE)) { if (!(user->confflags & CONFFLAG_QUIET)) { confcall_playsound(conf, ast_strlen_zero(conf->alone_sound) ? global_alone_sound : conf->alone_sound, NULL); } user->confflags |= CONFFLAG_ALONE; if ((user->confflags & CONFFLAG_MOH) && !ast_test_flag(chan, AST_FLAG_MOH)) { ast_moh_start(chan, NULL); } } else if (users != 1 && (user->confflags & CONFFLAG_ALONE)) { user->confflags &= ~CONFFLAG_ALONE; if (!((user->confflags & CONFFLAG_MUTED) && (user->confflags & CONFFLAG_MOH_MUTED))) ast_moh_stop(chan); } if (markedusers == 0 && user->confflags & CONFFLAG_MARKEDEXIT) { res = -1; break; } } if (read_frame) { ast_frfree(read_frame); read_frame = NULL; } if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "User %d (%s) Departing Conference %s (%d)\n", user->user_no, chan->name, conf->confno, conf->zapconf); manager_event(EVENT_FLAG_USER, "confcall_exit_conf", "Conference: %s\r\n" "UserNumber: %d\r\n" "Channel: %s\r\n" "CallerIDName: %s\r\n" "CallerIDNum: %s\r\n", conf->confno, user->user_no, chan->name, chan->cid.cid_name, chan->cid.cid_num ); /* unhook the chan from this conference */ if (fd) { confcall_deactivate_zap_conference(fd); } if (myfd) { if (fd != myfd) { confcall_deactivate_zap_conference(myfd); } close(myfd); myfd = 0; } return res; } struct conf_thread_obj { struct ast_channel *chan; unsigned int confflags; struct ast_conference *conf; }; static void *confcall_join_conference_thread(void *ptr) { struct conf_thread_obj *obj = ptr; obj->chan->appl = app; obj->chan->data = obj->conf->confno; if (!obj->chan->cdr) { if ((obj->chan->cdr = ast_cdr_alloc())) { ast_cdr_init(obj->chan->cdr, obj->chan); ast_cdr_start(obj->chan->cdr); } } ast_answer(obj->chan); if (!ast_autoservice_stop(obj->chan)) { confcall_join(obj->chan, obj->conf, obj->confflags); //confcall_exec(obj->chan, obj->conf->confno); if (obj) { if (obj->chan) ast_hangup(obj->chan); free(obj); obj = NULL; } } return NULL; } static void confcall_join_conference(struct ast_channel *chan, struct ast_conference *conf, unsigned int confflags) { pthread_attr_t attr; int result = 0; pthread_t thread; struct conf_thread_obj *obj = NULL; if ((obj = (struct conf_thread_obj *) malloc(sizeof(struct conf_thread_obj)))) { memset(obj, 0, sizeof(struct conf_thread_obj)); obj->conf = conf; obj->chan = chan; obj->confflags = confflags; result = pthread_attr_init(&attr); pthread_attr_setschedpolicy(&attr, SCHED_RR); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); result = ast_pthread_create(&thread, &attr, confcall_join_conference_thread, obj); result = pthread_attr_destroy(&attr); } else { ast_log(LOG_WARNING, "Malloc Failed!\n"); } } static struct ast_conference *confcall_find_realtime_conf(char *confno) { /*TBD*/ struct ast_conference *conf = NULL; return conf; } static struct ast_conference *confcall_find_conf(char *confno) { struct ast_conference *conf = NULL; /* Check first in the conference list */ ast_mutex_lock(&conflock); conf = global_conference_list; while (conf) { if (!strcmp(confno, conf->confno)) break; conf = conf->next; } ast_mutex_unlock(&conflock); if (!conf) conf = confcall_find_realtime_conf(confno); return conf; } /*--- count_exec: The ConfcallCount application */ static int count_exec(struct ast_channel *chan, void *data) { struct localuser *u; int res = 0; struct ast_conference *conf; int count; char *confnum, *localdata; char val[80] = "0"; if (!data || ast_strlen_zero(data)) { ast_log(LOG_WARNING, "ConfCallCount requires an argument (conference number)\n"); return -1; } localdata = ast_strdupa(data); LOCAL_USER_ADD(u); confnum = strsep(&localdata, "|"); conf = confcall_find_conf(confnum); if (conf) { ast_mutex_lock(&conf->lock); count = conf->users; ast_mutex_unlock(&conf->lock); } else count = 0; if (localdata && !ast_strlen_zero(localdata)){ /* have var so load it and exit */ snprintf(val, sizeof(val), "%i", count); pbx_builtin_setvar_helper(chan, localdata, val); } else { if (chan->_state != AST_STATE_UP) ast_answer(chan); res = ast_say_number(chan, count, "", chan->language, (char *) NULL); /* Needs gender */ } LOCAL_USER_REMOVE(u); return res; } static int confcall_parse_flags(char *inflags) { int confflags = 0; if (inflags) { if (strchr(inflags, 'a')) confflags |= CONFFLAG_ADMIN; if (strchr(inflags, 'i')) confflags |= CONFFLAG_INTROUSER; if (strchr(inflags, 'm')) confflags |= CONFFLAG_MONITOR; if (strchr(inflags, 'p')) confflags |= CONFFLAG_POUNDEXIT; if (strchr(inflags, 's')) confflags |= CONFFLAG_STARMENU; if (strchr(inflags, 't')) confflags |= CONFFLAG_TALKER; if (strchr(inflags, 'q')) confflags |= CONFFLAG_QUIET; if (strchr(inflags, 'M')) confflags |= CONFFLAG_MOH; if (strchr(inflags, 'u')) confflags |= CONFFLAG_MOH_MUTED; if (strchr(inflags, 'r')) confflags |= CONFFLAG_RECORD; if (strchr(inflags, 'x')) confflags |= CONFFLAG_MARKEDEXIT; if (strchr(inflags, 'X')) confflags |= CONFFLAG_EXIT_CONTEXT; if (strchr(inflags, 'A')) confflags |= CONFFLAG_MARKED; if (strchr(inflags, 'P')) confflags |= CONFFLAG_NOPIN; if (strchr(inflags, 'b')) confflags |= CONFFLAG_AGI; if (strchr(inflags, 'w')) confflags |= CONFFLAG_WAITMARKED; if (strchr(inflags, 'd')) confflags |= CONFFLAG_DYNAMIC; if (strchr(inflags, '1')) confflags |= CONFFLAG_ONE; } return confflags; } static void confcall_uninstall_user(struct ast_conf_user *user) { struct ast_conf_user *uptr = NULL, *lastuser = NULL; struct ast_conference *conf = user->conf; int x = 1; ast_mutex_lock(&conf->lock); for(uptr = conf->firstuser ; uptr ; uptr = uptr->nextuser) { if (uptr == user) { if (uptr == conf->firstuser) { conf->firstuser = uptr->nextuser; } else if (lastuser){ lastuser->nextuser = uptr->nextuser; } } else { uptr->user_no = x++; conf->lastuser = uptr; } lastuser = uptr; } if (conf->users == 2 && ((user->confflags & CONFFLAG_ONE) || ast_test_flag(conf, CONFCALL_FLAG_ONE))) { ast_set_flag(conf, CONFCALL_FLAG_ENDCONF); } conf->users--; if (user->confflags & CONFFLAG_MARKED) conf->markedusers--; if (!ast_strlen_zero(user->namerecloc)) { ast_filedelete(user->namerecloc, NULL); } ast_mutex_unlock(&conf->lock); } static void confcall_install_user(struct ast_conference *conf, struct ast_channel *chan, int confflags, struct ast_conf_user *user) { struct ast_conf_user *uptr = NULL; ast_mutex_lock(&conf->lock); memset(user, 0, sizeof(struct ast_conf_user)); if (conf->firstuser) { user->user_no++; for(uptr = conf->firstuser ; uptr && uptr->nextuser; uptr = uptr->nextuser) { user->user_no++; } uptr->nextuser = user; } else { conf->firstuser = user; } user->user_no++; conf->lastuser = user; user->chan = chan; user->confflags = confflags; user->adminflags = 0; if (user->confflags & CONFFLAG_INTROUSER) snprintf(user->namerecloc,sizeof(user->namerecloc),"/var/tmp/confcall-username-%s-%s",conf->confno,user->chan->uniqueid); time(&user->jointime); conf->users++; if (user->confflags & CONFFLAG_MARKED) conf->markedusers++; user->conf = conf; user->confflags = confflags; if (user->confflags & CONFFLAG_MONITOR) { user->adminflags |= ADMINFLAG_MUTE_ME; } ast_mutex_unlock(&conf->lock); } static int confcall_join(struct ast_channel *chan, struct ast_conference *conf, int confflags) { int res = 0, duration = 20; struct ast_conf_user user; if (!(confflags & CONFFLAG_ADMIN)) { /* we dont need to see no stinking badges! */ if (ast_test_flag(conf, CONFCALL_FLAG_LOCKED)) { /* Sorry, but this confernce is locked! */ confcall_stream_file(NULL, chan, global_locked_warning); return -1; } while(!(confflags & CONFFLAG_MARKED) && (confflags & CONFFLAG_WAITMARKED) && (conf->markedusers == 0)) { /* XXX Announce that we're waiting on the conference lead to join */ res = confcall_stream_file(NULL, chan, global_wait_to_join); if (!res) { if ((confflags & CONFFLAG_MOH)) ast_moh_start(chan, NULL); res = ast_safe_sleep_conditional(chan, 60000, confnonzero, conf); if ((confflags & CONFFLAG_MOH)) ast_moh_stop(chan); } if (res < 0) { return -1; } } } if (!res && !ast_test_flag(conf, CONFCALL_FLAG_ACTIVE)) { res = confcall_activate_conference(conf); } else { res = 0; } if (!res) { confcall_install_user(conf, chan, confflags, &user); /* if a tree falls in a conf and nobody is around to hear it does it make a sound.*/ if ((user.confflags & CONFFLAG_INTROUSER)) { res = ast_record_review(chan, "vm-rec-name", user.namerecloc, 10, "sln", &duration, NULL); } if (!res) { /* join the audio bridge */ confcall_bridge(&user); if (!res && !(user.confflags & CONFFLAG_QUIET) && (user.confflags & CONFFLAG_INTROUSER) && conf->users > 1 && ast_fileexists(user.namerecloc, NULL, NULL) > 0) { confcall_playsound(conf, user.namerecloc, global_hasleft, NULL); } else if (conf->users > 1 && !(user.confflags & CONFFLAG_QUIET) && !ast_strlen_zero(conf->enter_sound)) { confcall_playsound(conf, conf->exit_sound, NULL); } if (chan) { char meetmesecs[32]; snprintf(meetmesecs, sizeof(meetmesecs), "%i", (int) (time(NULL) - user.jointime)); pbx_builtin_setvar_helper(chan, "MEETMESECS", meetmesecs); } } confcall_uninstall_user(&user); if (ast_test_flag(conf, CONFCALL_FLAG_ACTIVE) && conf->users < 1) confcall_deactivate_conference(conf); } else ast_log(LOG_WARNING, "Cannot activate conference %s\n", conf->confno); return res; } /*--- confcall_exec: The confcall() application */ static int confcall_exec(struct ast_channel *chan, void *data) { struct localuser *u = NULL; struct ast_conference *conf = NULL; int res = 0, confflags = 0, argc = 0, x = 0, y = 0; char *confno = NULL, *inflags = NULL, *mydata = NULL, *argv[3]; char buf[CONFCALL_STRLEN]; if (data && !ast_strlen_zero(data)) { mydata = ast_strdupa((char *)data); argc = ast_app_separate_args(mydata, '|', argv, sizeof(argv) / sizeof(argv[0])); confno = argv[0]; inflags = argv[1]; } LOCAL_USER_ADD(u); if (chan->_state != AST_STATE_UP) ast_answer(chan); if (inflags) confflags = confcall_parse_flags(inflags); for(y = 3; y > 0 ; y--) { if (!confno || ast_strlen_zero(confno)) { res = ast_app_getdata(chan, "conf-getconfno", buf, sizeof(buf) - 1, 0); confno = buf; } if (confno && !ast_strlen_zero(confno)) { conf = confcall_find_conf(confno); if (!conf && (confflags & CONFFLAG_DYNAMIC)) { if ((conf = confcall_new_conf(confno))) { ast_set_flag(conf, CONFCALL_FLAG_DYNAMIC); ast_mutex_lock(&conflock); if (global_conference_list) { conf->next = global_conference_list; } global_conference_list = conf; ast_mutex_unlock(&conflock); confcall_parse_options(conf, NULL); } } } if (conf) { if (!ast_strlen_zero(conf->options)) /* inherit default options from the config */ confflags |= confcall_parse_flags(conf->options); if (!ast_strlen_zero(conf->pin) && !(confflags & CONFFLAG_NOPIN)) { res = 0; for(x = 3; x > 0 ; x--) { char pin[CONFCALL_STRLEN]; if (ast_app_getdata(chan, "conf-getpin", pin, CONFCALL_STRLEN - 1, 0)) break; if (!strcmp(pin, conf->pin)) { res = 1; break; } else if (!strcmp(pin, conf->admin_pin)) { res = 1; confflags |= CONFFLAG_ADMIN; break; } else { if (confcall_stream_file(NULL, chan, "conf-invalid")) break; confno = NULL; } } } else { res = 1; } if (res) { res = confcall_join(chan, conf, confflags); break; } } else { ast_log(LOG_WARNING, "Cannot locate conference %s\n", confno); res = confcall_stream_file(NULL, chan, "conf-invalid"); confno = NULL; } } LOCAL_USER_REMOVE(u); return (chan && !ast_check_hangup(chan)) ? 0 : -1; } static struct ast_conf_user *confcall_find_user_int(struct ast_conference *conf, int callerident) { struct ast_conf_user *user = NULL; if (conf && callerident) { ast_mutex_lock(&conf->lock); for(user = conf->firstuser; user; user = user->nextuser) { if (user->user_no == callerident) break; } ast_mutex_unlock(&conf->lock); } return user; } static struct ast_conf_user *confcall_find_user(struct ast_conference *conf, char *callerident) { struct ast_conf_user *user = NULL; if (callerident) { ast_mutex_lock(&conf->lock); if (!strcmp(callerident, "first")) user = conf->firstuser; if (!strcmp(callerident, "last")) user = conf->lastuser; ast_mutex_unlock(&conf->lock); } return user ? user : confcall_find_user_int(conf, atoi(callerident)); } static int confcall_adminflag_apply_user(int fd, struct ast_conf_user *uptr, unsigned int newflags, unsigned int exception_flags) { int res = -1; char msg[CONFCALL_STRLEN]; if (!uptr) return res; if (!(uptr->confflags & exception_flags)) { if (newflags & ADMINFLAG_MUTE_ME) { if ((uptr->zapflags & ZT_CONF_TALKER)) { uptr->adminflags |= newflags; res = 0; } else { res = -1; } } if (newflags & ADMINFLAG_UNMUTE_ME) { if (!(uptr->zapflags & ZT_CONF_TALKER)) { uptr->adminflags |= newflags; res = 0; } else { res = -1; } } if ((newflags & ADMINFLAG_KICK_ME)) { uptr->adminflags |= newflags; res = 0; } if ((newflags & ADMINFLAG_MARK_ME)) { uptr->adminflags |= newflags; res = 0; } if ((newflags & ADMINFLAG_UNMARK_ME)) { uptr->adminflags |= newflags; res = 0; } if ((newflags & ADMINFLAG_ADMIN_ME)) { uptr->adminflags |= newflags; res = 0; } if ((newflags & ADMINFLAG_UNADMIN_ME)) { uptr->adminflags |= newflags; res = 0; } if ((newflags & ADMINFLAG_DTMF)) { uptr->adminflags |= newflags; res = 0; } } else { snprintf(msg, CONFCALL_STRLEN, "user %d is immune to the requested change!\n",uptr->user_no); if (fd) ast_cli(fd,msg); else ast_log(LOG_NOTICE,"%s",msg); } return res; } static int confcall_adminflag_apply_all(int fd, struct ast_conference *conf, unsigned int newflags, unsigned int exception_flags) { int x = 0; struct ast_conf_user *uptr = NULL; ast_mutex_lock(&conf->lock); for(uptr = conf->firstuser; uptr; uptr = uptr->nextuser) { if (!confcall_adminflag_apply_user(fd, uptr, newflags, exception_flags)) x++; } ast_mutex_unlock(&conf->lock); return x; } /*--- confcall_admin_exec: The ConfCalladmin application */ /* ConfCallAdmin(confno, command, caller) */ static int confcall_admin_exec(struct ast_channel *chan, void *data) { char *params, *command = NULL, *caller = NULL, *confno = NULL; struct ast_conference *conf; struct ast_conf_user *user = NULL; ast_mutex_lock(&conflock); /* The param has the conference number the user and the command to execute */ if (data && !ast_strlen_zero(data)) { params = ast_strdupa((char *) data); confno = strsep(¶ms, "|"); command = strsep(¶ms, "|"); caller = strsep(¶ms, "|"); if (!command) { ast_log(LOG_WARNING, "ConfcallAdmin requires a command!\n"); ast_mutex_unlock(&conflock); return -1; } conf = global_conference_list; while (conf) { if (strcmp(conf->confno, confno) == 0) break; conf = conf->next; } if (caller) user = confcall_find_user(conf, caller); if (conf) { switch((int) (*command)) { case 76: /* L: Lock */ ast_set_flag(conf, CONFCALL_FLAG_LOCKED); break; case 108: /* l: Unlock */ ast_clear_flag(conf, CONFCALL_FLAG_LOCKED); break; case 75: /* K: kick all users*/ user = conf->firstuser; while(user) { user->adminflags |= ADMINFLAG_KICK_ME; if (user->nextuser) { user = user->nextuser; } else { break; } } break; case 101: /* e: Eject last user*/ user = conf->lastuser; if (!(user->confflags & CONFFLAG_ADMIN)) { user->adminflags |= ADMINFLAG_KICK_ME; break; } else ast_log(LOG_NOTICE, "Not kicking last user, is an Admin!\n"); break; case 77: /* M: Mute */ if (user) { user->adminflags |= ADMINFLAG_MUTE_ME; } else { ast_log(LOG_NOTICE, "Specified User not found!\n"); } break; case 78: /* N: Mute all users */ user = conf->firstuser; while(user) { if (user && !(user->confflags & CONFFLAG_ADMIN)) user->adminflags |= ADMINFLAG_MUTE_ME; if (user->nextuser) { user = user->nextuser; } else { break; } } break; case 109: /* m: Unmute */ if (user && (user->adminflags & ADMINFLAG_MUTE_ME)) { user->adminflags ^= ADMINFLAG_MUTE_ME; } else { ast_log(LOG_NOTICE, "Specified User not found or he muted himself!\n"); } break; case 110: /* n: Unmute all users */ user = conf->firstuser; while(user) { if (user && (user-> adminflags & ADMINFLAG_MUTE_ME)) { user->adminflags ^= ADMINFLAG_MUTE_ME; } if (user->nextuser) { user = user->nextuser; } else { break; } } break; case 107: /* k: Kick user */ if (user) { user->adminflags |= ADMINFLAG_KICK_ME; } else { ast_log(LOG_NOTICE, "Specified User not found!\n"); } break; } } else { ast_log(LOG_NOTICE, "Conference Number not found\n"); } } ast_mutex_unlock(&conflock); return 0; } static int confcall_load_config(int reload) { struct ast_config *cfg = NULL; char *entry = NULL, *confno = NULL; struct ast_conference *conf = NULL, *cptr = NULL, *clast = NULL; /* clear defaults if there are any.*/ if ((conf = confcall_find_conf("default"))) { entry = (char *) conf + CONFCALL_STRLEN; memset(entry, 0, CONFCALL_STRLEN * CONFCALL_ARGS); conf = NULL; entry = NULL; } if ((cfg = ast_config_load(global_conf_file))) { ast_mutex_lock(&conflock); for(cptr = global_conference_list; cptr ; cptr = cptr->next) ast_set_flag(cptr, CONFCALL_FLAG_PRUNE); for (entry = ast_category_browse(cfg, NULL); entry != NULL; entry = ast_category_browse(cfg, entry)) { confno = entry; for(cptr = global_conference_list; cptr ; cptr = cptr->next) { if (!strcmp(cptr->confno, confno)) { ast_clear_flag(cptr, CONFCALL_FLAG_PRUNE); break; } clast = cptr; } if (cptr) { conf = cptr; } else { if ((conf = confcall_new_conf(confno))) { if (clast) clast->next = conf; else global_conference_list = conf; } else { ast_log(LOG_ERROR, "Error Creating Conference!\n"); } } if (conf) { confcall_parse_options(conf, ast_variable_browse(cfg, entry)); } } confcall_prune(NULL); ast_mutex_unlock(&conflock); ast_config_destroy(cfg); } return 0; } int reload() { return confcall_load_config(1); } int unload_module(void) { struct ast_conference *cptr = NULL, *ctmp = NULL; STANDARD_HANGUP_LOCALUSERS; ast_mutex_lock(&conflock); cptr = global_conference_list; while(cptr) { if (ast_test_flag(cptr, CONFCALL_FLAG_ACTIVE)) { /*doh this shouldn't be possible!*/ confcall_adminflag_apply_all(0, cptr, ADMINFLAG_KICK_ME, 0); confcall_deactivate_conference(cptr); ast_log(LOG_WARNING,"Unloading while confs are in use, oh oh!\n"); } ctmp = cptr; cptr = cptr->next; ast_mutex_destroy(&ctmp->lock); ast_mutex_destroy(&ctmp->soundlock); free(ctmp); ctmp = NULL; } ast_mutex_unlock(&conflock); ast_cli_unregister(&cli_confcall); ast_unregister_application(app3); ast_unregister_application(app2); return ast_unregister_application(app); } int load_module(void) { confcall_load_config(0); global_monitor_app = pbx_findapp("Monitor"); ast_cli_register(&cli_confcall); ast_register_application(app3, confcall_admin_exec, synopsis3, descrip3); ast_register_application(app2, count_exec, synopsis2, descrip2); return ast_register_application(app, confcall_exec, synopsis, descrip); } char *description(void) { return tdesc; } int usecount(void) { int res; STANDARD_USECOUNT(res); return res; } #define JIBBERISH_BS_KEY \ "Uijt!qbsbhsbqi!jt!Dpqzsj"\ "hiu!)D*!3111-!Mjovy!Tvqqpsu!"\ "Tfswjdft-!Jod/!!Jo!psefs!gps!zp"\ "vs!npevmf!up!mpbe-!ju!nvtu!sfuv"\ "so!uijt!lfz!wjb!b!gvodujpo!dbmmfe!#lfz#/!!Bo"\ "z!dpef!xijdi!jodmveft!uijt!qbsbhsbqi!nvtu!cf"\ "!mjdfotfe!voefs!uif!HOV!Hfofsbm!Qv"\ "cmjd!Mjdfotf!wfstjpo!3!ps!mbufs!)b"\ "u!zpvs!pqujpo*/!!!Mjovy!Tvqqps"\ "u!Tfswjdft-!Jod/!sftfswft!ui"\ "f!sjhiu!up!bmmpx!puifs!qbsu"\ "jft!up!mjdfotf!uijt!qbsbhsbq"\ "i!voefs!puifs!ufsnt!bt!xfmm/" static char ridiciulous_text[412] = ""; char *key() { int x; /* This is ridiciulous IF **IT'S FREE WHY DO YOU NEED A KEY? Let's compute thier BS value on the fly so we don't have to store it in our binary. */ for(x = 0; x < strlen(JIBBERISH_BS_KEY); x++) { ridiciulous_text[x] = JIBBERISH_BS_KEY[x] - 1; } return ridiciulous_text; }