static int attach_barge(struct ast_autochan *spyee_autochan, struct ast_autochan **spyee_bridge_autochan, struct ast_audiohook *bridge_whisper_audiohook, const char *spyer_name, const char *name) { int retval = 0; struct ast_autochan *internal_bridge_autochan; struct ast_channel *bridged = ast_bridged_channel(spyee_autochan->chan); if (!bridged) { return -1; } ast_audiohook_init(bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0); internal_bridge_autochan = ast_autochan_setup(bridged); if (!internal_bridge_autochan) { return -1; } ast_channel_lock(internal_bridge_autochan->chan); if (start_spying(internal_bridge_autochan, spyer_name, bridge_whisper_audiohook)) { ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee '%s'. Barge mode disabled.\n", name); retval = -1; } ast_channel_unlock(internal_bridge_autochan->chan); *spyee_bridge_autochan = internal_bridge_autochan; return retval; }
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 channel_spy(struct ast_channel *chan, struct ast_channel *spyee, int *volfactor, int fd) { struct chanspy_translation_helper csth; int running = 0, res = 0, x = 0; char inp[24] = "", *name = NULL; struct ast_frame *f = NULL; if ((chan && ast_check_hangup(chan)) || (spyee && ast_check_hangup(spyee))) return 0; name = ast_strdupa(spyee->name); if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Spying on channel %s\n", name); memset(&csth, 0, sizeof(csth)); ast_set_flag(&csth.spy, CHANSPY_FORMAT_AUDIO); ast_set_flag(&csth.spy, CHANSPY_TRIGGER_NONE); ast_set_flag(&csth.spy, CHANSPY_MIXAUDIO); csth.spy.type = chanspy_spy_type; csth.spy.status = CHANSPY_RUNNING; csth.spy.read_queue.format = AST_FORMAT_SLINEAR; csth.spy.write_queue.format = AST_FORMAT_SLINEAR; ast_mutex_init(&csth.spy.lock); csth.volfactor = *volfactor; set_volume(chan, &csth); if (csth.volfactor) { ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST); csth.spy.read_vol_adjustment = csth.volfactor; ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST); csth.spy.write_vol_adjustment = csth.volfactor; } csth.fd = fd; if (start_spying(spyee, chan, &csth.spy)) { ast_channel_spy_free(&csth.spy); return 0; } ast_activate_generator(chan, &spygen, &csth); while (csth.spy.status == CHANSPY_RUNNING && (res = ast_waitfor(chan, -1) > -1)) { /* Read in frame from channel, break out if no frame */ if (!(f = ast_read(chan))) break; /* Now if this is DTMF then we have to handle it as such, otherwise just skip it */ res = 0; if (f->frametype == AST_FRAME_DTMF) res = f->subclass; ast_frfree(f); if (!res) continue; if (x == sizeof(inp)) x = 0; if (res < 0) { running = -1; break; } /* Process DTMF digits */ if (res == '#') { if (!ast_strlen_zero(inp)) { running = x ? atoi(inp) : -1; break; } else { (*volfactor)++; if (*volfactor > 4) *volfactor = -1; if (option_verbose > 2) ast_verbose(VERBOSE_PREFIX_3 "Setting spy volume on %s to %d\n", chan->name, *volfactor); csth.volfactor = *volfactor; set_volume(chan, &csth); if (csth.volfactor) { ast_set_flag(&csth.spy, CHANSPY_READ_VOLADJUST); csth.spy.read_vol_adjustment = csth.volfactor; ast_set_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST); csth.spy.write_vol_adjustment = csth.volfactor; } else { ast_clear_flag(&csth.spy, CHANSPY_READ_VOLADJUST); ast_clear_flag(&csth.spy, CHANSPY_WRITE_VOLADJUST); } } } else if (res == '*') { break; } else if (res >= 48 && res <= 57) { inp[x++] = res; } } ast_deactivate_generator(chan); csth.spy.status = CHANSPY_DONE; ast_mutex_lock(&csth.spy.lock); if (csth.spy.chan) { ast_mutex_lock(&csth.spy.chan->lock); ast_channel_spy_remove(csth.spy.chan, &csth.spy); ast_mutex_unlock(&csth.spy.chan->lock); } ast_mutex_unlock(&csth.spy.lock); if (option_verbose > 1) ast_verbose(VERBOSE_PREFIX_2 "Done Spying on channel %s\n", name); ast_channel_spy_free(&csth.spy); return running; }
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; if (ast_check_hangup(chan) || ast_check_hangup(spyee_autochan->chan) || ast_test_flag(ast_channel_flags(spyee_autochan->chan), AST_FLAG_ZOMBIE)) { return 0; } ast_channel_lock(chan); spyer_name = ast_strdupa(ast_channel_name(chan)); ast_channel_unlock(chan); ast_channel_lock(spyee_autochan->chan); name = ast_strdupa(ast_channel_name(spyee_autochan->chan)); ast_channel_unlock(spyee_autochan->chan); ast_verb(2, "Spying on channel %s\n", name); publish_chanspy_message(chan, spyee_autochan->chan, 1); memset(&csth, 0, sizeof(csth)); ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL); /* This is the audiohook which gives us the audio off the channel we are spying on. */ ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy", 0); if (start_spying(spyee_autochan, spyer_name, &csth.spy_audiohook)) { ast_audiohook_destroy(&csth.spy_audiohook); return 0; } if (ast_test_flag(flags, OPTION_WHISPER | OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) { /* This audiohook will let us inject audio from our channel into the channel we are currently spying on. */ ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy", 0); 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 (ast_test_flag(flags, OPTION_BARGE | OPTION_DTMF_SWITCH_MODES)) { RAII_VAR(struct ast_channel *, bridged, ast_channel_bridge_peer(spyee_autochan->chan), ast_channel_cleanup); /* And this hook lets us inject audio into the channel that the spied on channel is currently bridged with. */ ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy", 0); if ((spyee_bridge_autochan = ast_autochan_setup(bridged))) { 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); } }
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; }