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