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: 320506 $")
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/manager.h"
00050 #include "asterisk/module.h"
00051 #include "asterisk/lock.h"
00052 #include "asterisk/options.h"
00053
00054 #define AST_NAME_STRLEN 256
00055 #define NUM_SPYGROUPS 128
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292 static const char *app_chan = "ChanSpy";
00293
00294 static const char *app_ext = "ExtenSpy";
00295
00296 enum {
00297 OPTION_QUIET = (1 << 0),
00298 OPTION_BRIDGED = (1 << 1),
00299 OPTION_VOLUME = (1 << 2),
00300 OPTION_GROUP = (1 << 3),
00301 OPTION_RECORD = (1 << 4),
00302 OPTION_WHISPER = (1 << 5),
00303 OPTION_PRIVATE = (1 << 6),
00304 OPTION_READONLY = (1 << 7),
00305 OPTION_EXIT = (1 << 8),
00306 OPTION_ENFORCED = (1 << 9),
00307 OPTION_NOTECH = (1 << 10),
00308 OPTION_BARGE = (1 << 11),
00309 OPTION_NAME = (1 << 12),
00310 OPTION_DTMF_SWITCH_MODES = (1 << 13),
00311 } chanspy_opt_flags;
00312
00313 enum {
00314 OPT_ARG_VOLUME = 0,
00315 OPT_ARG_GROUP,
00316 OPT_ARG_RECORD,
00317 OPT_ARG_ENFORCED,
00318 OPT_ARG_NAME,
00319 OPT_ARG_ARRAY_SIZE,
00320 } chanspy_opt_args;
00321
00322 AST_APP_OPTIONS(spy_opts, {
00323 AST_APP_OPTION('q', OPTION_QUIET),
00324 AST_APP_OPTION('b', OPTION_BRIDGED),
00325 AST_APP_OPTION('B', OPTION_BARGE),
00326 AST_APP_OPTION('w', OPTION_WHISPER),
00327 AST_APP_OPTION('W', OPTION_PRIVATE),
00328 AST_APP_OPTION_ARG('v', OPTION_VOLUME, OPT_ARG_VOLUME),
00329 AST_APP_OPTION_ARG('g', OPTION_GROUP, OPT_ARG_GROUP),
00330 AST_APP_OPTION_ARG('r', OPTION_RECORD, OPT_ARG_RECORD),
00331 AST_APP_OPTION_ARG('e', OPTION_ENFORCED, OPT_ARG_ENFORCED),
00332 AST_APP_OPTION('o', OPTION_READONLY),
00333 AST_APP_OPTION('X', OPTION_EXIT),
00334 AST_APP_OPTION('s', OPTION_NOTECH),
00335 AST_APP_OPTION_ARG('n', OPTION_NAME, OPT_ARG_NAME),
00336 AST_APP_OPTION('d', OPTION_DTMF_SWITCH_MODES),
00337 });
00338
00339 static int next_unique_id_to_use = 0;
00340
00341 struct chanspy_translation_helper {
00342
00343 struct ast_audiohook spy_audiohook;
00344 struct ast_audiohook whisper_audiohook;
00345 struct ast_audiohook bridge_whisper_audiohook;
00346 int fd;
00347 int volfactor;
00348 struct ast_flags flags;
00349 };
00350
00351 static void *spy_alloc(struct ast_channel *chan, void *data)
00352 {
00353
00354 return data;
00355 }
00356
00357 static void spy_release(struct ast_channel *chan, void *data)
00358 {
00359
00360 }
00361
00362 static int spy_generate(struct ast_channel *chan, void *data, int len, int samples)
00363 {
00364 struct chanspy_translation_helper *csth = data;
00365 struct ast_frame *f, *cur;
00366
00367 ast_audiohook_lock(&csth->spy_audiohook);
00368 if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
00369
00370 ast_audiohook_unlock(&csth->spy_audiohook);
00371 return -1;
00372 }
00373
00374 if (ast_test_flag(&csth->flags, OPTION_READONLY)) {
00375
00376 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_READ, AST_FORMAT_SLINEAR);
00377 } else {
00378 f = ast_audiohook_read_frame(&csth->spy_audiohook, samples, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR);
00379 }
00380
00381 ast_audiohook_unlock(&csth->spy_audiohook);
00382
00383 if (!f)
00384 return 0;
00385
00386 for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
00387 if (ast_write(chan, cur)) {
00388 ast_frfree(f);
00389 return -1;
00390 }
00391
00392 if (csth->fd) {
00393 if (write(csth->fd, cur->data.ptr, cur->datalen) < 0) {
00394 ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno));
00395 }
00396 }
00397 }
00398
00399 ast_frfree(f);
00400
00401 return 0;
00402 }
00403
00404 static struct ast_generator spygen = {
00405 .alloc = spy_alloc,
00406 .release = spy_release,
00407 .generate = spy_generate,
00408 };
00409
00410 static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook)
00411 {
00412 int res = 0;
00413 struct ast_channel *peer = NULL;
00414
00415 ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name);
00416
00417 ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE);
00418 res = ast_audiohook_attach(chan, audiohook);
00419
00420 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) {
00421 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
00422 }
00423 return res;
00424 }
00425
00426 struct chanspy_ds {
00427 struct ast_channel *chan;
00428 char unique_id[20];
00429 ast_mutex_t lock;
00430 };
00431
00432 static void change_spy_mode(const char digit, struct ast_flags *flags)
00433 {
00434 if (digit == '4') {
00435 ast_clear_flag(flags, OPTION_WHISPER);
00436 ast_clear_flag(flags, OPTION_BARGE);
00437 } else if (digit == '5') {
00438 ast_clear_flag(flags, OPTION_BARGE);
00439 ast_set_flag(flags, OPTION_WHISPER);
00440 } else if (digit == '6') {
00441 ast_clear_flag(flags, OPTION_WHISPER);
00442 ast_set_flag(flags, OPTION_BARGE);
00443 }
00444 }
00445
00446 static int channel_spy(struct ast_channel *chan, struct chanspy_ds *spyee_chanspy_ds,
00447 int *volfactor, int fd, struct ast_flags *flags, char *exitcontext)
00448 {
00449 struct chanspy_translation_helper csth;
00450 int running = 0, res, x = 0;
00451 char inp[24] = {0};
00452 char *name;
00453 struct ast_frame *f;
00454 struct ast_silence_generator *silgen = NULL;
00455 struct ast_channel *spyee = NULL, *spyee_bridge = NULL;
00456 const char *spyer_name;
00457
00458 ast_channel_lock(chan);
00459 spyer_name = ast_strdupa(chan->name);
00460 ast_channel_unlock(chan);
00461
00462 ast_mutex_lock(&spyee_chanspy_ds->lock);
00463 while ((spyee = spyee_chanspy_ds->chan) && ast_channel_trylock(spyee)) {
00464
00465
00466 DEADLOCK_AVOIDANCE(&spyee_chanspy_ds->lock);
00467 }
00468 ast_mutex_unlock(&spyee_chanspy_ds->lock);
00469
00470 if (!spyee) {
00471 return 0;
00472 }
00473
00474
00475
00476 if (ast_check_hangup(chan) || ast_check_hangup(spyee)) {
00477 ast_channel_unlock(spyee);
00478 return 0;
00479 }
00480
00481 name = ast_strdupa(spyee->name);
00482
00483 ast_verb(2, "Spying on channel %s\n", name);
00484 manager_event(EVENT_FLAG_CALL, "ChanSpyStart",
00485 "SpyerChannel: %s\r\n"
00486 "SpyeeChannel: %s\r\n",
00487 spyer_name, name);
00488
00489 memset(&csth, 0, sizeof(csth));
00490 ast_copy_flags(&csth.flags, flags, AST_FLAGS_ALL);
00491
00492 ast_audiohook_init(&csth.spy_audiohook, AST_AUDIOHOOK_TYPE_SPY, "ChanSpy");
00493
00494 if (start_spying(spyee, spyer_name, &csth.spy_audiohook)) {
00495 ast_audiohook_destroy(&csth.spy_audiohook);
00496 ast_channel_unlock(spyee);
00497 return 0;
00498 }
00499
00500 ast_audiohook_init(&csth.whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "ChanSpy");
00501 ast_audiohook_init(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_TYPE_WHISPER, "Chanspy");
00502 if (start_spying(spyee, spyer_name, &csth.whisper_audiohook)) {
00503 ast_log(LOG_WARNING, "Unable to attach whisper audiohook to spyee %s. Whisper mode disabled!\n", spyee->name);
00504 }
00505 if ((spyee_bridge = ast_bridged_channel(spyee))) {
00506 ast_channel_lock(spyee_bridge);
00507 if (start_spying(spyee_bridge, spyer_name, &csth.bridge_whisper_audiohook)) {
00508 ast_log(LOG_WARNING, "Unable to attach barge audiohook on spyee %s. Barge mode disabled!\n", spyee->name);
00509 }
00510 ast_channel_unlock(spyee_bridge);
00511 }
00512 ast_channel_unlock(spyee);
00513 spyee = NULL;
00514
00515 ast_channel_lock(chan);
00516 ast_set_flag(chan, AST_FLAG_END_DTMF_ONLY);
00517 ast_channel_unlock(chan);
00518
00519 csth.volfactor = *volfactor;
00520
00521 if (csth.volfactor) {
00522 csth.spy_audiohook.options.read_volume = csth.volfactor;
00523 csth.spy_audiohook.options.write_volume = csth.volfactor;
00524 }
00525
00526 csth.fd = fd;
00527
00528 if (ast_test_flag(flags, OPTION_PRIVATE))
00529 silgen = ast_channel_start_silence_generator(chan);
00530 else
00531 ast_activate_generator(chan, &spygen, &csth);
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547 while ((res = ast_waitfor(chan, -1) > -1) && csth.spy_audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) {
00548 if (!(f = ast_read(chan)) || ast_check_hangup(chan)) {
00549 running = -1;
00550 break;
00551 }
00552
00553 if (ast_test_flag(flags, OPTION_BARGE) && f->frametype == AST_FRAME_VOICE) {
00554 ast_audiohook_lock(&csth.whisper_audiohook);
00555 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00556 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00557 ast_audiohook_write_frame(&csth.bridge_whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00558 ast_audiohook_unlock(&csth.whisper_audiohook);
00559 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00560 ast_frfree(f);
00561 continue;
00562 } else if (ast_test_flag(flags, OPTION_WHISPER) && f->frametype == AST_FRAME_VOICE) {
00563 ast_audiohook_lock(&csth.whisper_audiohook);
00564 ast_audiohook_write_frame(&csth.whisper_audiohook, AST_AUDIOHOOK_DIRECTION_WRITE, f);
00565 ast_audiohook_unlock(&csth.whisper_audiohook);
00566 ast_frfree(f);
00567 continue;
00568 }
00569
00570 res = (f->frametype == AST_FRAME_DTMF) ? f->subclass : 0;
00571 ast_frfree(f);
00572 if (!res)
00573 continue;
00574
00575 if (x == sizeof(inp))
00576 x = 0;
00577
00578 if (res < 0) {
00579 running = -1;
00580 break;
00581 }
00582
00583 if (ast_test_flag(flags, OPTION_EXIT)) {
00584 char tmp[2];
00585 tmp[0] = res;
00586 tmp[1] = '\0';
00587 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1)) {
00588 ast_debug(1, "Got DTMF %c, goto context %s\n", tmp[0], exitcontext);
00589 pbx_builtin_setvar_helper(chan, "SPY_CHANNEL", name);
00590 running = -2;
00591 break;
00592 } else {
00593 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00594 }
00595 } else if (res >= '0' && res <= '9') {
00596 if (ast_test_flag(flags, OPTION_DTMF_SWITCH_MODES)) {
00597 change_spy_mode(res, flags);
00598 } else {
00599 inp[x++] = res;
00600 }
00601 }
00602
00603 if (res == '*') {
00604 running = 0;
00605 break;
00606 } else if (res == '#') {
00607 if (!ast_strlen_zero(inp)) {
00608 running = atoi(inp);
00609 break;
00610 }
00611
00612 (*volfactor)++;
00613 if (*volfactor > 4)
00614 *volfactor = -4;
00615 ast_verb(3, "Setting spy volume on %s to %d\n", chan->name, *volfactor);
00616
00617 csth.volfactor = *volfactor;
00618 csth.spy_audiohook.options.read_volume = csth.volfactor;
00619 csth.spy_audiohook.options.write_volume = csth.volfactor;
00620 }
00621 }
00622
00623 if (ast_test_flag(flags, OPTION_PRIVATE))
00624 ast_channel_stop_silence_generator(chan, silgen);
00625 else
00626 ast_deactivate_generator(chan);
00627
00628 ast_channel_lock(chan);
00629 ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY);
00630 ast_channel_unlock(chan);
00631
00632 ast_audiohook_lock(&csth.whisper_audiohook);
00633 ast_audiohook_detach(&csth.whisper_audiohook);
00634 ast_audiohook_unlock(&csth.whisper_audiohook);
00635 ast_audiohook_destroy(&csth.whisper_audiohook);
00636
00637 ast_audiohook_lock(&csth.bridge_whisper_audiohook);
00638 ast_audiohook_detach(&csth.bridge_whisper_audiohook);
00639 ast_audiohook_unlock(&csth.bridge_whisper_audiohook);
00640 ast_audiohook_destroy(&csth.bridge_whisper_audiohook);
00641
00642 ast_audiohook_lock(&csth.spy_audiohook);
00643 ast_audiohook_detach(&csth.spy_audiohook);
00644 ast_audiohook_unlock(&csth.spy_audiohook);
00645 ast_audiohook_destroy(&csth.spy_audiohook);
00646
00647 ast_verb(2, "Done Spying on channel %s\n", name);
00648 manager_event(EVENT_FLAG_CALL, "ChanSpyStop", "SpyeeChannel: %s\r\n", name);
00649
00650 return running;
00651 }
00652
00653
00654
00655
00656
00657 static void chanspy_ds_destroy(void *data)
00658 {
00659 struct chanspy_ds *chanspy_ds = data;
00660
00661
00662
00663
00664
00665 ast_mutex_lock(&chanspy_ds->lock);
00666 chanspy_ds->chan = NULL;
00667 ast_mutex_unlock(&chanspy_ds->lock);
00668 }
00669
00670 static void chanspy_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
00671 {
00672 struct chanspy_ds *chanspy_ds = data;
00673
00674 ast_mutex_lock(&chanspy_ds->lock);
00675 chanspy_ds->chan = new_chan;
00676 ast_mutex_unlock(&chanspy_ds->lock);
00677 }
00678
00679 static const struct ast_datastore_info chanspy_ds_info = {
00680 .type = "chanspy",
00681 .destroy = chanspy_ds_destroy,
00682 .chan_fixup = chanspy_ds_chan_fixup,
00683 };
00684
00685 static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds)
00686 {
00687 struct ast_channel *chan;
00688
00689 if (!chanspy_ds) {
00690 return NULL;
00691 }
00692
00693 ast_mutex_lock(&chanspy_ds->lock);
00694 while ((chan = chanspy_ds->chan)) {
00695 struct ast_datastore *datastore;
00696
00697 if (ast_channel_trylock(chan)) {
00698 DEADLOCK_AVOIDANCE(&chanspy_ds->lock);
00699 continue;
00700 }
00701 if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) {
00702 ast_channel_datastore_remove(chan, datastore);
00703
00704 chanspy_ds_destroy(datastore->data);
00705 datastore->data = NULL;
00706 ast_datastore_free(datastore);
00707 }
00708 ast_channel_unlock(chan);
00709 break;
00710 }
00711 ast_mutex_unlock(&chanspy_ds->lock);
00712
00713 return NULL;
00714 }
00715
00716
00717 static struct chanspy_ds *setup_chanspy_ds(struct ast_channel *chan, struct chanspy_ds *chanspy_ds)
00718 {
00719 struct ast_datastore *datastore = NULL;
00720
00721 ast_mutex_lock(&chanspy_ds->lock);
00722
00723 if (!(datastore = ast_datastore_alloc(&chanspy_ds_info, chanspy_ds->unique_id))) {
00724 ast_mutex_unlock(&chanspy_ds->lock);
00725 chanspy_ds = chanspy_ds_free(chanspy_ds);
00726 ast_channel_unlock(chan);
00727 return NULL;
00728 }
00729
00730 chanspy_ds->chan = chan;
00731 datastore->data = chanspy_ds;
00732 ast_channel_datastore_add(chan, datastore);
00733
00734 return chanspy_ds;
00735 }
00736
00737 static struct chanspy_ds *next_channel(struct ast_channel *chan,
00738 const struct ast_channel *last, const char *spec,
00739 const char *exten, const char *context, struct chanspy_ds *chanspy_ds)
00740 {
00741 struct ast_channel *next;
00742 const size_t pseudo_len = strlen("DAHDI/pseudo");
00743
00744 redo:
00745 if (!ast_strlen_zero(spec))
00746 next = ast_walk_channel_by_name_prefix_locked(last, spec, strlen(spec));
00747 else if (!ast_strlen_zero(exten))
00748 next = ast_walk_channel_by_exten_locked(last, exten, context);
00749 else
00750 next = ast_channel_walk_locked(last);
00751
00752 if (!next)
00753 return NULL;
00754
00755 if (!strncmp(next->name, "DAHDI/pseudo", pseudo_len)) {
00756 last = next;
00757 ast_channel_unlock(next);
00758 goto redo;
00759 } else if (next == chan) {
00760 last = next;
00761 ast_channel_unlock(next);
00762 goto redo;
00763 }
00764
00765 return setup_chanspy_ds(next, chanspy_ds);
00766 }
00767
00768 static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
00769 int volfactor, const int fd, const char *mygroup, const char *myenforced,
00770 const char *spec, const char *exten, const char *context, const char *mailbox,
00771 const char *name_context)
00772 {
00773 char nameprefix[AST_NAME_STRLEN];
00774 char peer_name[AST_NAME_STRLEN + 5];
00775 char exitcontext[AST_MAX_CONTEXT] = "";
00776 signed char zero_volume = 0;
00777 int waitms;
00778 int res;
00779 char *ptr;
00780 int num;
00781 int num_spyed_upon = 1;
00782 struct chanspy_ds chanspy_ds = { 0, };
00783
00784 if (ast_test_flag(flags, OPTION_EXIT)) {
00785 const char *c;
00786 ast_channel_lock(chan);
00787 if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
00788 ast_copy_string(exitcontext, c, sizeof(exitcontext));
00789 } else if (!ast_strlen_zero(chan->macrocontext)) {
00790 ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
00791 } else {
00792 ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
00793 }
00794 ast_channel_unlock(chan);
00795 }
00796
00797 ast_mutex_init(&chanspy_ds.lock);
00798
00799 snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));
00800
00801 if (chan->_state != AST_STATE_UP)
00802 ast_answer(chan);
00803
00804 ast_set_flag(chan, AST_FLAG_SPYING);
00805
00806 waitms = 100;
00807
00808 for (;;) {
00809 struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
00810 struct ast_channel *prev = NULL, *peer = NULL;
00811
00812 if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
00813 res = ast_streamfile(chan, "beep", chan->language);
00814 if (!res)
00815 res = ast_waitstream(chan, "");
00816 else if (res < 0) {
00817 ast_clear_flag(chan, AST_FLAG_SPYING);
00818 break;
00819 }
00820 if (!ast_strlen_zero(exitcontext)) {
00821 char tmp[2];
00822 tmp[0] = res;
00823 tmp[1] = '\0';
00824 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00825 goto exit;
00826 else
00827 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00828 }
00829 }
00830
00831 res = ast_waitfordigit(chan, waitms);
00832 if (res < 0) {
00833 ast_clear_flag(chan, AST_FLAG_SPYING);
00834 break;
00835 }
00836 if (!ast_strlen_zero(exitcontext)) {
00837 char tmp[2];
00838 tmp[0] = res;
00839 tmp[1] = '\0';
00840 if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
00841 goto exit;
00842 else
00843 ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
00844 }
00845
00846
00847 waitms = 100;
00848 num_spyed_upon = 0;
00849
00850 for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
00851 peer_chanspy_ds;
00852 chanspy_ds_free(peer_chanspy_ds), prev = peer,
00853 peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds :
00854 next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
00855 int igrp = !mygroup;
00856 int ienf = !myenforced;
00857 char *s;
00858
00859 peer = peer_chanspy_ds->chan;
00860
00861 ast_mutex_unlock(&peer_chanspy_ds->lock);
00862
00863 if (peer == prev) {
00864 ast_channel_unlock(peer);
00865 chanspy_ds_free(peer_chanspy_ds);
00866 break;
00867 }
00868
00869 if (ast_check_hangup(chan)) {
00870 ast_channel_unlock(peer);
00871 chanspy_ds_free(peer_chanspy_ds);
00872 break;
00873 }
00874
00875 if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
00876 ast_channel_unlock(peer);
00877 continue;
00878 }
00879
00880 if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
00881 ast_channel_unlock(peer);
00882 continue;
00883 }
00884
00885 if (mygroup) {
00886 int num_groups = 0;
00887 int num_mygroups = 0;
00888 char dup_group[512];
00889 char dup_mygroup[512];
00890 char *groups[NUM_SPYGROUPS];
00891 char *mygroups[NUM_SPYGROUPS];
00892 const char *group;
00893 int x;
00894 int y;
00895 ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
00896 num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
00897 ARRAY_LEN(mygroups));
00898
00899 if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
00900 ast_copy_string(dup_group, group, sizeof(dup_group));
00901 num_groups = ast_app_separate_args(dup_group, ':', groups,
00902 ARRAY_LEN(groups));
00903 }
00904
00905 for (y = 0; y < num_mygroups; y++) {
00906 for (x = 0; x < num_groups; x++) {
00907 if (!strcmp(mygroups[y], groups[x])) {
00908 igrp = 1;
00909 break;
00910 }
00911 }
00912 }
00913 }
00914
00915 if (!igrp) {
00916 ast_channel_unlock(peer);
00917 continue;
00918 }
00919
00920 if (myenforced) {
00921 char ext[AST_CHANNEL_NAME + 3];
00922 char buffer[512];
00923 char *end;
00924
00925 snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
00926
00927 ast_copy_string(ext + 1, peer->name, sizeof(ext) - 1);
00928 if ((end = strchr(ext, '-'))) {
00929 *end++ = ':';
00930 *end = '\0';
00931 }
00932
00933 ext[0] = ':';
00934
00935 if (strcasestr(buffer, ext)) {
00936 ienf = 1;
00937 }
00938 }
00939
00940 if (!ienf) {
00941 ast_channel_unlock(peer);
00942 continue;
00943 }
00944
00945 strcpy(peer_name, "spy-");
00946 strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
00947 ptr = strchr(peer_name, '/');
00948 *ptr++ = '\0';
00949 ptr = strsep(&ptr, "-");
00950
00951 for (s = peer_name; s < ptr; s++)
00952 *s = tolower(*s);
00953
00954
00955
00956
00957 ast_channel_unlock(peer);
00958
00959 if (!ast_test_flag(flags, OPTION_QUIET)) {
00960 if (ast_test_flag(flags, OPTION_NAME)) {
00961 const char *local_context = S_OR(name_context, "default");
00962 const char *local_mailbox = S_OR(mailbox, ptr);
00963 res = ast_app_sayname(chan, local_mailbox, local_context);
00964 }
00965 if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
00966 if (!ast_test_flag(flags, OPTION_NOTECH)) {
00967 if (ast_fileexists(peer_name, NULL, NULL) > 0) {
00968 res = ast_streamfile(chan, peer_name, chan->language);
00969 if (!res) {
00970 res = ast_waitstream(chan, "");
00971 }
00972 if (res) {
00973 chanspy_ds_free(peer_chanspy_ds);
00974 break;
00975 }
00976 } else {
00977 res = ast_say_character_str(chan, peer_name, "", chan->language);
00978 }
00979 }
00980 if ((num = atoi(ptr)))
00981 ast_say_digits(chan, atoi(ptr), "", chan->language);
00982 }
00983 }
00984
00985 res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
00986 num_spyed_upon++;
00987
00988 if (res == -1) {
00989 chanspy_ds_free(peer_chanspy_ds);
00990 goto exit;
00991 } else if (res == -2) {
00992 res = 0;
00993 chanspy_ds_free(peer_chanspy_ds);
00994 goto exit;
00995 } else if (res > 1 && spec) {
00996 struct ast_channel *next;
00997
00998 snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
00999
01000 if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
01001 peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
01002 next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
01003 } else {
01004
01005
01006 ast_mutex_lock(&peer_chanspy_ds->lock);
01007 if (peer_chanspy_ds->chan) {
01008 ast_channel_lock(peer_chanspy_ds->chan);
01009 next_chanspy_ds = peer_chanspy_ds;
01010 peer_chanspy_ds = NULL;
01011 } else {
01012
01013 ast_mutex_unlock(&peer_chanspy_ds->lock);
01014 next_chanspy_ds = NULL;
01015 }
01016 }
01017
01018 peer = NULL;
01019 }
01020 }
01021 if (res == -1 || ast_check_hangup(chan))
01022 break;
01023 }
01024 exit:
01025
01026 ast_clear_flag(chan, AST_FLAG_SPYING);
01027
01028 ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
01029
01030 ast_mutex_lock(&chanspy_ds.lock);
01031 ast_mutex_unlock(&chanspy_ds.lock);
01032 ast_mutex_destroy(&chanspy_ds.lock);
01033
01034 return res;
01035 }
01036
01037 static int chanspy_exec(struct ast_channel *chan, void *data)
01038 {
01039 char *myenforced = NULL;
01040 char *mygroup = NULL;
01041 char *recbase = NULL;
01042 int fd = 0;
01043 struct ast_flags flags;
01044 int oldwf = 0;
01045 int volfactor = 0;
01046 int res;
01047 char *mailbox = NULL;
01048 char *name_context = NULL;
01049 AST_DECLARE_APP_ARGS(args,
01050 AST_APP_ARG(spec);
01051 AST_APP_ARG(options);
01052 );
01053 char *opts[OPT_ARG_ARRAY_SIZE];
01054
01055 data = ast_strdupa(data);
01056 AST_STANDARD_APP_ARGS(args, data);
01057
01058 if (args.spec && !strcmp(args.spec, "all"))
01059 args.spec = NULL;
01060
01061 if (args.options) {
01062 ast_app_parse_options(spy_opts, &flags, opts, args.options);
01063 if (ast_test_flag(&flags, OPTION_GROUP))
01064 mygroup = opts[OPT_ARG_GROUP];
01065
01066 if (ast_test_flag(&flags, OPTION_RECORD) &&
01067 !(recbase = opts[OPT_ARG_RECORD]))
01068 recbase = "chanspy";
01069
01070 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01071 int vol;
01072
01073 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01074 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01075 else
01076 volfactor = vol;
01077 }
01078
01079 if (ast_test_flag(&flags, OPTION_PRIVATE))
01080 ast_set_flag(&flags, OPTION_WHISPER);
01081
01082 if (ast_test_flag(&flags, OPTION_ENFORCED))
01083 myenforced = opts[OPT_ARG_ENFORCED];
01084
01085 if (ast_test_flag(&flags, OPTION_NAME)) {
01086 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01087 char *delimiter;
01088 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01089 mailbox = opts[OPT_ARG_NAME];
01090 *delimiter++ = '\0';
01091 name_context = delimiter;
01092 } else {
01093 mailbox = opts[OPT_ARG_NAME];
01094 }
01095 }
01096 }
01097
01098
01099 } else
01100 ast_clear_flag(&flags, AST_FLAGS_ALL);
01101
01102 oldwf = chan->writeformat;
01103 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01104 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01105 return -1;
01106 }
01107
01108 if (recbase) {
01109 char filename[PATH_MAX];
01110
01111 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01112 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01113 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01114 fd = 0;
01115 }
01116 }
01117
01118 res = common_exec(chan, &flags, volfactor, fd, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
01119
01120 if (fd)
01121 close(fd);
01122
01123 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01124 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01125
01126 return res;
01127 }
01128
01129 static int extenspy_exec(struct ast_channel *chan, void *data)
01130 {
01131 char *ptr, *exten = NULL;
01132 char *mygroup = NULL;
01133 char *recbase = NULL;
01134 int fd = 0;
01135 struct ast_flags flags;
01136 int oldwf = 0;
01137 int volfactor = 0;
01138 int res;
01139 char *mailbox = NULL;
01140 char *name_context = NULL;
01141 AST_DECLARE_APP_ARGS(args,
01142 AST_APP_ARG(context);
01143 AST_APP_ARG(options);
01144 );
01145
01146 data = ast_strdupa(data);
01147
01148 AST_STANDARD_APP_ARGS(args, data);
01149 if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
01150 exten = args.context;
01151 *ptr++ = '\0';
01152 args.context = ptr;
01153 }
01154
01155 if (ast_strlen_zero(args.context))
01156 args.context = ast_strdupa(chan->context);
01157
01158 if (args.options) {
01159 char *opts[OPT_ARG_ARRAY_SIZE];
01160
01161 ast_app_parse_options(spy_opts, &flags, opts, args.options);
01162 if (ast_test_flag(&flags, OPTION_GROUP))
01163 mygroup = opts[OPT_ARG_GROUP];
01164
01165 if (ast_test_flag(&flags, OPTION_RECORD) &&
01166 !(recbase = opts[OPT_ARG_RECORD]))
01167 recbase = "chanspy";
01168
01169 if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
01170 int vol;
01171
01172 if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
01173 ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
01174 else
01175 volfactor = vol;
01176 }
01177
01178 if (ast_test_flag(&flags, OPTION_PRIVATE))
01179 ast_set_flag(&flags, OPTION_WHISPER);
01180
01181
01182 if (ast_test_flag(&flags, OPTION_NAME)) {
01183 if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
01184 char *delimiter;
01185 if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
01186 mailbox = opts[OPT_ARG_NAME];
01187 *delimiter++ = '\0';
01188 name_context = delimiter;
01189 } else {
01190 mailbox = opts[OPT_ARG_NAME];
01191 }
01192 }
01193 }
01194
01195 } else
01196 ast_clear_flag(&flags, AST_FLAGS_ALL);
01197
01198 oldwf = chan->writeformat;
01199 if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
01200 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01201 return -1;
01202 }
01203
01204 if (recbase) {
01205 char filename[PATH_MAX];
01206
01207 snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
01208 if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
01209 ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
01210 fd = 0;
01211 }
01212 }
01213
01214
01215 res = common_exec(chan, &flags, volfactor, fd, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
01216
01217 if (fd)
01218 close(fd);
01219
01220 if (oldwf && ast_set_write_format(chan, oldwf) < 0)
01221 ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
01222
01223 return res;
01224 }
01225
01226 static int unload_module(void)
01227 {
01228 int res = 0;
01229
01230 res |= ast_unregister_application(app_chan);
01231 res |= ast_unregister_application(app_ext);
01232
01233 return res;
01234 }
01235
01236 static int load_module(void)
01237 {
01238 int res = 0;
01239
01240 res |= ast_register_application_xml(app_chan, chanspy_exec);
01241 res |= ast_register_application_xml(app_ext, extenspy_exec);
01242
01243 return res;
01244 }
01245
01246 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");