55 #include "asterisk/stasis_channels.h"
354 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
356 static const char *
const app =
"MixMonitor";
358 static const char *
const stop_app =
"StopMixMonitor";
360 static const char *
const mixmonitor_spy_type =
"MixMonitor";
377 char *filename_write;
399 enum mixmonitor_flags {
400 MUXFLAG_APPEND = (1 << 1),
401 MUXFLAG_BRIDGED = (1 << 2),
402 MUXFLAG_VOLUME = (1 << 3),
403 MUXFLAG_READVOLUME = (1 << 4),
404 MUXFLAG_WRITEVOLUME = (1 << 5),
405 MUXFLAG_READ = (1 << 6),
406 MUXFLAG_WRITE = (1 << 7),
407 MUXFLAG_COMBINED = (1 << 8),
408 MUXFLAG_UID = (1 << 9),
409 MUXFLAG_VMRECIPIENTS = (1 << 10),
410 MUXFLAG_BEEP = (1 << 11),
411 MUXFLAG_BEEP_START = (1 << 12),
412 MUXFLAG_BEEP_STOP = (1 << 13),
413 MUXFLAG_DEPRECATED_RWSYNC = (1 << 14),
414 MUXFLAG_NO_RWSYNC = (1 << 15),
415 MUXFLAG_AUTO_DELETE = (1 << 16),
416 MUXFLAG_REAL_CALLERID = (1 << 17),
419 enum mixmonitor_args {
420 OPT_ARG_READVOLUME = 0,
426 OPT_ARG_VMRECIPIENTS,
427 OPT_ARG_BEEP_INTERVAL,
428 OPT_ARG_DEPRECATED_RWSYNC,
453 unsigned int destruction_ok;
454 ast_cond_t destruction_condition;
467 unsigned int samp_rate;
478 unsigned char quitting = 0;
480 if (mixmonitor_ds->fs) {
483 mixmonitor_ds->fs = NULL;
484 ast_verb(2,
"MixMonitor close filestream (mixed)\n");
487 if (mixmonitor_ds->fs_read) {
490 mixmonitor_ds->fs_read = NULL;
491 ast_verb(2,
"MixMonitor close filestream (read)\n");
494 if (mixmonitor_ds->fs_write) {
497 mixmonitor_ds->fs_write = NULL;
498 ast_verb(2,
"MixMonitor close filestream (write)\n");
502 mixmonitor_ds->fs_quit = 1;
506 static void mixmonitor_ds_destroy(
void *data)
508 struct mixmonitor_ds *mixmonitor_ds = data;
510 ast_mutex_lock(&mixmonitor_ds->lock);
511 mixmonitor_ds->audiohook = NULL;
512 mixmonitor_ds->destruction_ok = 1;
513 ast_free(mixmonitor_ds->filename);
514 ast_free(mixmonitor_ds->beep_id);
515 ast_cond_signal(&mixmonitor_ds->destruction_condition);
516 ast_mutex_unlock(&mixmonitor_ds->lock);
520 .
type =
"mixmonitor",
521 .destroy = mixmonitor_ds_destroy,
526 if (mixmonitor->mixmonitor_ds) {
527 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
528 mixmonitor->mixmonitor_ds->audiohook = NULL;
529 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
553 static void add_vm_recipients_from_string(
struct mixmonitor *mixmonitor,
const char *vm_recipients)
560 int elements_processed = 0;
562 while (!ast_strlen_zero(cur_mailbox)) {
563 ast_debug(3,
"attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
564 if ((next = strchr(cur_mailbox,
',')) || (next = strchr(cur_mailbox,
'&'))) {
568 if ((cur_folder = strchr(cur_mailbox,
'/'))) {
569 *(cur_folder++) =
'\0';
571 cur_folder =
"INBOX";
574 if ((cur_context = strchr(cur_mailbox,
'@'))) {
575 *(cur_context++) =
'\0';
577 cur_context =
"default";
580 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
582 if (!(recipient =
ast_malloc(
sizeof(*recipient)))) {
583 ast_log(LOG_ERROR,
"Failed to allocate recipient. Aborting function.\n");
586 ast_copy_string(recipient->context, cur_context,
sizeof(recipient->context));
587 ast_copy_string(recipient->mailbox, cur_mailbox,
sizeof(recipient->mailbox));
588 ast_copy_string(recipient->folder, cur_folder,
sizeof(recipient->folder));
591 ast_verb(4,
"Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
594 ast_log(LOG_ERROR,
"Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
598 elements_processed++;
602 static void clear_mixmonitor_recipient_list(
struct mixmonitor *mixmonitor)
611 #define SAMPLES_PER_FRAME 160
613 static void mixmonitor_free(
struct mixmonitor *mixmonitor)
616 if (mixmonitor->mixmonitor_ds) {
617 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
618 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
619 ast_free(mixmonitor->mixmonitor_ds);
622 ast_free(mixmonitor->name);
623 ast_free(mixmonitor->post_process);
624 ast_free(mixmonitor->filename);
625 ast_free(mixmonitor->filename_write);
626 ast_free(mixmonitor->filename_read);
629 clear_mixmonitor_recipient_list(mixmonitor);
634 ast_free(mixmonitor);
645 static void copy_to_voicemail(
struct mixmonitor *mixmonitor,
const char *ext,
const char *filename)
650 ast_log(LOG_ERROR,
"Failed to string_field_init, skipping copy_to_voicemail\n");
662 recording_data.call_priority = mixmonitor->call_priority;
670 ast_verb(4,
"MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
671 recording_data.context);
679 static void mixmonitor_save_prep(
struct mixmonitor *mixmonitor,
char *filename,
struct ast_filestream **fs,
unsigned int *oflags,
int *errflag,
char **ext)
682 char *last_slash = NULL;
683 if (!ast_strlen_zero(filename)) {
684 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
685 *oflags = O_CREAT | O_WRONLY;
686 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
688 last_slash = strrchr(filename,
'/');
690 if ((*ext = strrchr(filename,
'.')) && (*ext > last_slash)) {
697 if (!(*fs =
ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
698 ast_log(LOG_ERROR,
"Cannot open %s.%s\n", filename, *ext);
708 static int mixmonitor_autochan_is_bridged(
struct ast_autochan *autochan)
714 ast_autochan_channel_unlock(autochan);
718 static void *mixmonitor_thread(
void *obj)
720 struct mixmonitor *mixmonitor = obj;
722 char *fs_read_ext =
"";
723 char *fs_write_ext =
"";
734 if (mixmonitor->callid) {
738 ast_verb(2,
"Begin MixMonitor Recording %s\n", mixmonitor->name);
740 fs = &mixmonitor->mixmonitor_ds->fs;
741 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
742 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
744 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
745 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
746 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
747 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
751 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
761 &fr_read, &fr_write))) {
774 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED)
775 || mixmonitor_autochan_is_bridged(mixmonitor->autochan)) {
776 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
779 if ((*fs_read) && (fr_read)) {
787 if ((*fs_write) && (fr_write)) {
802 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
824 if (ast_test_flag(mixmonitor, MUXFLAG_BEEP_STOP)) {
827 ast_autochan_channel_unlock(mixmonitor->autochan);
833 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
834 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
835 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
836 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
838 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
841 destroy_monitor_audiohook(mixmonitor);
843 if (mixmonitor->post_process) {
844 ast_verb(2,
"Executing [%s]\n", mixmonitor->post_process);
848 ast_verb(2,
"End MixMonitor Recording %s\n", mixmonitor->name);
852 if (ast_strlen_zero(fs_ext)) {
853 ast_log(LOG_ERROR,
"No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
856 ast_verb(3,
"Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
857 copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
859 if (!ast_strlen_zero(fs_read_ext)) {
860 ast_verb(3,
"Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
861 copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
863 if (!ast_strlen_zero(fs_write_ext)) {
864 ast_verb(3,
"Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
865 copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
868 ast_debug(3,
"No recipients to forward monitor to, moving on.\n");
871 if (ast_test_flag(mixmonitor, MUXFLAG_AUTO_DELETE)) {
872 ast_debug(3,
"Deleting our copies of recording files\n");
873 if (!ast_strlen_zero(fs_ext)) {
876 if (!ast_strlen_zero(fs_read_ext)) {
879 if (!ast_strlen_zero(fs_write_ext)) {
884 mixmonitor_free(mixmonitor);
890 static int setup_mixmonitor_ds(
struct mixmonitor *mixmonitor,
struct ast_channel *chan,
char **datastore_id,
const char *beep_id)
893 struct mixmonitor_ds *mixmonitor_ds;
895 if (!(mixmonitor_ds =
ast_calloc(1,
sizeof(*mixmonitor_ds)))) {
899 if (
ast_asprintf(datastore_id,
"%p", mixmonitor_ds) == -1) {
900 ast_log(LOG_ERROR,
"Failed to allocate memory for MixMonitor ID.\n");
901 ast_free(mixmonitor_ds);
905 ast_mutex_init(&mixmonitor_ds->lock);
906 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
908 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
909 ast_mutex_destroy(&mixmonitor_ds->lock);
910 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
911 ast_free(mixmonitor_ds);
915 if (ast_test_flag(mixmonitor, MUXFLAG_BEEP_START)) {
918 ast_autochan_channel_unlock(mixmonitor->autochan);
921 mixmonitor_ds->samp_rate = 8000;
922 mixmonitor_ds->audiohook = &mixmonitor->audiohook;
923 mixmonitor_ds->filename =
ast_strdup(mixmonitor->filename);
924 if (!ast_strlen_zero(beep_id)) {
927 datastore->
data = mixmonitor_ds;
929 ast_channel_lock(chan);
931 ast_channel_unlock(chan);
933 mixmonitor->mixmonitor_ds = mixmonitor_ds;
937 static void mixmonitor_ds_remove_and_free(
struct ast_channel *chan,
const char *datastore_id)
941 ast_channel_lock(chan);
952 ast_channel_unlock(chan);
955 static int launch_monitor_thread(
struct ast_channel *chan,
const char *filename,
956 unsigned int flags,
int readvol,
int writevol,
957 const char *post_process,
const char *filename_write,
958 char *filename_read,
const char *uid_channel_var,
959 const char *recipients,
const char *beep_id)
962 struct mixmonitor *mixmonitor;
963 char postprocess2[1024] =
"";
964 char *datastore_id = NULL;
968 if (!ast_strlen_zero(post_process)) {
972 for (p2 = p1; *p2; p2++) {
973 if (*p2 ==
'^' && *(p2+1) ==
'{') {
977 ast_channel_lock(chan);
978 pbx_substitute_variables_helper(chan, p1, postprocess2,
sizeof(postprocess2) - 1);
979 ast_channel_unlock(chan);
983 if (!(mixmonitor =
ast_calloc(1,
sizeof(*mixmonitor)))) {
989 mixmonitor_free(mixmonitor);
995 mixmonitor_free(mixmonitor);
1000 mixmonitor->flags = flags;
1002 mixmonitor_free(mixmonitor);
1006 if (!ast_strlen_zero(filename)) {
1010 if (!ast_strlen_zero(filename_write)) {
1011 mixmonitor->filename_write =
ast_strdup(filename_write);
1014 if (!ast_strlen_zero(filename_read)) {
1015 mixmonitor->filename_read =
ast_strdup(filename_read);
1018 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1020 mixmonitor_free(mixmonitor);
1021 ast_free(datastore_id);
1025 if (!ast_strlen_zero(uid_channel_var)) {
1031 mixmonitor->name =
ast_strdup(ast_channel_name(chan));
1033 if (!ast_strlen_zero(postprocess2)) {
1034 mixmonitor->post_process =
ast_strdup(postprocess2);
1037 if (!ast_strlen_zero(recipients)) {
1040 ast_channel_lock(chan);
1049 if (ast_test_flag(mixmonitor, MUXFLAG_REAL_CALLERID)) {
1051 caller = ast_channel_caller(chan);
1055 ast_callerid_merge(callerid,
sizeof(callerid),
1061 connected = ast_channel_connected(chan);
1065 ast_callerid_merge(callerid,
sizeof(callerid),
1075 mixmonitor->call_priority = ast_channel_priority(chan);
1077 ast_channel_unlock(chan);
1079 add_vm_recipients_from_string(mixmonitor, recipients);
1083 if (!ast_test_flag(mixmonitor, MUXFLAG_NO_RWSYNC)) {
1092 if (startmon(chan, &mixmonitor->audiohook)) {
1093 ast_log(LOG_WARNING,
"Unable to add '%s' spy to channel '%s'\n",
1094 mixmonitor_spy_type, ast_channel_name(chan));
1095 mixmonitor_ds_remove_and_free(chan, datastore_id);
1096 ast_free(datastore_id);
1099 mixmonitor_free(mixmonitor);
1103 ast_free(datastore_id);
1108 return ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
1113 static char *filename_parse(
char *filename,
char *buffer,
size_t len)
1118 ast_assert(len > 0);
1120 if (ast_strlen_zero(filename)) {
1121 ast_log(LOG_WARNING,
"No file name was provided for a file save option.\n");
1127 if (*filename !=
'/') {
1128 char *build =
ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1129 sprintf(build,
"%s/%s", ast_config_AST_MONITOR_DIR, filename);
1138 ext = strrchr(buffer,
'.');
1139 if (ext && !strcmp(ext,
".wav49")) {
1142 memcpy(ext,
".WAV",
sizeof(
".WAV"));
1145 if ((slash = strrchr(filename,
'/'))) {
1153 static int mixmonitor_exec(
struct ast_channel *chan,
const char *data)
1155 int x, readvol = 0, writevol = 0;
1156 char *filename_read = NULL;
1157 char *filename_write = NULL;
1158 char filename_buffer[1024] =
"";
1159 char *uid_channel_var = NULL;
1160 char beep_id[64] =
"";
1163 char *recipients = NULL;
1172 if (ast_strlen_zero(data)) {
1173 ast_log(LOG_WARNING,
"MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1182 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1186 if (ast_test_flag(&flags, MUXFLAG_DEPRECATED_RWSYNC)) {
1187 ast_log(LOG_NOTICE,
"The synchronization behavior enabled by the 'S' option is now the default"
1188 " and does not need to be specified.\n");
1191 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
1192 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
1193 ast_log(LOG_WARNING,
"No volume level was provided for the heard volume ('v') option.\n");
1194 }
else if ((sscanf(opts[OPT_ARG_READVOLUME],
"%2d", &x) != 1) || (x < -4) || (x > 4)) {
1195 ast_log(LOG_NOTICE,
"Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1197 readvol = get_volfactor(x);
1201 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
1202 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
1203 ast_log(LOG_WARNING,
"No volume level was provided for the spoken volume ('V') option.\n");
1204 }
else if ((sscanf(opts[OPT_ARG_WRITEVOLUME],
"%2d", &x) != 1) || (x < -4) || (x > 4)) {
1205 ast_log(LOG_NOTICE,
"Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1207 writevol = get_volfactor(x);
1211 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
1212 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1213 ast_log(LOG_WARNING,
"No volume level was provided for the combined volume ('W') option.\n");
1214 }
else if ((sscanf(opts[OPT_ARG_VOLUME],
"%2d", &x) != 1) || (x < -4) || (x > 4)) {
1215 ast_log(LOG_NOTICE,
"Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1217 readvol = writevol = get_volfactor(x);
1221 if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
1222 if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
1223 ast_log(LOG_WARNING,
"No voicemail recipients were specified for the vm copy ('m') option.\n");
1225 recipients =
ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1229 if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
1230 filename_write =
ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer,
sizeof(filename_buffer)));
1233 if (ast_test_flag(&flags, MUXFLAG_READ)) {
1234 filename_read =
ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer,
sizeof(filename_buffer)));
1237 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1238 uid_channel_var = opts[OPT_ARG_UID];
1241 if (ast_test_flag(&flags, MUXFLAG_BEEP)) {
1242 const char *interval_str =
S_OR(opts[OPT_ARG_BEEP_INTERVAL],
"15");
1243 unsigned int interval = 15;
1245 if (sscanf(interval_str,
"%30u", &interval) != 1) {
1246 ast_log(LOG_WARNING,
"Invalid interval '%s' for periodic beep. Using default of %u\n",
1247 interval_str, interval);
1250 if (ast_beep_start(chan, interval, beep_id,
sizeof(beep_id))) {
1251 ast_log(LOG_WARNING,
"Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1258 if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
1259 ast_log(LOG_WARNING,
"MixMonitor requires an argument (filename)\n");
1264 if (!(ast_strlen_zero(args.filename))) {
1265 args.filename =
ast_strdupa(filename_parse(args.filename, filename_buffer,
sizeof(filename_buffer)));
1272 if (launch_monitor_thread(chan,
1295 static int stop_mixmonitor_full(
struct ast_channel *chan,
const char *data)
1299 struct mixmonitor_ds *mixmonitor_ds;
1300 const char *beep_id = NULL;
1307 if (!ast_strlen_zero(data)) {
1313 ast_channel_lock(chan);
1316 S_OR(args.mixmonid, NULL));
1318 ast_channel_unlock(chan);
1321 mixmonitor_ds = datastore->
data;
1323 ast_mutex_lock(&mixmonitor_ds->lock);
1327 mixmonitor_ds_close_fs(mixmonitor_ds);
1332 if (mixmonitor_ds->audiohook) {
1337 ast_cond_signal(&mixmonitor_ds->audiohook->
trigger);
1339 mixmonitor_ds->audiohook = NULL;
1342 if (!ast_strlen_zero(mixmonitor_ds->beep_id)) {
1346 ast_mutex_unlock(&mixmonitor_ds->lock);
1353 ast_channel_unlock(chan);
1355 if (!ast_strlen_zero(beep_id)) {
1356 ast_beep_stop(chan, beep_id);
1369 static int stop_mixmonitor_exec(
struct ast_channel *chan,
const char *data)
1371 stop_mixmonitor_full(chan, data);
1379 struct mixmonitor_ds *mixmonitor_ds = NULL;
1383 e->
command =
"mixmonitor {start|stop|list}";
1385 "Usage: mixmonitor start <chan_name> [args]\n"
1386 " The optional arguments are passed to the MixMonitor application.\n"
1387 " mixmonitor stop <chan_name> [args]\n"
1388 " The optional arguments are passed to the StopMixMonitor application.\n"
1389 " mixmonitor list <chan_name>\n";
1396 return CLI_SHOWUSAGE;
1400 ast_cli(a->fd,
"No channel matching '%s' found.\n", a->argv[2]);
1405 if (!strcasecmp(a->argv[1],
"start")) {
1406 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] :
"");
1407 }
else if (!strcasecmp(a->argv[1],
"stop")){
1408 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] :
"");
1409 }
else if (!strcasecmp(a->argv[1],
"list")) {
1410 ast_cli(a->fd,
"MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1411 ast_cli(a->fd,
"=========================================================================\n");
1412 ast_channel_lock(chan);
1414 if (datastore->
info == &mixmonitor_ds_info) {
1415 char *filename =
"";
1416 char *filename_read =
"";
1417 char *filename_write =
"";
1419 mixmonitor_ds = datastore->
data;
1420 if (mixmonitor_ds->fs) {
1421 filename = mixmonitor_ds->fs->filename;
1423 if (mixmonitor_ds->fs_read) {
1424 filename_read = mixmonitor_ds->fs_read->filename;
1426 if (mixmonitor_ds->fs_write) {
1427 filename_write = mixmonitor_ds->fs_write->filename;
1429 ast_cli(a->fd,
"%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1432 ast_channel_unlock(chan);
1435 return CLI_SHOWUSAGE;
1449 struct mixmonitor_ds *mixmonitor_ds;
1455 if (!ast_strlen_zero(data)) {
1461 ast_channel_lock(chan);
1464 S_OR(args.mixmonid, NULL));
1466 ast_channel_unlock(chan);
1469 mixmonitor_ds = datastore->
data;
1471 ast_mutex_lock(&mixmonitor_ds->lock);
1473 if (mixmonitor_ds->audiohook) {
1475 ast_clear_flag(mixmonitor_ds->audiohook, flag);
1477 ast_set_flag(mixmonitor_ds->audiohook, flag);
1481 ast_mutex_unlock(&mixmonitor_ds->lock);
1482 ast_channel_unlock(chan);
1496 int clearmute = 1, mutedcount = 0;
1501 if (ast_strlen_zero(direction)) {
1506 if (!strcasecmp(direction,
"read")) {
1508 }
else if (!strcasecmp(direction,
"write")) {
1510 }
else if (!strcasecmp(direction,
"both")) {
1513 astman_send_error(s, m,
"Invalid direction specified. Must be read, write or both");
1517 if (ast_strlen_zero(name)) {
1522 if (ast_strlen_zero(state)) {
1535 if (ast_strlen_zero(mixmonitor_id)) {
1537 if (mutedcount < 0) {
1552 stasis_message_blob =
ast_json_pack(
"{s: s, s: b, s: s, s: i}",
1553 "direction", direction,
1555 "mixmonitorid", mixmonitor_id,
1556 "count", mutedcount);
1567 if (!ast_strlen_zero(
id)) {
1578 static int start_mixmonitor_callback(
struct ast_channel *chan,
const char *filename,
const char *options)
1580 char args[PATH_MAX];
1582 if (ast_strlen_zero(options)) {
1583 snprintf(args,
sizeof(args),
"%s", filename);
1585 snprintf(args,
sizeof(args),
"%s,%s", filename, options);
1588 return mixmonitor_exec(chan, args);
1591 static int stop_mixmonitor_callback(
struct ast_channel *chan,
const char *mixmonitor_id)
1593 return stop_mixmonitor_full(chan, mixmonitor_id);
1604 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1606 char *uid_channel_var = NULL;
1607 const char *mixmonitor_id = NULL;
1609 char args[PATH_MAX];
1611 if (ast_strlen_zero(name)) {
1622 if (!ast_strlen_zero(options)) {
1626 snprintf(args,
sizeof(args),
"%s,%s,%s", file, options, command);
1628 res = mixmonitor_exec(c, args);
1630 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1631 uid_channel_var = opts[OPT_ARG_UID];
1632 ast_channel_lock(c);
1635 ast_channel_unlock(c);
1646 if (!ast_strlen_zero(
id)) {
1650 if (!ast_strlen_zero(mixmonitor_id)) {
1661 static int manager_stop_mixmonitor(
struct mansession *s,
const struct message *m)
1669 if (ast_strlen_zero(name)) {
1680 res = stop_mixmonitor_full(c, mixmonitor_id);
1689 if (!ast_strlen_zero(
id)) {
1700 static int func_mixmonitor_read(
struct ast_channel *chan,
const char *cmd,
char *data,
1701 char *buf,
size_t len)
1704 struct mixmonitor_ds *ds_data;
1712 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1713 ast_log(LOG_WARNING,
"Not enough arguments provided to %s. "
1714 "An ID and key must be provided\n", cmd);
1718 ast_channel_lock(chan);
1720 ast_channel_unlock(chan);
1723 ast_log(LOG_WARNING,
"Could not find MixMonitor with ID %s\n", args.id);
1727 ds_data = datastore->
data;
1729 if (!strcasecmp(args.key,
"filename")) {
1732 ast_log(LOG_WARNING,
"Unrecognized %s option %s\n", cmd, args.key);
1739 .
name =
"MIXMONITOR",
1740 .read = func_mixmonitor_read,
1744 AST_CLI_DEFINE(handle_cli_mixmonitor,
"Execute a MixMonitor command")
1747 static int set_mixmonitor_methods(
void)
1750 .start = start_mixmonitor_callback,
1751 .stop = stop_mixmonitor_callback,
1757 static int clear_mixmonitor_methods(
void)
1762 static int unload_module(
void)
1773 res |= clear_mixmonitor_methods();
1778 static int load_module(
void)
1789 res |= set_mixmonitor_methods();
1794 AST_MODULE_INFO(
ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,
"Mixed Audio Monitoring Application",
1795 .support_level = AST_MODULE_SUPPORT_CORE,
1796 .load = load_module,
1797 .unload = unload_module,
1798 .optional_modules =
"func_periodic_hook",
static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data, enum ast_audiohook_flags flag, int clearmute)
Mute / unmute an individual MixMonitor by id.
Main Channel structure associated with a channel.
char * str
Subscriber phone number (Malloced)
void astman_append(struct mansession *s, const char *fmt,...)
Asterisk main include file. File version handling, generic pbx functions.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
#define ast_autochan_channel_lock(autochan)
Lock the autochan's channel lock.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
struct ast_party_id id
Connected party ID.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
#define ast_channel_unref(c)
Decrease channel reference count.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
struct ast_party_name name
Subscriber name.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
void ast_audiohook_update_status(struct ast_audiohook *audiohook, enum ast_audiohook_status status)
Update audiohook's status.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Structure for a data store type.
char * str
Subscriber name (Malloced)
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
#define ast_cli_register_multiple(e, len)
Register multiple commands.
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
ast_callid ast_read_threadstorage_callid(void)
extracts the callerid from the thread
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
Mute / unmute a MixMonitor channel.
#define ast_strdup(str)
A wrapper for strdup()
Structure for a data store object.
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
int ast_set_mixmonitor_methods(struct ast_mixmonitor_methods *vmethod_table)
Setup MixMonitor virtual methods table. Use this to provide the MixMonitor functionality from a loada...
int ast_unregister_application(const char *app)
Unregister an application.
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Periodic beeps into the audio of a call.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
struct ast_channel * ast_channel_get_by_name_prefix(const char *name, size_t name_len)
Find a channel by a name prefix.
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags)
Initialize an audiohook structure.
MixMonitor virtual methods table definition.
struct ast_party_id id
Caller party ID.
#define ast_audiohook_unlock(ah)
Unlock an audiohook.
int ast_audiohook_set_mute_all(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear)
Mute frames read from or written for all audiohooks on a channel.
General Asterisk PBX channel definitions.
Asterisk file paths, configured in asterisk.conf.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Data structure associated with a custom dialplan function.
#define AST_MAX_EXTENSION
#define AST_STRING_FIELD(name)
Declare a string field.
struct ast_format_def * fmt
Caller Party information.
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
In case you didn't read that giant block of text above the mansession_session struct, the mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
const struct ast_datastore_info * info
A set of macros to manage forward-linked lists.
#define ast_malloc(len)
A wrapper for malloc()
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Core PBX routines and definitions.
#define ast_test_suite_event_notify(s, f,...)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
"smart" channels that update automatically if a channel is masqueraded
struct stasis_message_type * ast_channel_mixmonitor_start_type(void)
Message type for starting mixmonitor on a channel.
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
struct stasis_message_type * ast_channel_mixmonitor_stop_type(void)
Message type for stopping mixmonitor on a channel.
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
Connected Line/Party information.
const ast_string_field name
struct ast_filestream * ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts writing a file.
#define ast_module_ref(mod)
Hold a reference to the module.
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
#define ast_calloc(num, len)
A wrapper for calloc()
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Structure used to handle boolean flags.
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
void ast_autochan_destroy(struct ast_autochan *autochan)
destroy an ast_autochan structure
#define ast_module_unref(mod)
Release a reference to the module.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
int ast_clear_mixmonitor_methods(void)
Clear the MixMonitor virtual methods table. Use this to cleanup function pointers provided by a modul...
struct ast_autochan * ast_autochan_setup(struct ast_channel *chan)
set up a new ast_autochan structure
struct ast_audiohook_options options
Structure used for ast_copy_recording_to_vm in order to cleanly supply data needed for making the rec...
struct ast_frame * ast_audiohook_read_frame_all(struct ast_audiohook *audiohook, size_t samples, struct ast_format *format, struct ast_frame **read_frame, struct ast_frame **write_frame)
Reads a frame in from the audiohook structure in mixed audio mode and copies read and write frame dat...
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Standard Command Line Interface.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
Writes a frame to a stream.
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
char * ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
Command completion for the list of active channels.
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
loadable MixMonitor functionality
enum ast_audiohook_status status
unsigned char valid
TRUE if the name information is valid/present.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
#define ASTERISK_GPL_KEY
The text the key() function should return.
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
#define ast_audiohook_lock(ah)
Lock an audiohook.
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Asterisk module definitions.
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
Wait for audiohook trigger to be triggered.
int ast_app_copy_recording_to_vm(struct ast_vm_recording_data *vm_rec_data)
param[in] vm_rec_data Contains data needed to make the recording. retval 0 voicemail successfully cre...
unsigned char valid
TRUE if the number information is valid/present.
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
#define ast_custom_function_register(acf)
Register a custom function.
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Structure for mutex and tracking information.
void ast_frame_free(struct ast_frame *frame, int cache)
Frees a frame or list of frames.
#define AST_APP_ARG(name)
Define an application argument.
struct stasis_message_type * ast_channel_mixmonitor_mute_type(void)
Message type for muting or unmuting mixmonitor on a channel.
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
struct ast_party_number number
Subscriber phone number.