static void destroy_callback(void *data) { struct volume_information *vi = data; /* Destroy the audiohook, and destroy ourselves */ ast_audiohook_lock(&vi->audiohook); ast_audiohook_detach(&vi->audiohook); ast_audiohook_unlock(&vi->audiohook); ast_audiohook_destroy(&vi->audiohook); ast_free(vi); return; }
static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor) { if (mixmonitor->mixmonitor_ds) { ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); mixmonitor->mixmonitor_ds->audiohook = NULL; ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); } /* kill the audiohook.*/ ast_audiohook_lock(&mixmonitor->audiohook); ast_audiohook_detach(&mixmonitor->audiohook); ast_audiohook_unlock(&mixmonitor->audiohook); ast_audiohook_destroy(&mixmonitor->audiohook); }
static void hook_datastore_destroy_callback(void *data) { struct hook_state *state = data; ast_audiohook_lock(&state->audiohook); ast_audiohook_detach(&state->audiohook); ast_audiohook_unlock(&state->audiohook); ast_audiohook_destroy(&state->audiohook); ast_free(state->context); ast_free(state->exten); ast_free(state); ast_module_unref(ast_module_info->self); }
/*! \brief Callback function for hanging up a Snoop channel */ static int snoop_hangup(struct ast_channel *chan) { struct stasis_app_snoop *snoop = ast_channel_tech_pvt(chan); if (snoop->spy_active) { ast_audiohook_lock(&snoop->spy); ast_audiohook_detach(&snoop->spy); ast_audiohook_unlock(&snoop->spy); } if (snoop->whisper_active) { ast_audiohook_lock(&snoop->whisper); ast_audiohook_detach(&snoop->whisper); ast_audiohook_unlock(&snoop->whisper); } publish_chanspy_message(snoop, 0); ao2_cleanup(snoop); ast_channel_tech_pvt_set(chan, NULL); return 0; }
static int channel_spy(struct ast_channel *chan, struct ast_autochan *spyee_autochan, int *volfactor, int fd, struct spy_dtmf_options *user_options, struct ast_flags *flags, char *exitcontext) { struct chanspy_translation_helper csth; int running = 0, res, x = 0; char inp[24] = {0}; char *name; struct ast_frame *f; struct ast_silence_generator *silgen = NULL; struct ast_autochan *spyee_bridge_autochan = NULL; const char *spyer_name; struct ast_channel *chans[] = { chan, spyee_autochan->chan }; ast_channel_lock(chan); spyer_name = ast_strdupa(chan->name); ast_channel_unlock(chan); /* We now hold the channel lock on spyee */ if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan)) { return 0; } ast_channel_lock(spyee_autochan->chan); name = ast_strdupa(spyee_autochan->chan->name); ast_channel_unlock(spyee_autochan->chan); ast_verb(2, "Spying on channel %s\n", name); ast_manager_event_multichan(EVENT_FLAG_CALL, "ChanSpyStart", 2, chans, "SpyerChannel: %s\r\n" "SpyeeChannel: %s\r\n", spyer_name, name); memset(&csth, 0, sizeof(csth)); ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL); ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) { ast_audiohook_destroy(&csth.spy_audiohook); return 0; } ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy"); if (start_spying(spyee_autochan, spyer_name, &csth.whisper_audiohook)) { ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", name); } if ((spyee_bridge_autochan = ast_autochan_setup(ast_bridged_channel(spyee_autochan->chan)))) { ast_channel_lock(spyee_bridge_autochan->chan); if (start_spying(spyee_bridge_autochan, spyer_name, &csth.bridge_whisper_audiohook)) { ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", name); } ast_channel_unlock(spyee_bridge_autochan->chan); } ast_channel_lock(chan); ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY); ast_channel_unlock(chan); csth.volfactor = *volfactor; if (csth.volfactor) { csth.spy_audiohook.options.read_volume = csth.volfactor; csth.spy_audiohook.options.write_volume = csth.volfactor; } csth.fd = fd; if (ast_test_flag(flags, OPTION_PRIVATE)) silgen = ast_channel_start_silence_generator(chan); else ast_activate_generator(chan, &spygen, &csth); /* We can no longer rely on 'spyee' being an actual channel; it can be hung up and freed out from under us. However, the channel destructor will put NULL into our csth.spy.chan field when that happens, so that is our signal that the spyee channel has gone away. */ /* Note: it is very important that the ast_waitfor() be the first condition in this expression, so that if we wait for some period of time before receiving a frame from our spying channel, we check for hangup on the spied-on channel _after_ knowing that a frame has arrived, since the spied-on channel could have gone away while we were waiting */ while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { running = -1; break; } if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) { ast_audiohook_lock(&csth.whisper_audiohook); ast_audiohook_lock(&csth.bridge_whisper_audiohook); ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); ast_audiohook_unlock(&csth.whisper_audiohook); ast_audiohook_unlock(&csth.bridge_whisper_audiohook); ast_frfree(f); continue; } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) { ast_audiohook_lock(&csth.whisper_audiohook); ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); ast_audiohook_unlock(&csth.whisper_audiohook); ast_frfree(f); continue; } res = (f->frametype == AST_FRAME_DTMF) ? f->subclass.integer : 0; ast_frfree(f); if (!res) continue; if (x == sizeof(inp)) x = 0; if (res < 0) { running = -1; break; } if (ast_test_flag(flags, OPTION_EXIT)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) { ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext); pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name); running = -2; break; } else { ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } } else if (res >= '0' && res <= '9') { if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) { change_spy_mode(res, flags); } else { inp[x++] = res; } } if (res == user_options->cycle) { running = 0; break; } else if (res == user_options->exit) { running = -2; break; } else if (res == user_options->volume) { if (!ast_strlen_zero(inp)) { running = atoi(inp); break; } (*volfactor)++; if (*volfactor > 4) *volfactor = -4; ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor); csth.volfactor = *volfactor; csth.spy_audiohook.options.read_volume = csth.volfactor; csth.spy_audiohook.options.write_volume = csth.volfactor; } } if (ast_test_flag(flags, OPTION_PRIVATE)) ast_channel_stop_silence_generator(chan, silgen); else ast_deactivate_generator(chan); ast_channel_lock(chan); ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); ast_channel_unlock(chan); ast_audiohook_lock(&csth.whisper_audiohook); ast_audiohook_detach(&csth.whisper_audiohook); ast_audiohook_unlock(&csth.whisper_audiohook); ast_audiohook_destroy(&csth.whisper_audiohook); ast_audiohook_lock(&csth.bridge_whisper_audiohook); ast_audiohook_detach(&csth.bridge_whisper_audiohook); ast_audiohook_unlock(&csth.bridge_whisper_audiohook); ast_audiohook_destroy(&csth.bridge_whisper_audiohook); ast_audiohook_lock(&csth.spy_audiohook); ast_audiohook_detach(&csth.spy_audiohook); ast_audiohook_unlock(&csth.spy_audiohook); ast_audiohook_destroy(&csth.spy_audiohook); if (spyee_bridge_autochan) { ast_autochan_destroy(spyee_bridge_autochan); } ast_verb(2, "Done Spying on channel %s\n", name); ast_manager_event(chan, EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name); return running; }
static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) { struct ast_datastore *datastore = NULL; struct speex_info *si = NULL; struct speex_direction_info **sdi = NULL; int is_new = 0; ast_channel_lock(chan); if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) { ast_channel_unlock(chan); if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) { return 0; } if (!(si = ast_calloc(1, sizeof(*si)))) { ast_datastore_free(datastore); return 0; } ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); si->audiohook.manipulate_callback = speex_callback; si->lastrate = 8000; is_new = 1; } else { ast_channel_unlock(chan); si = datastore->data; } if (!strcasecmp(data, "rx")) { sdi = &si->rx; } else if (!strcasecmp(data, "tx")) { sdi = &si->tx; } else { ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd); if (is_new) { ast_datastore_free(datastore); return -1; } } if (!*sdi) { if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) { return 0; } /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear * audio. When it supports 16 kHz (or any other sample rates, we will * have to take that into account here. */ (*sdi)->samples = -1; } if (!strcasecmp(cmd, "agc")) { if (!sscanf(value, "%30f", &(*sdi)->agclevel)) (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0; if ((*sdi)->agclevel > 32768.0) { ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n", ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel); (*sdi)->agclevel = 32768.0; } (*sdi)->agc = !!((*sdi)->agclevel); if ((*sdi)->state) { speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc); if ((*sdi)->agc) { speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel); } } } else if (!strcasecmp(cmd, "denoise")) { (*sdi)->denoise = (ast_true(value) != 0); if ((*sdi)->state) { speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise); } } if (!(*sdi)->agc && !(*sdi)->denoise) { if ((*sdi)->state) speex_preprocess_state_destroy((*sdi)->state); ast_free(*sdi); *sdi = NULL; } if (!si->rx && !si->tx) { if (is_new) { is_new = 0; } else { ast_channel_lock(chan); ast_channel_datastore_remove(chan, datastore); ast_channel_unlock(chan); ast_audiohook_remove(chan, &si->audiohook); ast_audiohook_detach(&si->audiohook); } ast_datastore_free(datastore); } if (is_new) { datastore->data = si; ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_channel_unlock(chan); ast_audiohook_attach(chan, &si->audiohook); } return 0; }
static void *mixmonitor_thread(void *obj) { struct mixmonitor *mixmonitor = obj; struct ast_filestream *fs = NULL; unsigned int oflags; char *ext; int errflag = 0; if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name); ast_audiohook_lock(&mixmonitor->audiohook); while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { struct ast_frame *fr = NULL; ast_audiohook_trigger_wait(&mixmonitor->audiohook); if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) break; if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) continue; ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) { ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); /* Initialize the file if not already done so */ if (!fs && !errflag) { oflags = O_CREAT | O_WRONLY; oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; if ((ext = strrchr(mixmonitor->filename, '.'))) *(ext++) = '\0'; else ext = "raw"; if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) { ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); errflag = 1; } } /* Write out the frame */ if (fs) ast_writestream(fs, fr); } else { ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); } /* All done! free it. */ ast_frame_free(fr, 0); } ast_audiohook_detach(&mixmonitor->audiohook); ast_audiohook_unlock(&mixmonitor->audiohook); ast_audiohook_destroy(&mixmonitor->audiohook); if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); if (fs) ast_closestream(fs); if (mixmonitor->post_process) { if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); ast_safe_system(mixmonitor->post_process); } ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); if (!mixmonitor->mixmonitor_ds->destruction_ok) { ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock); } ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock); ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition); ast_free(mixmonitor->mixmonitor_ds); free(mixmonitor); return NULL; }
static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds, int *volfactor, int fd, const struct ast_flags *flags) { struct chanspy_translation_helper csth; int running = 0, res, x = 0; char inp[24] = {0}; char *name; struct ast_frame *f; struct ast_silence_generator *silgen = NULL; struct ast_channel *spyee = NULL; const char *spyer_name; ast_channel_lock(chan); spyer_name = ast_strdupa(chan->name); ast_channel_unlock(chan); ast_mutex_lock(&spyee_chanspy_ds->lock); while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) { /* avoid a deadlock here, just in case spyee is masqueraded and * chanspy_ds_chan_fixup() is called with the channel locked */ DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock); } ast_mutex_unlock(&spyee_chanspy_ds->lock); if (!spyee) return 0; /* We now hold the channel lock on spyee */ if (ast_check_hangup(chan) || ast_check_hangup(spyee)) { ast_channel_unlock(spyee); return 0; } name = ast_strdupa(spyee->name); if (option_verbose >= 2) ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name); memset(&csth, 0, sizeof(csth)); ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy"); if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) { ast_audiohook_destroy(&csth.spy_audiohook); ast_channel_unlock(spyee); return 0; } if (ast_test_flag(flags, OPTION_WHISPER)) { ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy"); start_spying(spyee, spyer_name, &csth.whisper_audiohook); } ast_channel_unlock(spyee); spyee = NULL; csth.volfactor = *volfactor; if (csth.volfactor) { csth.spy_audiohook.options.read_volume = csth.volfactor; csth.spy_audiohook.options.write_volume = csth.volfactor; } csth.fd = fd; if (ast_test_flag(flags, OPTION_PRIVATE)) silgen = ast_channel_start_silence_generator(chan); else ast_activate_generator(chan, &spygen, &csth); /* We can no longer rely on 'spyee' being an actual channel; it can be hung up and freed out from under us. However, the channel destructor will put NULL into our csth.spy.chan field when that happens, so that is our signal that the spyee channel has gone away. */ /* Note: it is very important that the ast_waitfor() be the first condition in this expression, so that if we wait for some period of time before receiving a frame from our spying channel, we check for hangup on the spied-on channel _after_ knowing that a frame has arrived, since the spied-on channel could have gone away while we were waiting */ while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { if (!(f = ast_read(chan)) || ast_check_hangup(chan)) { running = -1; break; } if (ast_test_flag(flags, OPTION_WHISPER) && (f->frametype == AST_FRAME_VOICE)) { ast_audiohook_lock(&csth.whisper_audiohook); ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f); ast_audiohook_unlock(&csth.whisper_audiohook); ast_frfree(f); continue; } res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0; ast_frfree(f); if (!res) continue; if (x == sizeof(inp)) x = 0; if (res < 0) { running = -1; break; } if (res == '*') { running = 0; break; } else if (res == '#') { if (!ast_strlen_zero(inp)) { running = atoi(inp); break; } (*volfactor)++; if (*volfactor > 4) *volfactor = -4; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor); csth.volfactor = *volfactor; csth.spy_audiohook.options.read_volume = csth.volfactor; csth.spy_audiohook.options.write_volume = csth.volfactor; } else if (res >= '0' && res <= '9') { inp[x++] = res; } } if (ast_test_flag(flags, OPTION_PRIVATE)) ast_channel_stop_silence_generator(chan, silgen); else ast_deactivate_generator(chan); if (ast_test_flag(flags, OPTION_WHISPER)) { ast_audiohook_lock(&csth.whisper_audiohook); ast_audiohook_detach(&csth.whisper_audiohook); ast_audiohook_unlock(&csth.whisper_audiohook); ast_audiohook_destroy(&csth.whisper_audiohook); } ast_audiohook_lock(&csth.spy_audiohook); ast_audiohook_detach(&csth.spy_audiohook); ast_audiohook_unlock(&csth.spy_audiohook); ast_audiohook_destroy(&csth.spy_audiohook); if (option_verbose >= 2) ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); return running; }