Skip to content
Snippets Groups Projects
app_chanspy.c 47.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			if (autochan->chan == prev) {
    				ast_autochan_destroy(autochan);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				break;
    
    			if (ast_check_hangup(chan)) {
    
    			ast_autochan_channel_lock(autochan);
    			if (ast_test_flag(flags, OPTION_BRIDGED)
    				&& !ast_channel_is_bridged(autochan->chan)) {
    				ast_autochan_channel_unlock(autochan);
    
    			if (ast_check_hangup(autochan->chan)
    				|| ast_test_flag(ast_channel_flags(autochan->chan), AST_FLAG_SPYING)) {
    				ast_autochan_channel_unlock(autochan);
    
    			ast_autochan_channel_unlock(autochan);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			if (mygroup) {
    
    				int num_groups = 0;
    				int num_mygroups = 0;
    				char dup_group[512];
    				char dup_mygroup[512];
    				char *groups[NUM_SPYGROUPS];
    				char *mygroups[NUM_SPYGROUPS];
    
    				int x;
    				int y;
    				ast_copy_string(dup_mygroup, mygroup, sizeof(dup_mygroup));
    
    				num_mygroups = ast_app_separate_args(dup_mygroup, ':', mygroups,
    
    					ARRAY_LEN(mygroups));
    
    				/* Before dahdi scan was part of chanspy, it would use the "GROUP" variable
    
    				 * rather than "SPYGROUP", this check is done to preserve expected behavior */
    
    				if (ast_test_flag(flags, OPTION_DAHDI_SCAN)) {
    					group = pbx_builtin_getvar_helper(autochan->chan, "GROUP");
    				} else {
    					group = pbx_builtin_getvar_helper(autochan->chan, "SPYGROUP");
    				}
    
    				ast_autochan_channel_unlock(autochan);
    
    					ast_copy_string(dup_group, group, sizeof(dup_group));
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    					num_groups = ast_app_separate_args(dup_group, ':', groups,
    
    				for (y = 0; y < num_mygroups; y++) {
    					for (x = 0; x < num_groups; x++) {
    						if (!strcmp(mygroups[y], groups[x])) {
    							igrp = 1;
    							break;
    						}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				continue;
    
    				char ext[AST_CHANNEL_NAME + 3];
    				char buffer[512];
    				char *end;
    
    				snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);
    
    				ast_copy_string(ext + 1, ast_channel_name(autochan->chan), sizeof(ext) - 1);
    
    				ast_autochan_channel_unlock(autochan);
    
    				if ((end = strchr(ext, '-'))) {
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    					*end++ = ':';
    					*end = '\0';
    
    				if (strcasestr(buffer, ext)) {
    
    			if (!ast_test_flag(flags, OPTION_QUIET)) {
    
    				char peer_name[AST_NAME_STRLEN + 5];
    				char *ptr, *s;
    
    				strcpy(peer_name, "spy-");
    
    				strncat(peer_name, ast_channel_name(autochan->chan), AST_NAME_STRLEN - 4 - 1);
    
    				ast_autochan_channel_unlock(autochan);
    
    				if ((ptr = strchr(peer_name, '/'))) {
    					*ptr++ = '\0';
    					for (s = peer_name; s < ptr; s++) {
    						*s = tolower(*s);
    					}
    					if ((s = strchr(ptr, '-'))) {
    						*s = '\0';
    					}
    				}
    
    
    				if (ast_test_flag(flags, OPTION_NAME)) {
    					const char *local_context = S_OR(name_context, "default");
    					const char *local_mailbox = S_OR(mailbox, ptr);
    
    						res = spy_sayname(chan, local_mailbox, local_context);
    
    				}
    				if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
    
    						if (ast_fileexists(peer_name, NULL, NULL) > 0) {
    
    							res = ast_streamfile(chan, peer_name, ast_channel_language(chan));
    
    							res = ast_say_character_str(chan, peer_name, "", ast_channel_language(chan), AST_SAY_CASE_NONE);
    
    					if (ptr && (num = atoi(ptr))) {
    
    						ast_say_digits(chan, num, "", ast_channel_language(chan));
    					}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			}
    
    			res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
    			num_spyed_upon++;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			if (res == -1) {
    
    				iter = ast_channel_iterator_destroy(iter);
    
    				iter = ast_channel_iterator_destroy(iter);
    
    			} else if (res > 1 && spec && !ast_test_flag(flags, OPTION_UNIQUEID)) {
    
    				struct ast_channel *next;
    
    
    				snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);
    
    				if ((next = ast_channel_get_by_name_prefix(nameprefix, strlen(nameprefix)))) {
    					next_autochan = ast_autochan_setup(next);
    					next = ast_channel_unref(next);
    
    					/* stay on this channel, if it is still valid */
    
    					if (!ast_check_hangup(autochan->chan)) {
    						next_autochan = ast_autochan_setup(autochan->chan);
    
    					} else {
    						/* the channel is gone */
    
    					ast_autochan_channel_unlock(autochan);
    
    			} else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
    
    				ast_autochan_destroy(autochan);
    
    				iter = ast_channel_iterator_destroy(iter);
    
    		if (res == -1 || ast_check_hangup(chan))
    			break;
    
    		if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
    			break;
    		}
    
    	ast_channel_clear_flag(chan, AST_FLAG_SPYING);
    
    
    	ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);
    
    	return res;
    }
    
    
    static int chanspy_exec(struct ast_channel *chan, const char *data)
    
    	char *mygroup = NULL;
    	char *recbase = NULL;
    	int fd = 0;
    	struct ast_flags flags;
    
    	struct spy_dtmf_options user_options = {
    		.cycle = '*',
    		.volume = '#',
    		.exit = '\0',
    	};
    
    	RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
    
    	int volfactor = 0;
    	int res;
    
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(spec);
    		AST_APP_ARG(options);
    	);
    	char *opts[OPT_ARG_ARRAY_SIZE];
    
    	char *parse = ast_strdupa(data);
    
    	AST_STANDARD_APP_ARGS(args, parse);
    
    	if (args.spec && !strcmp(args.spec, "all"))
    		args.spec = NULL;
    
    		ast_app_parse_options(spy_opts, &flags, opts, args.options);
    
    		if (ast_test_flag(&flags, OPTION_GROUP))
    			mygroup = opts[OPT_ARG_GROUP];
    
    		if (ast_test_flag(&flags, OPTION_RECORD) &&
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			!(recbase = opts[OPT_ARG_RECORD]))
    
    			recbase = "chanspy";
    
    
    		if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
    			tmp = opts[OPT_ARG_EXIT][0];
    			if (strchr("0123456789*#", tmp) && tmp != '\0') {
    				user_options.exit = tmp;
    			} else {
    
    				ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
    
    			}
    		}
    
    		if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
    			tmp = opts[OPT_ARG_CYCLE][0];
    			if (strchr("0123456789*#", tmp) && tmp != '\0') {
    				user_options.cycle = tmp;
    			} else {
    
    				ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
    
    		if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
    			int vol;
    
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    			if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
    
    				ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
    			else
    				volfactor = vol;
    		}
    
    		if (ast_test_flag(&flags, OPTION_PRIVATE))
    			ast_set_flag(&flags, OPTION_WHISPER);
    
    
    		if (ast_test_flag(&flags, OPTION_ENFORCED))
    			myenforced = opts[OPT_ARG_ENFORCED];
    
    		if (ast_test_flag(&flags, OPTION_NAME)) {
    			if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
    				char *delimiter;
    				if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
    					mailbox = opts[OPT_ARG_NAME];
    					*delimiter++ = '\0';
    					name_context = delimiter;
    				} else {
    					mailbox = opts[OPT_ARG_NAME];
    				}
    			}
    		}
    
    		ast_clear_flag(&flags, AST_FLAGS_ALL);
    
    	oldwf = ao2_bump(ast_channel_writeformat(chan));
    	if (ast_set_write_format(chan, ast_format_slin) < 0) {
    
    		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    		return -1;
    	}
    
    	if (recbase) {
    
    		char filename[PATH_MAX];
    
    
    		snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
    
    		if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
    
    			ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
    			fd = 0;
    		}
    	}
    
    
    	res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, myenforced, args.spec, NULL, NULL, mailbox, name_context);
    
    	if (oldwf && ast_set_write_format(chan, oldwf) < 0)
    
    		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    
    
    	if (ast_test_flag(&flags, OPTION_EXITONHANGUP)) {
    		ast_verb(3, "Stopped spying due to the spied-on channel hanging up.\n");
    	}
    
    
    static int extenspy_exec(struct ast_channel *chan, const char *data)
    
    	char *mygroup = NULL;
    	char *recbase = NULL;
    	int fd = 0;
    	struct ast_flags flags;
    
    	struct spy_dtmf_options user_options = {
    		.cycle = '*',
    		.volume = '#',
    		.exit = '\0',
    	};
    
    	RAII_VAR(struct ast_format *, oldwf, NULL, ao2_cleanup);
    
    	int volfactor = 0;
    	int res;
    
    	AST_DECLARE_APP_ARGS(args,
    		AST_APP_ARG(context);
    		AST_APP_ARG(options);
    	);
    
    	char *parse = ast_strdupa(data);
    
    	AST_STANDARD_APP_ARGS(args, parse);
    
    	if (!ast_strlen_zero(args.context) && (ptr = strchr(args.context, '@'))) {
    		exten = args.context;
    		*ptr++ = '\0';
    		args.context = ptr;
    
    		args.context = ast_strdupa(ast_channel_context(chan));
    
    		char *opts[OPT_ARG_ARRAY_SIZE];
    
    		ast_app_parse_options(spy_opts, &flags, opts, args.options);
    
    		if (ast_test_flag(&flags, OPTION_GROUP))
    			mygroup = opts[OPT_ARG_GROUP];
    
    		if (ast_test_flag(&flags, OPTION_RECORD) &&
    
    Michiel van Baak's avatar
    Michiel van Baak committed
    			!(recbase = opts[OPT_ARG_RECORD]))
    
    			recbase = "chanspy";
    
    		if (ast_test_flag(&flags, OPTION_DTMF_EXIT) && opts[OPT_ARG_EXIT]) {
    			tmp = opts[OPT_ARG_EXIT][0];
    			if (strchr("0123456789*#", tmp) && tmp != '\0') {
    				user_options.exit = tmp;
    			} else {
    
    				ast_log(LOG_NOTICE, "Argument for option 'x' must be a valid DTMF digit.\n");
    
    			}
    		}
    
    		if (ast_test_flag(&flags, OPTION_DTMF_CYCLE) && opts[OPT_ARG_CYCLE]) {
    			tmp = opts[OPT_ARG_CYCLE][0];
    			if (strchr("0123456789*#", tmp) && tmp != '\0') {
    				user_options.cycle = tmp;
    			} else {
    
    				ast_log(LOG_NOTICE, "Argument for option 'c' must be a valid DTMF digit.\n");
    
    		if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
    			int vol;
    
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    			if ((sscanf(opts[OPT_ARG_VOLUME], "%30d", &vol) != 1) || (vol > 4) || (vol < -4))
    
    				ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
    			else
    				volfactor = vol;
    		}
    
    
    		if (ast_test_flag(&flags, OPTION_PRIVATE))
    			ast_set_flag(&flags, OPTION_WHISPER);
    
    
    		if (ast_test_flag(&flags, OPTION_NAME)) {
    			if (!ast_strlen_zero(opts[OPT_ARG_NAME])) {
    				char *delimiter;
    				if ((delimiter = strchr(opts[OPT_ARG_NAME], '@'))) {
    					mailbox = opts[OPT_ARG_NAME];
    					*delimiter++ = '\0';
    					name_context = delimiter;
    				} else {
    					mailbox = opts[OPT_ARG_NAME];
    				}
    			}
    		}
    
    
    		/* Coverity - This uninit_use should be ignored since this macro initializes the flags */
    
    		ast_clear_flag(&flags, AST_FLAGS_ALL);
    
    	oldwf = ao2_bump(ast_channel_writeformat(chan));
    	if (ast_set_write_format(chan, ast_format_slin) < 0) {
    
    		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    		return -1;
    	}
    
    	if (recbase) {
    
    		char filename[PATH_MAX];
    
    
    		snprintf(filename, sizeof(filename), "%s/%s.%d.raw", ast_config_AST_MONITOR_DIR, recbase, (int) time(NULL));
    
    		if ((fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, AST_FILE_MODE)) <= 0) {
    
    			ast_log(LOG_WARNING, "Cannot open '%s' for recording\n", filename);
    			fd = 0;
    		}
    	}
    
    
    	res = common_exec(chan, &flags, volfactor, fd, &user_options, mygroup, NULL, NULL, exten, args.context, mailbox, name_context);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (fd)
    
    	if (oldwf && ast_set_write_format(chan, oldwf) < 0)
    
    		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	return res;
    
    static int dahdiscan_exec(struct ast_channel *chan, const char *data)
    
    	struct ast_flags flags = {0};
    
    	struct spy_dtmf_options user_options = {
    		.cycle = '#',
    		.volume = '\0',
    		.exit = '*',
    	};
    
    	/* Coverity - This uninit_use should be ignored since this macro initializes the flags */
    
    	ast_clear_flag(&flags, AST_FLAGS_ALL);
    
    	if (!ast_strlen_zero(data)) {
    		mygroup = ast_strdupa(data);
    	}
    	ast_set_flag(&flags, OPTION_DTMF_EXIT);
    	ast_set_flag(&flags, OPTION_DTMF_CYCLE);
    	ast_set_flag(&flags, OPTION_DAHDI_SCAN);
    
    
    	oldwf = ao2_bump(ast_channel_writeformat(chan));
    	if (ast_set_write_format(chan, ast_format_slin) < 0) {
    
    		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    
    		return -1;
    	}
    
    	res = common_exec(chan, &flags, 0, 0, &user_options, mygroup, NULL, spec, NULL, NULL, NULL, NULL);
    
    
    	if (oldwf && ast_set_write_format(chan, oldwf) < 0)
    
    		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
    
    static int unload_module(void)
    
    	int res = 0;
    
    	res |= ast_unregister_application(app_chan);
    	res |= ast_unregister_application(app_ext);
    
    	res |= ast_unregister_application(app_dahdiscan);
    
    static int load_module(void)
    
    	res |= ast_register_application_xml(app_chan, chanspy_exec);
    	res |= ast_register_application_xml(app_ext, extenspy_exec);
    
    	res |= ast_register_application_xml(app_dahdiscan, dahdiscan_exec);
    
    AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Listen to the audio of an active channel");