00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "asterisk.h"
00033
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 174823 $")
00035
00036 #include <ctype.h>
00037 #include <errno.h>
00038
00039 #include "asterisk/paths.h"
00040 #include "asterisk/file.h"
00041 #include "asterisk/channel.h"
00042 #include "asterisk/audiohook.h"
00043 #include "asterisk/features.h"
00044 #include "asterisk/app.h"
00045 #include "asterisk/utils.h"
00046 #include "asterisk/say.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/translate.h"
00049 #include "asterisk/module.h"
00050 #include "asterisk/lock.h"
00051 #include "asterisk/options.h"
00052
00053 #define AST_NAME_STRLEN 256
00054 #define NUM_SPYGROUPS 128
00055
00056 static const char *tdesc = "Listen to a channel, and optionally whisper into it";
00057 static const char *app_chan = "ChanSpy";
00058 static const char *desc_chan =
00059 " ChanSpy([chanprefix][,options]): This application is used to listen to the\n"
00060 "audio from an Asterisk channel. This includes the audio coming in and\n"
00061 "out of the channel being spied on. If the 'chanprefix' parameter is specified,\n"
00062 "only channels beginning with this string will be spied upon.\n"
00063 " While spying, the following actions may be performed:\n"
00064 " - Dialing # cycles the volume level.\n"
00065 " - Dialing * will stop spying and look for another channel to spy on.\n"
00066 " - Dialing a series of digits followed by # builds a channel name to append\n"
00067 " to 'chanprefix'. For example, executing ChanSpy(Agent) and then dialing\n"
00068 " the digits '1234#' while spying will begin spying on the channel\n"
00069 " 'Agent/1234'. Note that this feature will be overriden if the 'd' option\n"
00070 " is used\n"
00071 " Note: The X option supersedes the three features above in that if a valid\n"
00072 " single digit extension exists in the correct context ChanSpy will\n"
00073 " exit to it. This also disables choosing a channel based on 'chanprefix'\n"
00074 " and a digit sequence.\n"
00075 " Options:\n"
00076 " b - Only spy on channels involved in a bridged call.\n"
00077 " B - Instead of whispering on a single channel barge in on both\n"
00078 " channels involved in the call.\n"
00079 " d - Override the typical numeric DTMF functionality and instead\n"
00080 " use DTMF to switch between spy modes.\n"
00081 " 4 = spy mode\n"
00082 " 5 = whisper mode\n"
00083 " 6 = barge mode\n"
00084 " g(grp) - Only spy on channels in which one or more of the groups \n"
00085 " listed in 'grp' matches one or more groups from the\n"
00086 " SPYGROUP variable set on the channel to be spied upon.\n"
00087 " Note that both 'grp' and SPYGROUP can contain either a\n"
00088 " single group or a colon-delimited list of groups, such\n"
00089 " as 'sales:support:accounting'.\n"
00090 " n([mailbox][@context]) - Say the name of the person being spied on if that person has recorded\n"
00091 " his/her name. If a context is specified, then that voicemail context will\n"
00092 " be searched when retrieving the name, otherwise the \"default\" context\n"
00093 " will be searched. If no mailbox is specified, then the channel name will\n"
00094 " be used when searching for the name (i.e. if SIP/1000 is the channel being\n"
00095 " spied on and no mailbox is specified, then \"1000\" will be used when searching\n"
00096 " for the name).\n"
00097 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
00098 " selected channel name.\n"
00099 " r[(basename)] - Record the session to the monitor spool directory. An\n"
00100 " optional base for the filename may be specified. The\n"
00101 " default is 'chanspy'.\n"
00102 " s - Skip the playback of the channel type (i.e. SIP, IAX, etc) when\n"
00103 " speaking the selected channel name.\n"
00104 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
00105 " negative value refers to a quieter setting.\n"
00106 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
00107 " the spied-on channel.\n"
00108 " W - Enable 'private whisper' mode, so the spying channel can\n"
00109 " talk to the spied-on channel but cannot listen to that\n"
00110 " channel.\n"
00111 " o - Only listen to audio coming from this channel.\n"
00112 " X - Allow the user to exit ChanSpy to a valid single digit\n"
00113 " numeric extension in the current context or the context\n"
00114 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
00115 " name of the last channel that was spied on will be stored\n"
00116 " in the SPY_CHANNEL variable.\n"
00117 " e(ext) - Enable 'enforced' mode, so the spying channel can\n"
00118 " only monitor extensions whose name is in the 'ext' : \n"
00119 " delimited list.\n"
00120 ;
00121
00122 static const char *app_ext = "ExtenSpy";
00123 static const char *desc_ext =
00124 " ExtenSpy(exten[@context][,options]): This application is used to listen to the\n"
00125 "audio from an Asterisk channel. This includes the audio coming in and\n"
00126 "out of the channel being spied on. Only channels created by outgoing calls for the\n"
00127 "specified extension will be selected for spying. If the optional context is not\n"
00128 "supplied, the current channel's context will be used.\n"
00129 " While spying, the following actions may be performed:\n"
00130 " - Dialing # cycles the volume level.\n"
00131 " - Dialing * will stop spying and look for another channel to spy on.\n"
00132 " Note: The X option superseeds the two features above in that if a valid\n"
00133 " single digit extension exists in the correct context it ChanSpy will\n"
00134 " exit to it.\n"
00135 " Options:\n"
00136 " b - Only spy on channels involved in a bridged call.\n"
00137 " B - Instead of whispering on a single channel barge in on both\n"
00138 " channels involved in the call.\n"
00139 " d - Override the typical numeric DTMF functionality and instead\n"
00140 " use DTMF to switch between spy modes.\n"
00141 " 4 = spy mode\n"
00142 " 5 = whisper mode\n"
00143 " 6 = barge mode\n"
00144 " g(grp) - Only spy on channels in which one or more of the groups \n"
00145 " listed in 'grp' matches one or more groups from the\n"
00146 " SPYGROUP variable set on the channel to be spied upon.\n"
00147 " Note that both 'grp' and SPYGROUP can contain either a\n"
00148 " single group or a colon-delimited list of groups, such\n"
00149 " as 'sales:support:accounting'.\n"
00150 " n([mailbox][@context]) - Say the name of the person being spied on if that person has recorded\n"
00151 " his/her name. If a context is specified, then that voicemail context will\n"
00152 " be searched when retrieving the name, otherwise the \"default\" context\n"
00153 " will be searched. If no mailbox is specified, then the channel name will\n"
00154 " be used when searching for the name (i.e. if SIP/1000 is the channel being\n"
00155 " spied on and no mailbox is specified, then \"1000\" will be used when searching\n"
00156 " for the name).\n"
00157 " q - Don't play a beep when beginning to spy on a channel, or speak the\n"
00158 " selected channel name.\n"
00159 " r[(basename)] - Record the session to the monitor spool directory. An\n"
00160 " optional base for the filename may be specified. The\n"
00161 " default is 'chanspy'.\n"
00162 " s - Skip the playback of the channel type (i.e. SIP, IAX, etc) when\n"
00163 " speaking the selected channel name.\n"
00164 " v([value]) - Adjust the initial volume in the range from -4 to 4. A\n"
00165 " negative value refers to a quieter setting.\n"
00166 " w - Enable 'whisper' mode, so the spying channel can talk to\n"
00167 " the spied-on channel.\n"
00168 " W - Enable 'private whisper' mode, so the spying channel can\n"
00169 " talk to the spied-on channel but cannot listen to that\n"
00170 " channel.\n"
00171 " o - Only listen to audio coming from this channel.\n"
00172 " X - Allow the user to exit ChanSpy to a valid single digit\n"
00173 " numeric extension in the current context or the context\n"
00174 " specified by the SPY_EXIT_CONTEXT channel variable. The\n"
00175 " name of the last channel that was spied on will be stored\n"
00176 " in the SPY_CHANNEL variable.\n"
00177 ;
00178
00179 enum {
00180 OPTION_QUIET = (1 << 0),
00181 OPTION_BRIDGED = (1 << 1),
00182 OPTION_VOLUME = (1 << 2),
00183 OPTION_GROUP = (1 << 3),
00184 OPTION_RECORD = (1 << 4),
00185 OPTION_WHISPER = (1 << 5),
00186 OPTION_PRIVATE = (1 << 6),
00187 OPTION_READONLY = (1 << 7),
00188 OPTION_EXIT = (1 << 8),
00189 OPTION_ENFORCED = (1 << 9),
00190 OPTION_NOTECH = (1 << 10),
00191 OPTION_BARGE = (1 << 11),
00192 OPTION_NAME = (1 << 12),
00193 OPTION_DTMF_SWITCH_MODES = (1 << 13),
00194 } chanspy_opt_flags;
00195
00196 enum {
00197 OPT_ARG_VOLUME = 0,
00198 OPT_ARG_GROUP,
00199 OPT_ARG_RECORD,
00200 OPT_ARG_ENFORCED,
00201 OPT_ARG_NAME,
00202 OPT_ARG_ARRAY_SIZE,
00203 } chanspy_opt_args;
00204
00205 AST_APP_OPTIONS(spy_opts, {
00206 AST_APP_OPTION('q', OPTION_QUIET),
00207 AST_APP_OPTION('b', OPTION_BRIDGED),
00208 AST_APP_OPTION('B', OPTION_BARGE),
00209 AST_APP_OPTION('w', OPTION_WHISPER),
00210 AST_APP_OPTION('W', OPTION_PRIVATE),
00211 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
00212 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
00213 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
00214 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
00215 AST_APP_OPTION('o', OPTION_READONLY),
00216 AST_APP_OPTION('X', OPTION_EXIT),
00217 AST_APP_OPTION('s', OPTION_NOTECH),
00218 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
00219 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
00220 });
00221
00222 static int next_unique_id_to_use = 0;
00223
00224 struct chanspy_translation_helper {
00225
00226 struct ast_audiohook spy_audiohook;
00227 struct ast_audiohook whisper_audiohook;
00228 struct ast_audiohook bridge_whisper_audiohook;
00229 int fd;
00230 int volfactor;
00231 };
00232
00233 static void *spy_alloc(struct ast_channel *chan, void *data)
00234 {
00235
00236 return data;
00237 }
00238
00239 static void spy_release(struct ast_channel *chan, void *data)
00240 {
00241
00242 }
00243
00244 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
00245 {
00246 struct chanspy_translation_helper *csth = data;
00247 struct ast_frame *f = NULL;
00248
00249 ast_audiohook_lock(&csth->spy_audiohook);
00250 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00251
00252 ast_audiohook_unlock(&csth->spy_audiohook);
00253 return -1;
00254 }
00255
00256 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00257
00258 ast_audiohook_unlock(&csth->spy_audiohook);
00259
00260 if (!f)
00261 return 0;
00262
00263 if (ast_write(chan, f)) {
00264 ast_frfree(f);
00265 return -1;
00266 }
00267
00268 if (csth->fd) {
00269 if (write(csth->fd, f->data.ptr, f->datalen) < 0) {
00270 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00271 }
00272 }
00273
00274 ast_frfree(f);
00275
00276 return 0;
00277 }
00278
00279 static struct ast_generator spygen = {
00280 .alloc = spy_alloc,
00281 .release = spy_release,
00282 .generate = spy_generate,
00283 };
00284
00285 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
00286 {
00287 int res = 0;
00288 struct ast_channel *peer = NULL;
00289
00290 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00291
00292 res = ast_audiohook_attach(chan, audiohook);
00293
00294 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
00295 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00296 }
00297 return res;
00298 }
00299
00300 struct chanspy_ds {
00301 struct ast_channel *chan;
00302 char unique_id[20];
00303 ast_mutex_t lock;
00304 };
00305
00306 static void change_spy_mode(const char digit, struct ast_flags *flags)
00307 {
00308 if (digit == '4') {
00309 ast_clear_flag(flags, OPTION_WHISPER);
00310 ast_clear_flag(flags, OPTION_BARGE);
00311 } else if (digit == '5') {
00312 ast_clear_flag(flags, OPTION_BARGE);
00313 ast_set_flag(flags, OPTION_WHISPER);
00314 } else if (digit == '6') {
00315 ast_clear_flag(flags, OPTION_WHISPER);
00316 ast_set_flag(flags, OPTION_BARGE);
00317 }
00318 }
00319
00320 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
00321 int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
00322 {
00323 struct chanspy_translation_helper csth;
00324 int running = 0, res, x = 0;
00325 char inp[24] = {0};
00326 char *name;
00327 struct ast_frame *f;
00328 struct ast_silence_generator *silgen = NULL;
00329 struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
00330 const char *spyer_name;
00331
00332 ast_channel_lock(chan);
00333 spyer_name = ast_strdupa(chan->name);
00334 ast_channel_unlock(chan);
00335
00336 ast_mutex_lock(&spyee_chanspy_ds->lock);
00337 if (spyee_chanspy_ds->chan) {
00338 spyee = spyee_chanspy_ds->chan;
00339 ast_channel_lock(spyee);
00340 }
00341 ast_mutex_unlock(&spyee_chanspy_ds->lock);
00342
00343 if (!spyee)
00344 return 0;
00345
00346
00347
00348 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00349 ast_channel_unlock(spyee);
00350 return 0;
00351 }
00352
00353 name = ast_strdupa(spyee->name);
00354 ast_verb(2, "Spying on channel %s\n", name);
00355
00356 memset(&csth, 0, sizeof(csth));
00357
00358 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00359
00360 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00361 ast_audiohook_destroy(&csth.spy_audiohook);
00362 ast_channel_unlock(spyee);
00363 return 0;
00364 }
00365
00366 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00367 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
00368 if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
00369 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
00370 }
00371 if ((spyee_bridge = ast_bridged_channel(spyee))) {
00372 ast_channel_lock(spyee_bridge);
00373 if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
00374 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
00375 }
00376 ast_channel_unlock(spyee_bridge);
00377 }
00378 ast_channel_unlock(spyee);
00379 spyee = NULL;
00380
00381 ast_channel_lock(chan);
00382 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00383 ast_channel_unlock(chan);
00384
00385 csth.volfactor = *volfactor;
00386
00387 if (csth.volfactor) {
00388 csth.spy_audiohook.options.read_volume = csth.volfactor;
00389 csth.spy_audiohook.options.write_volume = csth.volfactor;
00390 }
00391
00392 csth.fd = fd;
00393
00394 if (ast_test_flag(flags, OPTION_PRIVATE))
00395 silgen = ast_channel_start_silence_generator(chan);
00396 else
00397 ast_activate_generator(chan, &spygen, &csth);
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00414 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00415 running = -1;
00416 break;
00417 }
00418
00419 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
00420 ast_audiohook_lock(&csth.whisper_audiohook);
00421 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00422 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00423 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00424 ast_audiohook_unlock(&csth.whisper_audiohook);
00425 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00426 ast_frfree(f);
00427 continue;
00428 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00429 ast_audiohook_lock(&csth.whisper_audiohook);
00430 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00431 ast_audiohook_unlock(&csth.whisper_audiohook);
00432 ast_frfree(f);
00433 continue;
00434 }
00435
00436 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00437 ast_frfree(f);
00438 if (!res)
00439 continue;
00440
00441 if (x == sizeof(inp))
00442 x = 0;
00443
00444 if (res < 0) {
00445 running = -1;
00446 break;
00447 }
00448
00449 if (ast_test_flag(flags, OPTION_EXIT)) {
00450 char tmp[2];
00451 tmp[0] = res;
00452 tmp[1] = '\0';
00453 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00454 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00455 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00456 running = -2;
00457 break;
00458 } else {
00459 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00460 }
00461 } else if (res >= '0' && res <= '9') {
00462 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
00463 change_spy_mode(res, flags);
00464 } else {
00465 inp[x++] = res;
00466 }
00467 }
00468
00469 if (res == '*') {
00470 running = 0;
00471 break;
00472 } else if (res == '#') {
00473 if (!ast_strlen_zero(inp)) {
00474 running = atoi(inp);
00475 break;
00476 }
00477
00478 (*volfactor)++;
00479 if (*volfactor > 4)
00480 *volfactor = -4;
00481 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00482
00483 csth.volfactor = *volfactor;
00484 csth.spy_audiohook.options.read_volume = csth.volfactor;
00485 csth.spy_audiohook.options.write_volume = csth.volfactor;
00486 }
00487 }
00488
00489 if (ast_test_flag(flags, OPTION_PRIVATE))
00490 ast_channel_stop_silence_generator(chan, silgen);
00491 else
00492 ast_deactivate_generator(chan);
00493
00494 ast_channel_lock(chan);
00495 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00496 ast_channel_unlock(chan);
00497
00498 ast_audiohook_lock(&csth.whisper_audiohook);
00499 ast_audiohook_detach(&csth.whisper_audiohook);
00500 ast_audiohook_unlock(&csth.whisper_audiohook);
00501 ast_audiohook_destroy(&csth.whisper_audiohook);
00502
00503 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00504 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
00505 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00506 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
00507
00508 ast_audiohook_lock(&csth.spy_audiohook);
00509 ast_audiohook_detach(&csth.spy_audiohook);
00510 ast_audiohook_unlock(&csth.spy_audiohook);
00511 ast_audiohook_destroy(&csth.spy_audiohook);
00512
00513 ast_verb(2, "Done Spying on channel %s\n", name);
00514
00515 return running;
00516 }
00517
00518
00519
00520
00521
00522 static void chanspy_ds_destroy(void *data)
00523 {
00524 struct chanspy_ds *chanspy_ds = data;
00525
00526
00527
00528
00529
00530 ast_mutex_lock(&chanspy_ds->lock);
00531 chanspy_ds->chan = NULL;
00532 ast_mutex_unlock(&chanspy_ds->lock);
00533 }
00534
00535 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00536 {
00537 struct chanspy_ds *chanspy_ds = data;
00538
00539 ast_mutex_lock(&chanspy_ds->lock);
00540 chanspy_ds->chan = new_chan;
00541 ast_mutex_unlock(&chanspy_ds->lock);
00542 }
00543
00544 static const struct ast_datastore_info chanspy_ds_info = {
00545 .type = "chanspy",
00546 .destroy = chanspy_ds_destroy,
00547 .chan_fixup = chanspy_ds_chan_fixup,
00548 };
00549
00550 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
00551 {
00552 if (!chanspy_ds)
00553 return NULL;
00554
00555 ast_mutex_lock(&chanspy_ds->lock);
00556 if (chanspy_ds->chan) {
00557 struct ast_datastore *datastore;
00558 struct ast_channel *chan;
00559
00560 chan = chanspy_ds->chan;
00561
00562 ast_channel_lock(chan);
00563 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00564 ast_channel_datastore_remove(chan, datastore);
00565
00566 chanspy_ds_destroy(datastore->data);
00567 datastore->data = NULL;
00568 ast_datastore_free(datastore);
00569 }
00570 ast_channel_unlock(chan);
00571 }
00572 ast_mutex_unlock(&chanspy_ds->lock);
00573
00574 return NULL;
00575 }
00576
00577
00578 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
00579 {
00580 struct ast_datastore *datastore = NULL;
00581
00582 ast_mutex_lock(&chanspy_ds->lock);
00583
00584 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00585 ast_mutex_unlock(&chanspy_ds->lock);
00586 chanspy_ds = chanspy_ds_free(chanspy_ds);
00587 ast_channel_unlock(chan);
00588 return NULL;
00589 }
00590
00591 chanspy_ds->chan = chan;
00592 datastore->data = chanspy_ds;
00593 ast_channel_datastore_add(chan, datastore);
00594
00595 return chanspy_ds;
00596 }
00597
00598 static struct chanspy_ds *next_channel(struct ast_channel *chan,
00599 const struct ast_channel *last, const char *spec,
00600 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
00601 {
00602 struct ast_channel *next;
00603 const size_t pseudo_len = strlen("DAHDI/pseudo");
00604
00605 redo:
00606 if (!ast_strlen_zero(spec))
00607 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00608 else if (!ast_strlen_zero(exten))
00609 next = ast_walk_channel_by_exten_locked(last, exten, context);
00610 else
00611 next = ast_channel_walk_locked(last);
00612
00613 if (!next)
00614 return NULL;
00615
00616 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00617 last = next;
00618 ast_channel_unlock(next);
00619 goto redo;
00620 } else if (next == chan) {
00621 last = next;
00622 ast_channel_unlock(next);
00623 goto redo;
00624 }
00625
00626 return setup_chanspy_ds(next, chanspy_ds);
00627 }
00628
00629 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
00630 int volfactor, const int fd, const char *mygroup, const char *myenforced,
00631 const char *spec, const char *exten, const char *context, const char *mailbox,
00632 const char *name_context)
00633 {
00634 char nameprefix[AST_NAME_STRLEN];
00635 char peer_name[AST_NAME_STRLEN + 5];
00636 char exitcontext[AST_MAX_CONTEXT] = "";
00637 signed char zero_volume = 0;
00638 int waitms;
00639 int res;
00640 char *ptr;
00641 int num;
00642 int num_spyed_upon = 1;
00643 struct chanspy_ds chanspy_ds = { 0, };
00644
00645 if (ast_test_flag(flags, OPTION_EXIT)) {
00646 const char *c;
00647 ast_channel_lock(chan);
00648 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
00649 ast_copy_string(exitcontext, c, sizeof(exitcontext));
00650 } else if (!ast_strlen_zero(chan->macrocontext)) {
00651 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00652 } else {
00653 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00654 }
00655 ast_channel_unlock(chan);
00656 }
00657
00658 ast_mutex_init(&chanspy_ds.lock);
00659
00660 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00661
00662 if (chan->_state != AST_STATE_UP)
00663 ast_answer(chan);
00664
00665 ast_set_flag(chan, AST_FLAG_SPYING);
00666
00667 waitms = 100;
00668
00669 for (;;) {
00670 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00671 struct ast_channel *prev = NULL, *peer = NULL;
00672
00673 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00674 res = ast_streamfile(chan, "beep", chan->language);
00675 if (!res)
00676 res = ast_waitstream(chan, "");
00677 else if (res < 0) {
00678 ast_clear_flag(chan, AST_FLAG_SPYING);
00679 break;
00680 }
00681 if (!ast_strlen_zero(exitcontext)) {
00682 char tmp[2];
00683 tmp[0] = res;
00684 tmp[1] = '\0';
00685 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00686 goto exit;
00687 else
00688 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00689 }
00690 }
00691
00692 res = ast_waitfordigit(chan, waitms);
00693 if (res < 0) {
00694 ast_clear_flag(chan, AST_FLAG_SPYING);
00695 break;
00696 }
00697 if (!ast_strlen_zero(exitcontext)) {
00698 char tmp[2];
00699 tmp[0] = res;
00700 tmp[1] = '\0';
00701 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00702 goto exit;
00703 else
00704 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00705 }
00706
00707
00708 waitms = 100;
00709 num_spyed_upon = 0;
00710
00711 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00712 peer_chanspy_ds;
00713 chanspy_ds_free(peer_chanspy_ds), prev = peer,
00714 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
00715 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00716 int igrp = !mygroup;
00717 int ienf = !myenforced;
00718 char *s;
00719
00720 peer = peer_chanspy_ds->chan;
00721
00722 ast_mutex_unlock(&peer_chanspy_ds->lock);
00723
00724 if (peer == prev) {
00725 ast_channel_unlock(peer);
00726 chanspy_ds_free(peer_chanspy_ds);
00727 break;
00728 }
00729
00730 if (ast_check_hangup(chan)) {
00731 ast_channel_unlock(peer);
00732 chanspy_ds_free(peer_chanspy_ds);
00733 break;
00734 }
00735
00736 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00737 ast_channel_unlock(peer);
00738 continue;
00739 }
00740
00741 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00742 ast_channel_unlock(peer);
00743 continue;
00744 }
00745
00746 if (mygroup) {
00747 int num_groups = 0;
00748 int num_mygroups = 0;
00749 char dup_group[512];
00750 char dup_mygroup[512];
00751 char *groups[NUM_SPYGROUPS];
00752 char *mygroups[NUM_SPYGROUPS];
00753 const char *group;
00754 int x;
00755 int y;
00756 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
00757 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
00758 ARRAY_LEN(mygroups));
00759
00760 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00761 ast_copy_string(dup_group, group, sizeof(dup_group));
00762 num_groups = ast_app_separate_args(dup_group, ':', groups,
00763 ARRAY_LEN(groups));
00764 }
00765
00766 for (y = 0; y < num_mygroups; y++) {
00767 for (x = 0; x < num_groups; x++) {
00768 if (!strcmp(mygroups[y], groups[x])) {
00769 igrp = 1;
00770 break;
00771 }
00772 }
00773 }
00774 }
00775
00776 if (!igrp) {
00777 ast_channel_unlock(peer);
00778 continue;
00779 }
00780
00781 if (myenforced) {
00782 char ext[AST_CHANNEL_NAME + 3];
00783 char buffer[512];
00784 char *end;
00785
00786 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00787
00788 ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00789 if ((end = strchr(ext, '-'))) {
00790 *end++ = ':';
00791 *end = '\0';
00792 }
00793
00794 ext[0] = ':';
00795
00796 if (strcasestr(buffer, ext)) {
00797 ienf = 1;
00798 }
00799 }
00800
00801 if (!ienf) {
00802 continue;
00803 }
00804
00805 strcpy(peer_name, "spy-");
00806 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00807 ptr = strchr(peer_name, '/');
00808 *ptr++ = '\0';
00809 ptr = strsep(&ptr, "-");
00810
00811 for (s = peer_name; s < ptr; s++)
00812 *s = tolower(*s);
00813
00814
00815
00816
00817 ast_channel_unlock(peer);
00818
00819 if (!ast_test_flag(flags, OPTION_QUIET)) {
00820 if (ast_test_flag(flags, OPTION_NAME)) {
00821 const char *local_context = S_OR(name_context, "default");
00822 const char *local_mailbox = S_OR(mailbox, ptr);
00823 res = ast_app_sayname(chan, local_mailbox, local_context);
00824 }
00825 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
00826 if (!ast_test_flag(flags, OPTION_NOTECH)) {
00827 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00828 res = ast_streamfile(chan, peer_name, chan->language);
00829 if (!res) {
00830 res = ast_waitstream(chan, "");
00831 }
00832 if (res) {
00833 chanspy_ds_free(peer_chanspy_ds);
00834 break;
00835 }
00836 } else {
00837 res = ast_say_character_str(chan, peer_name, "", chan->language);
00838 }
00839 }
00840 if ((num = atoi(ptr)))
00841 ast_say_digits(chan, atoi(ptr), "", chan->language);
00842 }
00843 }
00844
00845 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00846 num_spyed_upon++;
00847
00848 if (res == -1) {
00849 chanspy_ds_free(peer_chanspy_ds);
00850 goto exit;
00851 } else if (res == -2) {
00852 res = 0;
00853 chanspy_ds_free(peer_chanspy_ds);
00854 goto exit;
00855 } else if (res > 1 && spec) {
00856 struct ast_channel *next;
00857
00858 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00859
00860 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
00861 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
00862 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
00863 } else {
00864
00865
00866 ast_mutex_lock(&peer_chanspy_ds->lock);
00867 if (peer_chanspy_ds->chan) {
00868 ast_channel_lock(peer_chanspy_ds->chan);
00869 next_chanspy_ds = peer_chanspy_ds;
00870 peer_chanspy_ds = NULL;
00871 } else {
00872
00873 ast_mutex_unlock(&peer_chanspy_ds->lock);
00874 next_chanspy_ds = NULL;
00875 }
00876 }
00877
00878 peer = NULL;
00879 }
00880 }
00881 if (res == -1 || ast_check_hangup(chan))
00882 break;
00883 }
00884 exit:
00885
00886 ast_clear_flag(chan, AST_FLAG_SPYING);
00887
00888 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00889
00890 ast_mutex_lock(&chanspy_ds.lock);
00891 ast_mutex_unlock(&chanspy_ds.lock);
00892 ast_mutex_destroy(&chanspy_ds.lock);
00893
00894 return res;
00895 }
00896
00897 static int chanspy_exec(struct ast_channel *chan, void *data)
00898 {
00899 char *myenforced = NULL;
00900 char *mygroup = NULL;
00901 char *recbase = NULL;
00902 int fd = 0;
00903 struct ast_flags flags;
00904 int oldwf = 0;
00905 int volfactor = 0;
00906 int res;
00907 char *mailbox = NULL;
00908 char *name_context = NULL;
00909 AST_DECLARE_APP_ARGS(args,
00910 AST_APP_ARG(spec);
00911 AST_APP_ARG(options);
00912 );
00913 char *opts[OPT_ARG_ARRAY_SIZE];
00914
00915 data = ast_strdupa(data);
00916 AST_STANDARD_APP_ARGS(args, data);
00917
00918 if (args.spec && !strcmp(args.spec, "all"))
00919 args.spec = NULL;
00920
00921 if (args.options) {
00922 ast_app_parse_options(spy_opts, &flags, opts, args.options);
00923 if (ast_test_flag(&flags, OPTION_GROUP))
00924 mygroup = opts[OPT_ARG_GROUP];
00925
00926 if (ast_test_flag(&flags, OPTION_RECORD) &&
00927 !(recbase = opts[OPT_ARG_RECORD]))
00928 recbase = "chanspy";
00929
00930 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00931 int vol;
00932
00933 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
00934 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00935 else
00936 volfactor = vol;
00937 }
00938
00939 if (ast_test_flag(&flags, OPTION_PRIVATE))
00940 ast_set_flag(&flags, OPTION_WHISPER);
00941
00942 if (ast_test_flag(&flags, OPTION_ENFORCED))
00943 myenforced = opts[OPT_ARG_ENFORCED];
00944
00945 if (ast_test_flag(&flags, OPTION_NAME)) {
00946 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
00947 char *delimiter;
00948 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
00949 mailbox = opts[OPT_ARG_NAME];
00950 *delimiter++ = '\0';
00951 name_context = delimiter;
00952 } else {
00953 mailbox = opts[OPT_ARG_NAME];
00954 }
00955 }
00956 }
00957
00958
00959 } else
00960 ast_clear_flag(&flags, AST_FLAGS_ALL);
00961
00962 oldwf = chan->writeformat;
00963 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00964 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00965 return -1;
00966 }
00967
00968 if (recbase) {
00969 char filename[PATH_MAX];
00970
00971 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00972 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
00973 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00974 fd = 0;
00975 }
00976 }
00977
00978 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
00979
00980 if (fd)
00981 close(fd);
00982
00983 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00984 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00985
00986 return res;
00987 }
00988
00989 static int extenspy_exec(struct ast_channel *chan, void *data)
00990 {
00991 char *ptr, *exten = NULL;
00992 char *mygroup = NULL;
00993 char *recbase = NULL;
00994 int fd = 0;
00995 struct ast_flags flags;
00996 int oldwf = 0;
00997 int volfactor = 0;
00998 int res;
00999 char *mailbox = NULL;
01000 char *name_context = NULL;
01001 AST_DECLARE_APP_ARGS(args,
01002 AST_APP_ARG(context);
01003 AST_APP_ARG(options);
01004 );
01005
01006 data = ast_strdupa(data);
01007
01008 AST_STANDARD_APP_ARGS(args, data);
01009 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
01010 exten = args.context;
01011 *ptr++ = '\0';
01012 args.context = ptr;
01013 }
01014
01015 if (ast_strlen_zero(args.context))
01016 args.context = ast_strdupa(chan->context);
01017
01018 if (args.options) {
01019 char *opts[OPT_ARG_ARRAY_SIZE];
01020
01021 ast_app_parse_options(spy_opts, &flags, opts, args.options);
01022 if (ast_test_flag(&flags, OPTION_GROUP))
01023 mygroup = opts[OPT_ARG_GROUP];
01024
01025 if (ast_test_flag(&flags, OPTION_RECORD) &&
01026 !(recbase = opts[OPT_ARG_RECORD]))
01027 recbase = "chanspy";
01028
01029 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01030 int vol;
01031
01032 if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
01033 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01034 else
01035 volfactor = vol;
01036 }
01037
01038 if (ast_test_flag(&flags, OPTION_PRIVATE))
01039 ast_set_flag(&flags, OPTION_WHISPER);
01040
01041
01042 if (ast_test_flag(&flags, OPTION_NAME)) {
01043 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01044 char *delimiter;
01045 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01046 mailbox = opts[OPT_ARG_NAME];
01047 *delimiter++ = '\0';
01048 name_context = delimiter;
01049 } else {
01050 mailbox = opts[OPT_ARG_NAME];
01051 }
01052 }
01053 }
01054
01055 } else
01056 ast_clear_flag(&flags, AST_FLAGS_ALL);
01057
01058 oldwf = chan->writeformat;
01059 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01060 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01061 return -1;
01062 }
01063
01064 if (recbase) {
01065 char filename[PATH_MAX];
01066
01067 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01068 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01069 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01070 fd = 0;
01071 }
01072 }
01073
01074
01075 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
01076
01077 if (fd)
01078 close(fd);
01079
01080 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01081 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01082
01083 return res;
01084 }
01085
01086 static int unload_module(void)
01087 {
01088 int res = 0;
01089
01090 res |= ast_unregister_application(app_chan);
01091 res |= ast_unregister_application(app_ext);
01092
01093 return res;
01094 }
01095
01096 static int load_module(void)
01097 {
01098 int res = 0;
01099
01100 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
01101 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
01102
01103 return res;
01104 }
01105
01106 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");