static struct chanspy_ds *next_channel(struct ast_channel *chan, const struct ast_channel *last, const char *spec, const char *exten, const char *context, struct chanspy_ds *chanspy_ds) { struct ast_channel *next; const size_t pseudo_len = strlen("DAHDI/pseudo"); redo: if (!ast_strlen_zero(spec)) next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); else if (!ast_strlen_zero(exten)) next = ast_walk_channel_by_exten_locked(last, exten, context); else next = ast_channel_walk_locked(last); if (!next) return NULL; if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) { last = next; ast_channel_unlock(next); goto redo; } else if (next == chan) { last = next; ast_channel_unlock(next); goto redo; } return setup_chanspy_ds(next, chanspy_ds); }
static struct chanspy_ds *next_channel(struct ast_channel *chan, const struct ast_channel *last, const char *spec, const char *exten, const char *context, struct chanspy_ds *chanspy_ds) { struct ast_channel *this; char channel_name[AST_CHANNEL_NAME]; static size_t PSEUDO_CHAN_LEN = 0; if (!PSEUDO_CHAN_LEN) { PSEUDO_CHAN_LEN = *dahdi_chan_name_len + strlen("/pseudo"); } redo: if (spec) this = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec)); else if (exten) this = ast_walk_channel_by_exten_locked(last, exten, context); else this = ast_channel_walk_locked(last); if (!this) return NULL; snprintf(channel_name, AST_CHANNEL_NAME, "%s/pseudo", dahdi_chan_name); if (!strncmp(this->name, channel_name, PSEUDO_CHAN_LEN)) { last = this; ast_channel_unlock(this); goto redo; } else if (this == chan) { last = this; ast_channel_unlock(this); goto redo; } return setup_chanspy_ds(this, chanspy_ds); }
static int common_exec(struct ast_channel *chan, const struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *myenforced, const char *spec, const char *exten, const char *context) { char nameprefix[AST_NAME_STRLEN]; char peer_name[AST_NAME_STRLEN + 5]; char exitcontext[AST_MAX_CONTEXT] = ""; signed char zero_volume = 0; int waitms; int res; char *ptr; int num; int num_spyed_upon = 1; struct chanspy_ds chanspy_ds; if (ast_test_flag(flags, OPTION_EXIT)) { const char *c; if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) ast_copy_string(exitcontext, c, sizeof(exitcontext)); else if (!ast_strlen_zero(chan->macrocontext)) ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext)); else ast_copy_string(exitcontext, chan->context, sizeof(exitcontext)); } ast_mutex_init(&chanspy_ds.lock); snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); if (chan->_state != AST_STATE_UP) ast_answer(chan); ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ waitms = 100; for (;;) { struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; struct ast_channel *prev = NULL, *peer = NULL; if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, ""); else if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } } res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } if (!ast_strlen_zero(exitcontext)) { char tmp[2]; tmp[0] = res; tmp[1] = '\0'; if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) goto exit; else ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext); } /* reset for the next loop around, unless overridden later */ waitms = 100; num_spyed_upon = 0; for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); peer_chanspy_ds; chanspy_ds_free(peer_chanspy_ds), prev = peer, peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { const char *group; int igrp = !mygroup; char *groups[25]; int num_groups = 0; char dup_group[512]; int x; char *s; char *buffer; char *end; char *ext; char *form_enforced; int ienf = !myenforced; peer = peer_chanspy_ds->chan; ast_mutex_unlock(&peer_chanspy_ds->lock); if (peer == prev) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_check_hangup(chan)) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { ast_channel_unlock(peer); continue; } if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { ast_channel_unlock(peer); continue; } if (mygroup) { if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { ast_copy_string(dup_group, group, sizeof(dup_group)); num_groups = ast_app_separate_args(dup_group, ':', groups, ARRAY_LEN(groups)); } for (x = 0; x < num_groups; x++) { if (!strcmp(mygroup, groups[x])) { igrp = 1; break; } } } if (!igrp) { ast_channel_unlock(peer); continue; } if (myenforced) { /* We don't need to allocate more space than just the length of (peer->name) for ext as we will cut the channel name's ending before copying into ext */ ext = alloca(strlen(peer->name)); form_enforced = alloca(strlen(myenforced) + 3); strcpy(form_enforced, ":"); strcat(form_enforced, myenforced); strcat(form_enforced, ":"); buffer = ast_strdupa(peer->name); if ((end = strchr(buffer, '-'))) { *end++ = ':'; *end = '\0'; } strcpy(ext, ":"); strcat(ext, buffer); if (strcasestr(form_enforced, ext)) ienf = 1; } if (!ienf) continue; strcpy(peer_name, "spy-"); strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); ptr = strchr(peer_name, '/'); *ptr++ = '\0'; for (s = peer_name; s < ptr; s++) *s = tolower(*s); /* We have to unlock the peer channel here to avoid a deadlock. * So, when we need to dereference it again, we have to lock the * datastore and get the pointer from there to see if the channel * is still valid. */ ast_channel_unlock(peer); if (!ast_test_flag(flags, OPTION_QUIET)) { if (ast_fileexists(peer_name, NULL, NULL) != -1) { res = ast_streamfile(chan, peer_name, chan->language); if (!res) res = ast_waitstream(chan, ""); if (res) { chanspy_ds_free(peer_chanspy_ds); break; } } else res = ast_say_character_str(chan, peer_name, "", chan->language); if ((num = atoi(ptr))) ast_say_digits(chan, atoi(ptr), "", chan->language); } res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext); num_spyed_upon++; if (res == -1) { chanspy_ds_free(peer_chanspy_ds); goto exit; } else if (res == -2) { res = 0; chanspy_ds_free(peer_chanspy_ds); goto exit; } else if (res > 1 && spec) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); } else { /* stay on this channel, if it is still valid */ ast_mutex_lock(&peer_chanspy_ds->lock); if (peer_chanspy_ds->chan) { ast_channel_lock(peer_chanspy_ds->chan); next_chanspy_ds = peer_chanspy_ds; peer_chanspy_ds = NULL; } else { /* the channel is gone */ ast_mutex_unlock(&peer_chanspy_ds->lock); next_chanspy_ds = NULL; } } peer = NULL; } } if (res == -1 || ast_check_hangup(chan)) break; } exit: ast_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); ast_mutex_lock(&chanspy_ds.lock); ast_mutex_unlock(&chanspy_ds.lock); ast_mutex_destroy(&chanspy_ds.lock); return res; }
static int common_exec(struct ast_channel *chan, const struct ast_flags *flags, int volfactor, const int fd, const char *mygroup, const char *spec, const char *exten, const char *context) { char nameprefix[AST_NAME_STRLEN]; char peer_name[AST_NAME_STRLEN + 5]; signed char zero_volume = 0; int waitms; int res; char *ptr; int num; int num_spyed_upon = 1; struct chanspy_ds chanspy_ds = { 0, }; ast_mutex_init(&chanspy_ds.lock); snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1)); if (chan->_state != AST_STATE_UP) ast_answer(chan); ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */ waitms = 100; for (;;) { struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL; struct ast_channel *prev = NULL, *peer = NULL; if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) { res = ast_streamfile(chan, "beep", chan->language); if (!res) res = ast_waitstream(chan, ""); else if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } } res = ast_waitfordigit(chan, waitms); if (res < 0) { ast_clear_flag(chan, AST_FLAG_SPYING); break; } /* reset for the next loop around, unless overridden later */ waitms = 100; num_spyed_upon = 0; for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds); peer_chanspy_ds; chanspy_ds_free(peer_chanspy_ds), prev = peer, peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) { const char *group; int igrp = !mygroup; char *groups[25]; int num_groups = 0; char dup_group[512]; int x; char *s; peer = peer_chanspy_ds->chan; ast_mutex_unlock(&peer_chanspy_ds->lock); if (peer == prev) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_check_hangup(chan)) { ast_channel_unlock(peer); chanspy_ds_free(peer_chanspy_ds); break; } if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) { ast_channel_unlock(peer); continue; } if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) { ast_channel_unlock(peer); continue; } if (mygroup) { if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) { ast_copy_string(dup_group, group, sizeof(dup_group)); num_groups = ast_app_separate_args(dup_group, ':', groups, sizeof(groups) / sizeof(groups[0])); } for (x = 0; x < num_groups; x++) { if (!strcmp(mygroup, groups[x])) { igrp = 1; break; } } } if (!igrp) { ast_channel_unlock(peer); continue; } strcpy(peer_name, "spy-"); strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1); ptr = strchr(peer_name, '/'); *ptr++ = '\0'; for (s = peer_name; s < ptr; s++) *s = tolower(*s); /* We have to unlock the peer channel here to avoid a deadlock. * So, when we need to dereference it again, we have to lock the * datastore and get the pointer from there to see if the channel * is still valid. */ ast_channel_unlock(peer); if (!ast_test_flag(flags, OPTION_QUIET)) { if (ast_fileexists(peer_name, NULL, NULL) != -1) { res = ast_streamfile(chan, peer_name, chan->language); if (!res) res = ast_waitstream(chan, ""); if (res) { chanspy_ds_free(peer_chanspy_ds); break; } } else res = ast_say_character_str(chan, peer_name, "", chan->language); if ((num = atoi(ptr))) ast_say_digits(chan, atoi(ptr), "", chan->language); } res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags); num_spyed_upon++; if (res == -1) { chanspy_ds_free(peer_chanspy_ds); break; } else if (res > 1 && spec) { struct ast_channel *next; snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res); if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) { peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds); next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds); } else { /* stay on this channel, if it is still valid */ ast_mutex_lock(&peer_chanspy_ds->lock); if (peer_chanspy_ds->chan) { ast_channel_lock(peer_chanspy_ds->chan); next_chanspy_ds = peer_chanspy_ds; peer_chanspy_ds = NULL; } else { /* the channel is gone */ ast_mutex_unlock(&peer_chanspy_ds->lock); next_chanspy_ds = NULL; } } peer = NULL; } } if (res == -1 || ast_check_hangup(chan)) break; } ast_clear_flag(chan, AST_FLAG_SPYING); ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0); ast_mutex_lock(&chanspy_ds.lock); ast_mutex_unlock(&chanspy_ds.lock); ast_mutex_destroy(&chanspy_ds.lock); return res; }