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: 224180 $")
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, *cur;
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 if (ast_test_flag(chan, OPTION_READONLY)) {
00257
00258 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR);
00259 } else {
00260 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00261 }
00262
00263 ast_audiohook_unlock(&csth->spy_audiohook);
00264
00265 if (!f)
00266 return 0;
00267
00268 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00269 if (ast_write(chan, cur)) {
00270 ast_frfree(f);
00271 return -1;
00272 }
00273
00274 if (csth->fd) {
00275 if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
00276 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00277 }
00278 }
00279 }
00280
00281 ast_frfree(f);
00282
00283 return 0;
00284 }
00285
00286 static struct ast_generator spygen = {
00287 .alloc = spy_alloc,
00288 .release = spy_release,
00289 .generate = spy_generate,
00290 };
00291
00292 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
00293 {
00294 int res = 0;
00295 struct ast_channel *peer = NULL;
00296
00297 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00298
00299 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
00300 res = ast_audiohook_attach(chan, audiohook);
00301
00302 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
00303 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00304 }
00305 return res;
00306 }
00307
00308 struct chanspy_ds {
00309 struct ast_channel *chan;
00310 char unique_id[20];
00311 ast_mutex_t lock;
00312 };
00313
00314 static void change_spy_mode(const char digit, struct ast_flags *flags)
00315 {
00316 if (digit == '4') {
00317 ast_clear_flag(flags, OPTION_WHISPER);
00318 ast_clear_flag(flags, OPTION_BARGE);
00319 } else if (digit == '5') {
00320 ast_clear_flag(flags, OPTION_BARGE);
00321 ast_set_flag(flags, OPTION_WHISPER);
00322 } else if (digit == '6') {
00323 ast_clear_flag(flags, OPTION_WHISPER);
00324 ast_set_flag(flags, OPTION_BARGE);
00325 }
00326 }
00327
00328 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
00329 int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
00330 {
00331 struct chanspy_translation_helper csth;
00332 int running = 0, res, x = 0;
00333 char inp[24] = {0};
00334 char *name;
00335 struct ast_frame *f;
00336 struct ast_silence_generator *silgen = NULL;
00337 struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
00338 const char *spyer_name;
00339
00340 ast_channel_lock(chan);
00341 spyer_name = ast_strdupa(chan->name);
00342 ast_channel_unlock(chan);
00343
00344 ast_mutex_lock(&spyee_chanspy_ds->lock);
00345 while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) {
00346
00347
00348 DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock);
00349 }
00350 ast_mutex_unlock(&spyee_chanspy_ds->lock);
00351
00352 if (!spyee)
00353 return 0;
00354
00355
00356
00357 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00358 ast_channel_unlock(spyee);
00359 return 0;
00360 }
00361
00362 name = ast_strdupa(spyee->name);
00363 ast_verb(2, "Spying on channel %s\n", name);
00364
00365 memset(&csth, 0, sizeof(csth));
00366
00367 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00368
00369 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00370 ast_audiohook_destroy(&csth.spy_audiohook);
00371 ast_channel_unlock(spyee);
00372 return 0;
00373 }
00374
00375 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00376 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
00377 if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
00378 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
00379 }
00380 if ((spyee_bridge = ast_bridged_channel(spyee))) {
00381 ast_channel_lock(spyee_bridge);
00382 if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
00383 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
00384 }
00385 ast_channel_unlock(spyee_bridge);
00386 }
00387 ast_channel_unlock(spyee);
00388 spyee = NULL;
00389
00390 ast_channel_lock(chan);
00391 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00392 ast_channel_unlock(chan);
00393
00394 csth.volfactor = *volfactor;
00395
00396 if (csth.volfactor) {
00397 csth.spy_audiohook.options.read_volume = csth.volfactor;
00398 csth.spy_audiohook.options.write_volume = csth.volfactor;
00399 }
00400
00401 csth.fd = fd;
00402
00403 if (ast_test_flag(flags, OPTION_PRIVATE))
00404 silgen = ast_channel_start_silence_generator(chan);
00405 else
00406 ast_activate_generator(chan, &spygen, &csth);
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00423 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00424 running = -1;
00425 break;
00426 }
00427
00428 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
00429 ast_audiohook_lock(&csth.whisper_audiohook);
00430 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00431 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00432 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00433 ast_audiohook_unlock(&csth.whisper_audiohook);
00434 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00435 ast_frfree(f);
00436 continue;
00437 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00438 ast_audiohook_lock(&csth.whisper_audiohook);
00439 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00440 ast_audiohook_unlock(&csth.whisper_audiohook);
00441 ast_frfree(f);
00442 continue;
00443 }
00444
00445 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00446 ast_frfree(f);
00447 if (!res)
00448 continue;
00449
00450 if (x == sizeof(inp))
00451 x = 0;
00452
00453 if (res < 0) {
00454 running = -1;
00455 break;
00456 }
00457
00458 if (ast_test_flag(flags, OPTION_EXIT)) {
00459 char tmp[2];
00460 tmp[0] = res;
00461 tmp[1] = '\0';
00462 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00463 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00464 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00465 running = -2;
00466 break;
00467 } else {
00468 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00469 }
00470 } else if (res >= '0' && res <= '9') {
00471 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
00472 change_spy_mode(res, flags);
00473 } else {
00474 inp[x++] = res;
00475 }
00476 }
00477
00478 if (res == '*') {
00479 running = 0;
00480 break;
00481 } else if (res == '#') {
00482 if (!ast_strlen_zero(inp)) {
00483 running = atoi(inp);
00484 break;
00485 }
00486
00487 (*volfactor)++;
00488 if (*volfactor > 4)
00489 *volfactor = -4;
00490 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00491
00492 csth.volfactor = *volfactor;
00493 csth.spy_audiohook.options.read_volume = csth.volfactor;
00494 csth.spy_audiohook.options.write_volume = csth.volfactor;
00495 }
00496 }
00497
00498 if (ast_test_flag(flags, OPTION_PRIVATE))
00499 ast_channel_stop_silence_generator(chan, silgen);
00500 else
00501 ast_deactivate_generator(chan);
00502
00503 ast_channel_lock(chan);
00504 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00505 ast_channel_unlock(chan);
00506
00507 ast_audiohook_lock(&csth.whisper_audiohook);
00508 ast_audiohook_detach(&csth.whisper_audiohook);
00509 ast_audiohook_unlock(&csth.whisper_audiohook);
00510 ast_audiohook_destroy(&csth.whisper_audiohook);
00511
00512 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00513 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
00514 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00515 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
00516
00517 ast_audiohook_lock(&csth.spy_audiohook);
00518 ast_audiohook_detach(&csth.spy_audiohook);
00519 ast_audiohook_unlock(&csth.spy_audiohook);
00520 ast_audiohook_destroy(&csth.spy_audiohook);
00521
00522 ast_verb(2, "Done Spying on channel %s\n", name);
00523
00524 return running;
00525 }
00526
00527
00528
00529
00530
00531 static void chanspy_ds_destroy(void *data)
00532 {
00533 struct chanspy_ds *chanspy_ds = data;
00534
00535
00536
00537
00538
00539 ast_mutex_lock(&chanspy_ds->lock);
00540 chanspy_ds->chan = NULL;
00541 ast_mutex_unlock(&chanspy_ds->lock);
00542 }
00543
00544 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00545 {
00546 struct chanspy_ds *chanspy_ds = data;
00547
00548 ast_mutex_lock(&chanspy_ds->lock);
00549 chanspy_ds->chan = new_chan;
00550 ast_mutex_unlock(&chanspy_ds->lock);
00551 }
00552
00553 static const struct ast_datastore_info chanspy_ds_info = {
00554 .type = "chanspy",
00555 .destroy = chanspy_ds_destroy,
00556 .chan_fixup = chanspy_ds_chan_fixup,
00557 };
00558
00559 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
00560 {
00561 struct ast_channel *chan;
00562
00563 if (!chanspy_ds) {
00564 return NULL;
00565 }
00566
00567 ast_mutex_lock(&chanspy_ds->lock);
00568 while ((chan = chanspy_ds->chan)) {
00569 struct ast_datastore *datastore;
00570
00571 if (ast_channel_trylock(chan)) {
00572 DEADLOCK_AVOIDANCE(&chanspy_ds->lock);
00573 continue;
00574 }
00575 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00576 ast_channel_datastore_remove(chan, datastore);
00577
00578 chanspy_ds_destroy(datastore->data);
00579 datastore->data = NULL;
00580 ast_datastore_free(datastore);
00581 }
00582 ast_channel_unlock(chan);
00583 break;
00584 }
00585 ast_mutex_unlock(&chanspy_ds->lock);
00586
00587 return NULL;
00588 }
00589
00590
00591 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
00592 {
00593 struct ast_datastore *datastore = NULL;
00594
00595 ast_mutex_lock(&chanspy_ds->lock);
00596
00597 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00598 ast_mutex_unlock(&chanspy_ds->lock);
00599 chanspy_ds = chanspy_ds_free(chanspy_ds);
00600 ast_channel_unlock(chan);
00601 return NULL;
00602 }
00603
00604 chanspy_ds->chan = chan;
00605 datastore->data = chanspy_ds;
00606 ast_channel_datastore_add(chan, datastore);
00607
00608 return chanspy_ds;
00609 }
00610
00611 static struct chanspy_ds *next_channel(struct ast_channel *chan,
00612 const struct ast_channel *last, const char *spec,
00613 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
00614 {
00615 struct ast_channel *next;
00616 const size_t pseudo_len = strlen("DAHDI/pseudo");
00617
00618 redo:
00619 if (!ast_strlen_zero(spec))
00620 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00621 else if (!ast_strlen_zero(exten))
00622 next = ast_walk_channel_by_exten_locked(last, exten, context);
00623 else
00624 next = ast_channel_walk_locked(last);
00625
00626 if (!next)
00627 return NULL;
00628
00629 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00630 last = next;
00631 ast_channel_unlock(next);
00632 goto redo;
00633 } else if (next == chan) {
00634 last = next;
00635 ast_channel_unlock(next);
00636 goto redo;
00637 }
00638
00639 return setup_chanspy_ds(next, chanspy_ds);
00640 }
00641
00642 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
00643 int volfactor, const int fd, const char *mygroup, const char *myenforced,
00644 const char *spec, const char *exten, const char *context, const char *mailbox,
00645 const char *name_context)
00646 {
00647 char nameprefix[AST_NAME_STRLEN];
00648 char peer_name[AST_NAME_STRLEN + 5];
00649 char exitcontext[AST_MAX_CONTEXT] = "";
00650 signed char zero_volume = 0;
00651 int waitms;
00652 int res;
00653 char *ptr;
00654 int num;
00655 int num_spyed_upon = 1;
00656 struct chanspy_ds chanspy_ds = { 0, };
00657
00658 if (ast_test_flag(flags, OPTION_EXIT)) {
00659 const char *c;
00660 ast_channel_lock(chan);
00661 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
00662 ast_copy_string(exitcontext, c, sizeof(exitcontext));
00663 } else if (!ast_strlen_zero(chan->macrocontext)) {
00664 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00665 } else {
00666 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00667 }
00668 ast_channel_unlock(chan);
00669 }
00670
00671 ast_mutex_init(&chanspy_ds.lock);
00672
00673 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00674
00675 if (chan->_state != AST_STATE_UP)
00676 ast_answer(chan);
00677
00678 ast_set_flag(chan, AST_FLAG_SPYING);
00679
00680 waitms = 100;
00681
00682 for (;;) {
00683 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00684 struct ast_channel *prev = NULL, *peer = NULL;
00685
00686 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00687 res = ast_streamfile(chan, "beep", chan->language);
00688 if (!res)
00689 res = ast_waitstream(chan, "");
00690 else if (res < 0) {
00691 ast_clear_flag(chan, AST_FLAG_SPYING);
00692 break;
00693 }
00694 if (!ast_strlen_zero(exitcontext)) {
00695 char tmp[2];
00696 tmp[0] = res;
00697 tmp[1] = '\0';
00698 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00699 goto exit;
00700 else
00701 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00702 }
00703 }
00704
00705 res = ast_waitfordigit(chan, waitms);
00706 if (res < 0) {
00707 ast_clear_flag(chan, AST_FLAG_SPYING);
00708 break;
00709 }
00710 if (!ast_strlen_zero(exitcontext)) {
00711 char tmp[2];
00712 tmp[0] = res;
00713 tmp[1] = '\0';
00714 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00715 goto exit;
00716 else
00717 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00718 }
00719
00720
00721 waitms = 100;
00722 num_spyed_upon = 0;
00723
00724 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00725 peer_chanspy_ds;
00726 chanspy_ds_free(peer_chanspy_ds), prev = peer,
00727 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
00728 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00729 int igrp = !mygroup;
00730 int ienf = !myenforced;
00731 char *s;
00732
00733 peer = peer_chanspy_ds->chan;
00734
00735 ast_mutex_unlock(&peer_chanspy_ds->lock);
00736
00737 if (peer == prev) {
00738 ast_channel_unlock(peer);
00739 chanspy_ds_free(peer_chanspy_ds);
00740 break;
00741 }
00742
00743 if (ast_check_hangup(chan)) {
00744 ast_channel_unlock(peer);
00745 chanspy_ds_free(peer_chanspy_ds);
00746 break;
00747 }
00748
00749 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00750 ast_channel_unlock(peer);
00751 continue;
00752 }
00753
00754 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00755 ast_channel_unlock(peer);
00756 continue;
00757 }
00758
00759 if (mygroup) {
00760 int num_groups = 0;
00761 int num_mygroups = 0;
00762 char dup_group[512];
00763 char dup_mygroup[512];
00764 char *groups[NUM_SPYGROUPS];
00765 char *mygroups[NUM_SPYGROUPS];
00766 const char *group;
00767 int x;
00768 int y;
00769 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
00770 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
00771 ARRAY_LEN(mygroups));
00772
00773 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00774 ast_copy_string(dup_group, group, sizeof(dup_group));
00775 num_groups = ast_app_separate_args(dup_group, ':', groups,
00776 ARRAY_LEN(groups));
00777 }
00778
00779 for (y = 0; y < num_mygroups; y++) {
00780 for (x = 0; x < num_groups; x++) {
00781 if (!strcmp(mygroups[y], groups[x])) {
00782 igrp = 1;
00783 break;
00784 }
00785 }
00786 }
00787 }
00788
00789 if (!igrp) {
00790 ast_channel_unlock(peer);
00791 continue;
00792 }
00793
00794 if (myenforced) {
00795 char ext[AST_CHANNEL_NAME + 3];
00796 char buffer[512];
00797 char *end;
00798
00799 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00800
00801 ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00802 if ((end = strchr(ext, '-'))) {
00803 *end++ = ':';
00804 *end = '\0';
00805 }
00806
00807 ext[0] = ':';
00808
00809 if (strcasestr(buffer, ext)) {
00810 ienf = 1;
00811 }
00812 }
00813
00814 if (!ienf) {
00815 continue;
00816 }
00817
00818 strcpy(peer_name, "spy-");
00819 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00820 ptr = strchr(peer_name, '/');
00821 *ptr++ = '\0';
00822 ptr = strsep(&ptr, "-");
00823
00824 for (s = peer_name; s < ptr; s++)
00825 *s = tolower(*s);
00826
00827
00828
00829
00830 ast_channel_unlock(peer);
00831
00832 if (!ast_test_flag(flags, OPTION_QUIET)) {
00833 if (ast_test_flag(flags, OPTION_NAME)) {
00834 const char *local_context = S_OR(name_context, "default");
00835 const char *local_mailbox = S_OR(mailbox, ptr);
00836 res = ast_app_sayname(chan, local_mailbox, local_context);
00837 }
00838 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
00839 if (!ast_test_flag(flags, OPTION_NOTECH)) {
00840 if (ast_fileexists(peer_name, NULL, NULL) != -1) {
00841 res = ast_streamfile(chan, peer_name, chan->language);
00842 if (!res) {
00843 res = ast_waitstream(chan, "");
00844 }
00845 if (res) {
00846 chanspy_ds_free(peer_chanspy_ds);
00847 break;
00848 }
00849 } else {
00850 res = ast_say_character_str(chan, peer_name, "", chan->language);
00851 }
00852 }
00853 if ((num = atoi(ptr)))
00854 ast_say_digits(chan, atoi(ptr), "", chan->language);
00855 }
00856 }
00857
00858 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00859 num_spyed_upon++;
00860
00861 if (res == -1) {
00862 chanspy_ds_free(peer_chanspy_ds);
00863 goto exit;
00864 } else if (res == -2) {
00865 res = 0;
00866 chanspy_ds_free(peer_chanspy_ds);
00867 goto exit;
00868 } else if (res > 1 && spec) {
00869 struct ast_channel *next;
00870
00871 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00872
00873 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
00874 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
00875 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
00876 } else {
00877
00878
00879 ast_mutex_lock(&peer_chanspy_ds->lock);
00880 if (peer_chanspy_ds->chan) {
00881 ast_channel_lock(peer_chanspy_ds->chan);
00882 next_chanspy_ds = peer_chanspy_ds;
00883 peer_chanspy_ds = NULL;
00884 } else {
00885
00886 ast_mutex_unlock(&peer_chanspy_ds->lock);
00887 next_chanspy_ds = NULL;
00888 }
00889 }
00890
00891 peer = NULL;
00892 }
00893 }
00894 if (res == -1 || ast_check_hangup(chan))
00895 break;
00896 }
00897 exit:
00898
00899 ast_clear_flag(chan, AST_FLAG_SPYING);
00900
00901 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
00902
00903 ast_mutex_lock(&chanspy_ds.lock);
00904 ast_mutex_unlock(&chanspy_ds.lock);
00905 ast_mutex_destroy(&chanspy_ds.lock);
00906
00907 return res;
00908 }
00909
00910 static int chanspy_exec(struct ast_channel *chan, void *data)
00911 {
00912 char *myenforced = NULL;
00913 char *mygroup = NULL;
00914 char *recbase = NULL;
00915 int fd = 0;
00916 struct ast_flags flags;
00917 int oldwf = 0;
00918 int volfactor = 0;
00919 int res;
00920 char *mailbox = NULL;
00921 char *name_context = NULL;
00922 AST_DECLARE_APP_ARGS(args,
00923 AST_APP_ARG(spec);
00924 AST_APP_ARG(options);
00925 );
00926 char *opts[OPT_ARG_ARRAY_SIZE];
00927
00928 data = ast_strdupa(data);
00929 AST_STANDARD_APP_ARGS(args, data);
00930
00931 if (args.spec && !strcmp(args.spec, "all"))
00932 args.spec = NULL;
00933
00934 if (args.options) {
00935 ast_app_parse_options(spy_opts, &flags, opts, args.options);
00936 if (ast_test_flag(&flags, OPTION_GROUP))
00937 mygroup = opts[OPT_ARG_GROUP];
00938
00939 if (ast_test_flag(&flags, OPTION_RECORD) &&
00940 !(recbase = opts[OPT_ARG_RECORD]))
00941 recbase = "chanspy";
00942
00943 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
00944 int vol;
00945
00946 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
00947 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
00948 else
00949 volfactor = vol;
00950 }
00951
00952 if (ast_test_flag(&flags, OPTION_PRIVATE))
00953 ast_set_flag(&flags, OPTION_WHISPER);
00954
00955 if (ast_test_flag(&flags, OPTION_ENFORCED))
00956 myenforced = opts[OPT_ARG_ENFORCED];
00957
00958 if (ast_test_flag(&flags, OPTION_NAME)) {
00959 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
00960 char *delimiter;
00961 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
00962 mailbox = opts[OPT_ARG_NAME];
00963 *delimiter++ = '\0';
00964 name_context = delimiter;
00965 } else {
00966 mailbox = opts[OPT_ARG_NAME];
00967 }
00968 }
00969 }
00970
00971
00972 } else
00973 ast_clear_flag(&flags, AST_FLAGS_ALL);
00974
00975 oldwf = chan->writeformat;
00976 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
00977 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00978 return -1;
00979 }
00980
00981 if (recbase) {
00982 char filename[PATH_MAX];
00983
00984 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
00985 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
00986 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
00987 fd = 0;
00988 }
00989 }
00990
00991 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
00992
00993 if (fd)
00994 close(fd);
00995
00996 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
00997 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
00998
00999 return res;
01000 }
01001
01002 static int extenspy_exec(struct ast_channel *chan, void *data)
01003 {
01004 char *ptr, *exten = NULL;
01005 char *mygroup = NULL;
01006 char *recbase = NULL;
01007 int fd = 0;
01008 struct ast_flags flags;
01009 int oldwf = 0;
01010 int volfactor = 0;
01011 int res;
01012 char *mailbox = NULL;
01013 char *name_context = NULL;
01014 AST_DECLARE_APP_ARGS(args,
01015 AST_APP_ARG(context);
01016 AST_APP_ARG(options);
01017 );
01018
01019 data = ast_strdupa(data);
01020
01021 AST_STANDARD_APP_ARGS(args, data);
01022 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
01023 exten = args.context;
01024 *ptr++ = '\0';
01025 args.context = ptr;
01026 }
01027
01028 if (ast_strlen_zero(args.context))
01029 args.context = ast_strdupa(chan->context);
01030
01031 if (args.options) {
01032 char *opts[OPT_ARG_ARRAY_SIZE];
01033
01034 ast_app_parse_options(spy_opts, &flags, opts, args.options);
01035 if (ast_test_flag(&flags, OPTION_GROUP))
01036 mygroup = opts[OPT_ARG_GROUP];
01037
01038 if (ast_test_flag(&flags, OPTION_RECORD) &&
01039 !(recbase = opts[OPT_ARG_RECORD]))
01040 recbase = "chanspy";
01041
01042 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01043 int vol;
01044
01045 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01046 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01047 else
01048 volfactor = vol;
01049 }
01050
01051 if (ast_test_flag(&flags, OPTION_PRIVATE))
01052 ast_set_flag(&flags, OPTION_WHISPER);
01053
01054
01055 if (ast_test_flag(&flags, OPTION_NAME)) {
01056 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01057 char *delimiter;
01058 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01059 mailbox = opts[OPT_ARG_NAME];
01060 *delimiter++ = '\0';
01061 name_context = delimiter;
01062 } else {
01063 mailbox = opts[OPT_ARG_NAME];
01064 }
01065 }
01066 }
01067
01068 } else
01069 ast_clear_flag(&flags, AST_FLAGS_ALL);
01070
01071 oldwf = chan->writeformat;
01072 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01073 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01074 return -1;
01075 }
01076
01077 if (recbase) {
01078 char filename[PATH_MAX];
01079
01080 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01081 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01082 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01083 fd = 0;
01084 }
01085 }
01086
01087
01088 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
01089
01090 if (fd)
01091 close(fd);
01092
01093 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01094 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01095
01096 return res;
01097 }
01098
01099 static int unload_module(void)
01100 {
01101 int res = 0;
01102
01103 res |= ast_unregister_application(app_chan);
01104 res |= ast_unregister_application(app_ext);
01105
01106 return res;
01107 }
01108
01109 static int load_module(void)
01110 {
01111 int res = 0;
01112
01113 res |= ast_register_application(app_chan, chanspy_exec, tdesc, desc_chan);
01114 res |= ast_register_application(app_ext, extenspy_exec, tdesc, desc_ext);
01115
01116 return res;
01117 }
01118
01119 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");