/*! 
 * \brief Stop monitoring channel 
 * \param chan 
 * \param need_lock
 * Stop the recording, close any open streams, mix in/out channels if required
 * \return Always 0
*/
int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_lock)
{
	int delfiles = 0;

	LOCK_IF_NEEDED(chan, need_lock);

	if (ast_channel_monitor(chan)) {
		char filename[ FILENAME_MAX ];

		if (ast_channel_monitor(chan)->read_stream) {
			ast_closestream(ast_channel_monitor(chan)->read_stream);
		}
		if (ast_channel_monitor(chan)->write_stream) {
			ast_closestream(ast_channel_monitor(chan)->write_stream);
		}

		if (ast_channel_monitor(chan)->filename_changed && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
			if (ast_channel_monitor(chan)->read_stream) {
				if (ast_fileexists(ast_channel_monitor(chan)->read_filename,NULL,NULL) > 0) {
					snprintf(filename, FILENAME_MAX, "%s-in", ast_channel_monitor(chan)->filename_base);
					if (ast_fileexists(filename, NULL, NULL) > 0) {
						ast_filedelete(filename, NULL);
					}
					ast_filerename(ast_channel_monitor(chan)->read_filename, filename, ast_channel_monitor(chan)->format);
				} else {
					ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->read_filename);
				}
			}

			if (ast_channel_monitor(chan)->write_stream) {
				if (ast_fileexists(ast_channel_monitor(chan)->write_filename,NULL,NULL) > 0) {
					snprintf(filename, FILENAME_MAX, "%s-out", ast_channel_monitor(chan)->filename_base);
					if (ast_fileexists(filename, NULL, NULL) > 0) {
						ast_filedelete(filename, NULL);
					}
					ast_filerename(ast_channel_monitor(chan)->write_filename, filename, ast_channel_monitor(chan)->format);
				} else {
					ast_log(LOG_WARNING, "File %s not found\n", ast_channel_monitor(chan)->write_filename);
				}
			}
		}

		if (ast_channel_monitor(chan)->joinfiles && !ast_strlen_zero(ast_channel_monitor(chan)->filename_base)) {
			char tmp[1024];
			char tmp2[1024];
			const char *format = !strcasecmp(ast_channel_monitor(chan)->format,"wav49") ? "WAV" : ast_channel_monitor(chan)->format;
			char *fname_base = ast_channel_monitor(chan)->filename_base;
			const char *execute, *execute_args;
			/* at this point, fname_base really is the full path */

			/* Set the execute application */
			execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
			if (ast_strlen_zero(execute)) {
#ifdef HAVE_SOXMIX
				execute = "nice -n 19 soxmix";
#else
				execute = "nice -n 19 sox -m";
#endif
				format = get_soxmix_format(format);
				delfiles = 1;
			} 
			execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
			if (ast_strlen_zero(execute_args)) {
				execute_args = "";
			}
			
			snprintf(tmp, sizeof(tmp), "%s \"%s-in.%s\" \"%s-out.%s\" \"%s.%s\" %s &",
				execute, fname_base, format, fname_base, format, fname_base, format,execute_args);
			if (delfiles) {
				snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s-\"* ) &",tmp, fname_base); /* remove legs when done mixing */
				ast_copy_string(tmp, tmp2, sizeof(tmp));
			}
			ast_debug(1,"monitor executing %s\n",tmp);
			if (ast_safe_system(tmp) == -1)
				ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
		}
		
		ast_free(ast_channel_monitor(chan)->format);
		ast_free(ast_channel_monitor(chan));
		ast_channel_monitor_set(chan, NULL);

		ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStop",
			                "Channel: %s\r\n"
	                        "Uniqueid: %s\r\n",
	                        ast_channel_name(chan),
	                        ast_channel_uniqueid(chan)
	                        );
		pbx_builtin_setvar_helper(chan, "MONITORED", NULL);
	}
	pbx_builtin_setvar_helper(chan, "AUTO_MONITOR", NULL);

	UNLOCK_IF_NEEDED(chan, need_lock);

	return 0;
}
Beispiel #2
0
static int common_exec(struct ast_channel *chan, struct ast_flags *flags,
	int volfactor, const int fd, struct spy_dtmf_options *user_options,
	const char *mygroup, const char *myenforced, const char *spec, const char *exten,
	const char *context, const char *mailbox, const char *name_context)
{
	char nameprefix[AST_NAME_STRLEN];
	char peer_name[AST_NAME_STRLEN + 5];
	char exitcontext[AST_MAX_CONTEXT] = "";
	signed char zero_volume = 0;
	int waitms;
	int res;
	char *ptr;
	int num;
	int num_spyed_upon = 1;
	struct ast_channel_iterator *iter = NULL;

	if (ast_test_flag(flags, OPTION_EXIT)) {
		const char *c;
		ast_channel_lock(chan);
		if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT"))) {
			ast_copy_string(exitcontext, c, sizeof(exitcontext));
		} else if (!ast_strlen_zero(chan->macrocontext)) {
			ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
		} else {
			ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
		}
		ast_channel_unlock(chan);
	}

	if (chan->_state != AST_STATE_UP)
		ast_answer(chan);

	ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */

	waitms = 100;

	for (;;) {
		struct ast_autochan *autochan = NULL, *next_autochan = NULL;
		struct ast_channel *prev = NULL;

		if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
			res = ast_streamfile(chan, "beep", chan->language);
			if (!res)
				res = ast_waitstream(chan, "");
			else if (res < 0) {
				ast_clear_flag(chan, AST_FLAG_SPYING);
				break;
			}
			if (!ast_strlen_zero(exitcontext)) {
				char tmp[2];
				tmp[0] = res;
				tmp[1] = '\0';
				if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
					goto exit;
				else
					ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
			}
		}

		/* Set up the iterator we'll be using during this call */
		if (!ast_strlen_zero(spec)) {
			iter = ast_channel_iterator_by_name_new(spec, strlen(spec));
		} else if (!ast_strlen_zero(exten)) {
			iter = ast_channel_iterator_by_exten_new(exten, context);
		} else {
			iter = ast_channel_iterator_all_new();
		}

		if (!iter) {
			return -1;
		}

		res = ast_waitfordigit(chan, waitms);
		if (res < 0) {
			ast_clear_flag(chan, AST_FLAG_SPYING);
			break;
		}
		if (!ast_strlen_zero(exitcontext)) {
			char tmp[2];
			tmp[0] = res;
			tmp[1] = '\0';
			if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
				goto exit;
			else
				ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
		}

		/* reset for the next loop around, unless overridden later */
		waitms = 100;
		num_spyed_upon = 0;

		for (autochan = next_channel(iter, autochan, chan);
		     autochan;
			 prev = autochan->chan, ast_autochan_destroy(autochan),
		     autochan = next_autochan ? next_autochan : 
				next_channel(iter, autochan, chan), next_autochan = NULL) {
			int igrp = !mygroup;
			int ienf = !myenforced;
			char *s;

			if (autochan->chan == prev) {
				ast_autochan_destroy(autochan);
				break;
			}

			if (ast_check_hangup(chan)) {
				ast_autochan_destroy(autochan);
				break;
			}

			if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(autochan->chan)) {
				continue;
			}

			if (ast_check_hangup(autochan->chan) || ast_test_flag(autochan->chan, AST_FLAG_SPYING)) {
				continue;
			}

			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];
				const char *group = NULL;
				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");
				}

				if (!ast_strlen_zero(group)) {
					ast_copy_string(dup_group, group, sizeof(dup_group));
					num_groups = ast_app_separate_args(dup_group, ':', groups,
						ARRAY_LEN(groups));
				}

				for (y = 0; y < num_mygroups; y++) {
					for (x = 0; x < num_groups; x++) {
						if (!strcmp(mygroups[y], groups[x])) {
							igrp = 1;
							break;
						}
					}
				}
			}

			if (!igrp) {
				continue;
			}
			if (myenforced) {
				char ext[AST_CHANNEL_NAME + 3];
				char buffer[512];
				char *end;

				snprintf(buffer, sizeof(buffer) - 1, ":%s:", myenforced);

				ast_copy_string(ext + 1, autochan->chan->name, sizeof(ext) - 1);
				if ((end = strchr(ext, '-'))) {
					*end++ = ':';
					*end = '\0';
				}

				ext[0] = ':';

				if (strcasestr(buffer, ext)) {
					ienf = 1;
				}
			}

			if (!ienf) {
				continue;
			}

			strcpy(peer_name, "spy-");
			strncat(peer_name, autochan->chan->name, AST_NAME_STRLEN - 4 - 1);
			ptr = strchr(peer_name, '/');
			*ptr++ = '\0';
			ptr = strsep(&ptr, "-");

			for (s = peer_name; s < ptr; s++)
				*s = tolower(*s);

			if (!ast_test_flag(flags, OPTION_QUIET)) {
				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 = ast_app_sayname(chan, local_mailbox, local_context);
				}
				if (!ast_test_flag(flags, OPTION_NAME) || res < 0) {
					if (!ast_test_flag(flags, OPTION_NOTECH)) {
						if (ast_fileexists(peer_name, NULL, NULL) > 0) {
							res = ast_streamfile(chan, peer_name, chan->language);
							if (!res) {
								res = ast_waitstream(chan, "");
							}
							if (res) {
								ast_autochan_destroy(autochan);
								break;
							}
						} else {
							res = ast_say_character_str(chan, peer_name, "", chan->language);
						}
					}
					if ((num = atoi(ptr)))
						ast_say_digits(chan, atoi(ptr), "", chan->language);
				}
			}

			res = channel_spy(chan, autochan, &volfactor, fd, user_options, flags, exitcontext);
			num_spyed_upon++;

			if (res == -1) {
				ast_autochan_destroy(autochan);
				goto exit;
			} else if (res == -2) {
				res = 0;
				ast_autochan_destroy(autochan);
				goto exit;
			} else if (res > 1 && spec) {
				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);
				} else {
					/* 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 */
						next_autochan = NULL;
					}
				}
			} else if (res == 0 && ast_test_flag(flags, OPTION_EXITONHANGUP)) {
				goto exit;
			}
		}

		iter = ast_channel_iterator_destroy(iter);

		if (res == -1 || ast_check_hangup(chan))
			break;
		if (ast_test_flag(flags, OPTION_STOP) && !next_autochan) {
			break;
		}
	}
exit:

	ast_clear_flag(chan, AST_FLAG_SPYING);

	ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);

	return res;
}
/*! \brief Start monitoring a channel
 * \param chan ast_channel struct to record
 * \param format_spec file format to use for recording
 * \param fname_base filename base to record to
 * \param need_lock whether to lock the channel mutex
 * \param stream_action whether to record the input and/or output streams.  X_REC_IN | X_REC_OUT is most often used
 * Creates the file to record, if no format is specified it assumes WAV
 * It also sets channel variable __MONITORED=yes
 * \retval 0 on success
 * \retval -1 on failure
 */
int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec,
					     const char *fname_base, int need_lock, int stream_action)
{
	int res = 0;

	LOCK_IF_NEEDED(chan, need_lock);

	if (!(ast_channel_monitor(chan))) {
		struct ast_channel_monitor *monitor;
		char *channel_name, *p;

		/* Create monitoring directory if needed */
		ast_mkdir(ast_config_AST_MONITOR_DIR, 0777);

		if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
			UNLOCK_IF_NEEDED(chan, need_lock);
			return -1;
		}

		/* Determine file names */
		if (!ast_strlen_zero(fname_base)) {
			int directory = strchr(fname_base, '/') ? 1 : 0;
			const char *absolute = *fname_base == '/' ? "" : ast_config_AST_MONITOR_DIR;
			const char *absolute_suffix = *fname_base == '/' ? "" : "/";

			snprintf(monitor->read_filename, FILENAME_MAX, "%s%s%s-in",
						absolute, absolute_suffix, fname_base);
			snprintf(monitor->write_filename, FILENAME_MAX, "%s%s%s-out",
						absolute, absolute_suffix, fname_base);
			snprintf(monitor->filename_base, FILENAME_MAX, "%s%s%s",
					 	absolute, absolute_suffix, fname_base);

			/* try creating the directory just in case it doesn't exist */
			if (directory) {
				char *name = ast_strdupa(monitor->filename_base);
				ast_mkdir(dirname(name), 0777);
			}
		} else {
			ast_mutex_lock(&monitorlock);
			snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%lu",
						ast_config_AST_MONITOR_DIR, seq);
			snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%lu",
						ast_config_AST_MONITOR_DIR, seq);
			seq++;
			ast_mutex_unlock(&monitorlock);

			/* Replace all '/' chars from the channel name with '-' chars. */
			channel_name = ast_strdupa(ast_channel_name(chan));
			for (p = channel_name; (p = strchr(p, '/')); ) {
				*p = '-';
			}

			snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
					 ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
			monitor->filename_changed = 1;
		}

		monitor->stop = ast_monitor_stop;

		/* Determine file format */
		if (!ast_strlen_zero(format_spec)) {
			monitor->format = ast_strdup(format_spec);
		} else {
			monitor->format = ast_strdup("wav");
		}
		
		/* open files */
		if (stream_action & X_REC_IN) {
			if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0)
				ast_filedelete(monitor->read_filename, NULL);
			if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
							monitor->format, NULL,
							O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
				ast_log(LOG_WARNING, "Could not create file %s\n",
							monitor->read_filename);
				ast_free(monitor);
				UNLOCK_IF_NEEDED(chan, need_lock);
				return -1;
			}
		} else
			monitor->read_stream = NULL;

		if (stream_action & X_REC_OUT) {
			if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
				ast_filedelete(monitor->write_filename, NULL);
			}
			if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
							monitor->format, NULL,
							O_CREAT|O_TRUNC|O_WRONLY, 0, AST_FILE_MODE))) {
				ast_log(LOG_WARNING, "Could not create file %s\n",
							monitor->write_filename);
				if (monitor->read_stream) {
					ast_closestream(monitor->read_stream);
				}
				ast_free(monitor);
				UNLOCK_IF_NEEDED(chan, need_lock);
				return -1;
			}
		} else
			monitor->write_stream = NULL;

		ast_channel_monitor_set(chan, monitor);
		ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
		/* so we know this call has been monitored in case we need to bill for it or something */
		pbx_builtin_setvar_helper(chan, "__MONITORED","true");

		ast_manager_event(chan, EVENT_FLAG_CALL, "MonitorStart",
			                "Channel: %s\r\n"
					        "Uniqueid: %s\r\n",
	                        ast_channel_name(chan),
			                ast_channel_uniqueid(chan));
	} else {
		ast_debug(1,"Cannot start monitoring %s, already monitored\n", ast_channel_name(chan));
		res = -1;
	}

	UNLOCK_IF_NEEDED(chan, need_lock);

	return res;
}
/* Stop monitoring a channel */
int ast_monitor_stop(struct ast_channel *chan, int need_lock)
{
	int delfiles = 0;

	LOCK_IF_NEEDED(chan, need_lock);

	if (chan->monitor) {
		char filename[ FILENAME_MAX ];

		if (chan->monitor->read_stream) {
			ast_closestream(chan->monitor->read_stream);
		}
		if (chan->monitor->write_stream) {
			ast_closestream(chan->monitor->write_stream);
		}

		if (chan->monitor->filename_changed && !ast_strlen_zero(chan->monitor->filename_base)) {
			if (ast_fileexists(chan->monitor->read_filename,NULL,NULL) > 0) {
				snprintf(filename, FILENAME_MAX, "%s-in", chan->monitor->filename_base);
				if (ast_fileexists(filename, NULL, NULL) > 0) {
					ast_filedelete(filename, NULL);
				}
				ast_filerename(chan->monitor->read_filename, filename, chan->monitor->format);
			} else {
				ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->read_filename);
			}

			if (ast_fileexists(chan->monitor->write_filename,NULL,NULL) > 0) {
				snprintf(filename, FILENAME_MAX, "%s-out", chan->monitor->filename_base);
				if (ast_fileexists(filename, NULL, NULL) > 0) {
					ast_filedelete(filename, NULL);
				}
				ast_filerename(chan->monitor->write_filename, filename, chan->monitor->format);
			} else {
				ast_log(LOG_WARNING, "File %s not found\n", chan->monitor->write_filename);
			}
		}

		if (chan->monitor->joinfiles && !ast_strlen_zero(chan->monitor->filename_base)) {
			char tmp[1024];
			char tmp2[1024];
			char *format = !strcasecmp(chan->monitor->format,"wav49") ? "WAV" : chan->monitor->format;
			char *name = chan->monitor->filename_base;
			int directory = strchr(name, '/') ? 1 : 0;
			char *dir = directory ? "" : ast_config_AST_MONITOR_DIR;
			const char *execute, *execute_args;

			/* Set the execute application */
			execute = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC");
			if (ast_strlen_zero(execute)) { 
				execute = "nice -n 19 soxmix";
				delfiles = 1;
			} 
			execute_args = pbx_builtin_getvar_helper(chan, "MONITOR_EXEC_ARGS");
			if (ast_strlen_zero(execute_args)) {
				execute_args = "";
			}
			
			snprintf(tmp, sizeof(tmp), "%s \"%s/%s-in.%s\" \"%s/%s-out.%s\" \"%s/%s.%s\" %s &", execute, dir, name, format, dir, name, format, dir, name, format,execute_args);
			if (delfiles) {
				snprintf(tmp2,sizeof(tmp2), "( %s& rm -f \"%s/%s-\"* ) &",tmp, dir ,name); /* remove legs when done mixing */
				ast_copy_string(tmp, tmp2, sizeof(tmp));
			}
			ast_log(LOG_DEBUG,"monitor executing %s\n",tmp);
			if (ast_safe_system(tmp) == -1)
				ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp);
		}
		
		free(chan->monitor->format);
		free(chan->monitor);
		chan->monitor = NULL;
	}

	UNLOCK_IF_NEEDED(chan, need_lock);

	return 0;
}
struct stasis_app_recording *stasis_app_control_record(
	struct stasis_app_control *control,
	struct stasis_app_recording_options *options)
{
	struct stasis_app_recording *recording;
	char *last_slash;

	errno = 0;

	if (options == NULL ||
		ast_strlen_zero(options->name) ||
		ast_strlen_zero(options->format) ||
		options->max_silence_seconds < 0 ||
		options->max_duration_seconds < 0) {
		errno = EINVAL;
		return NULL;
	}

	ast_debug(3, "%s: Sending record(%s.%s) command\n",
		stasis_app_control_get_channel_id(control), options->name,
		options->format);

	recording = ao2_alloc(sizeof(*recording), recording_dtor);
	if (!recording) {
		errno = ENOMEM;
		return NULL;
	}
	recording->duration.total = -1;
	recording->duration.energy_only = -1;

	ast_asprintf(&recording->absolute_name, "%s/%s",
		ast_config_AST_RECORDING_DIR, options->name);

	if (recording->absolute_name == NULL) {
		errno = ENOMEM;
		ao2_ref(recording, -1);
		return NULL;
	}

	if ((last_slash = strrchr(recording->absolute_name, '/'))) {
		*last_slash = '\0';
		if (ast_safe_mkdir(ast_config_AST_RECORDING_DIR,
				recording->absolute_name, 0777) != 0) {
			/* errno set by ast_mkdir */
			ao2_ref(recording, -1);
			return NULL;
		}
		*last_slash = '/';
	}

	ao2_ref(options, +1);
	recording->options = options;
	recording->control = control;
	recording->state = STASIS_APP_RECORDING_STATE_QUEUED;

	if ((recording->options->if_exists == AST_RECORD_IF_EXISTS_FAIL) &&
			(ast_fileexists(recording->absolute_name, NULL, NULL))) {
		ast_log(LOG_WARNING, "Recording file '%s' already exists and ifExists option is failure.\n",
			recording->absolute_name);
		errno = EEXIST;
		ao2_ref(recording, -1);
		return NULL;
	}

	{
		RAII_VAR(struct stasis_app_recording *, old_recording, NULL,
			ao2_cleanup);

		SCOPED_AO2LOCK(lock, recordings);

		old_recording = ao2_find(recordings, options->name,
			OBJ_KEY | OBJ_NOLOCK);
		if (old_recording) {
			ast_log(LOG_WARNING,
				"Recording %s already in progress\n",
				recording->options->name);
			errno = EEXIST;
			ao2_ref(recording, -1);
			return NULL;
		}
		ao2_link(recordings, recording);
	}

	stasis_app_control_register_add_rule(control, &rule_recording);

	stasis_app_send_command_async(control, record_file, ao2_bump(recording), recording_cleanup);

	return recording;
}
static int app_exec(struct ast_channel *chan, void *data)
{
	struct ast_module_user *lu;
	struct playlist_entry *entry;
	const char *args = data;
	int child_stdin[2] = { 0,0 };
	int child_stdout[2] = { 0,0 };
	int child_stderr[2] = { 0,0 };
	int res = -1;
	int test_available_fd = -1;
	int gen_active = 0;
	int pid;
	char *argv[32];
	int argc = 1;
	char *buf, *command;
	FILE *child_commands = NULL;
	FILE *child_errors = NULL;
	FILE *child_events = NULL;
	struct ivr_localuser foo = {
		.playlist = AST_LIST_HEAD_INIT_VALUE,
		.finishlist = AST_LIST_HEAD_INIT_VALUE,
	};
	struct ivr_localuser *u = &foo;
	sigset_t fullset, oldset;

	lu = ast_module_user_add(chan);

	sigfillset(&fullset);
	pthread_sigmask(SIG_BLOCK, &fullset, &oldset);

	u->abort_current_sound = 0;
	u->chan = chan;
	
	if (ast_strlen_zero(args)) {
		ast_log(LOG_WARNING, "ExternalIVR requires a command to execute\n");
		ast_module_user_remove(lu);
		return -1;	
	}

	buf = ast_strdupa(data);

	argc = ast_app_separate_args(buf, '|', argv, sizeof(argv) / sizeof(argv[0]));

	if (pipe(child_stdin)) {
		ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child input: %s\n", strerror(errno));
		goto exit;
	}

	if (pipe(child_stdout)) {
		ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child output: %s\n", strerror(errno));
		goto exit;
	}

	if (pipe(child_stderr)) {
		ast_chan_log(LOG_WARNING, chan, "Could not create pipe for child errors: %s\n", strerror(errno));
		goto exit;
	}

	if (chan->_state != AST_STATE_UP) {
		ast_answer(chan);
	}

	if (ast_activate_generator(chan, &gen, u) < 0) {
		ast_chan_log(LOG_WARNING, chan, "Failed to activate generator\n");
		goto exit;
	} else
		gen_active = 1;

	pid = fork();
	if (pid < 0) {
		ast_log(LOG_WARNING, "Failed to fork(): %s\n", strerror(errno));
		goto exit;
	}

	if (!pid) {
		/* child process */
		int i;

		signal(SIGPIPE, SIG_DFL);
		pthread_sigmask(SIG_UNBLOCK, &fullset, NULL);

		if (ast_opt_high_priority)
			ast_set_priority(0);

		dup2(child_stdin[0], STDIN_FILENO);
		dup2(child_stdout[1], STDOUT_FILENO);
		dup2(child_stderr[1], STDERR_FILENO);
		for (i = STDERR_FILENO + 1; i < 1024; i++)
			close(i);
		execv(argv[0], argv);
		fprintf(stderr, "Failed to execute '%s': %s\n", argv[0], strerror(errno));
		_exit(1);
	} else {
		/* parent process */
		int child_events_fd = child_stdin[1];
		int child_commands_fd = child_stdout[0];
		int child_errors_fd = child_stderr[0];
		struct ast_frame *f;
		int ms;
		int exception;
		int ready_fd;
		int waitfds[2] = { child_errors_fd, child_commands_fd };
		struct ast_channel *rchan;

		pthread_sigmask(SIG_SETMASK, &oldset, NULL);

		close(child_stdin[0]);
		child_stdin[0] = 0;
		close(child_stdout[1]);
		child_stdout[1] = 0;
		close(child_stderr[1]);
		child_stderr[1] = 0;

		if (!(child_events = fdopen(child_events_fd, "w"))) {
			ast_chan_log(LOG_WARNING, chan, "Could not open stream for child events\n");
			goto exit;
		}

		if (!(child_commands = fdopen(child_commands_fd, "r"))) {
			ast_chan_log(LOG_WARNING, chan, "Could not open stream for child commands\n");
			goto exit;
		}

		if (!(child_errors = fdopen(child_errors_fd, "r"))) {
			ast_chan_log(LOG_WARNING, chan, "Could not open stream for child errors\n");
			goto exit;
		}

		test_available_fd = open("/dev/null", O_RDONLY);

		setvbuf(child_events, NULL, _IONBF, 0);
		setvbuf(child_commands, NULL, _IONBF, 0);
		setvbuf(child_errors, NULL, _IONBF, 0);

		res = 0;

		while (1) {
			if (ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
				ast_chan_log(LOG_NOTICE, chan, "Is a zombie\n");
				res = -1;
				break;
			}

			if (ast_check_hangup(chan)) {
				ast_chan_log(LOG_NOTICE, chan, "Got check_hangup\n");
				send_child_event(child_events, 'H', NULL, chan);
				res = -1;
				break;
			}

			ready_fd = 0;
			ms = 100;
			errno = 0;
			exception = 0;

			rchan = ast_waitfor_nandfds(&chan, 1, waitfds, 2, &exception, &ready_fd, &ms);

			if (!AST_LIST_EMPTY(&u->finishlist)) {
				AST_LIST_LOCK(&u->finishlist);
				while ((entry = AST_LIST_REMOVE_HEAD(&u->finishlist, list))) {
					send_child_event(child_events, 'F', entry->filename, chan);
					free(entry);
				}
				AST_LIST_UNLOCK(&u->finishlist);
			}

			if (rchan) {
				/* the channel has something */
				f = ast_read(chan);
				if (!f) {
					ast_chan_log(LOG_NOTICE, chan, "Returned no frame\n");
					send_child_event(child_events, 'H', NULL, chan);
					res = -1;
					break;
				}

				if (f->frametype == AST_FRAME_DTMF) {
					send_child_event(child_events, f->subclass, NULL, chan);
					if (u->option_autoclear) {
						if (!u->abort_current_sound && !u->playing_silence)
							send_child_event(child_events, 'T', NULL, chan);
						AST_LIST_LOCK(&u->playlist);
						while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
							send_child_event(child_events, 'D', entry->filename, chan);
							free(entry);
						}
						if (!u->playing_silence)
							u->abort_current_sound = 1;
						AST_LIST_UNLOCK(&u->playlist);
					}
				} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
					ast_chan_log(LOG_NOTICE, chan, "Got AST_CONTROL_HANGUP\n");
					send_child_event(child_events, 'H', NULL, chan);
					ast_frfree(f);
					res = -1;
					break;
				}
				ast_frfree(f);
			} else if (ready_fd == child_commands_fd) {
				char input[1024];

				if (exception || feof(child_commands)) {
					ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
					res = -1;
					break;
				}

				if (!fgets(input, sizeof(input), child_commands))
					continue;

				command = ast_strip(input);

				ast_chan_log(LOG_DEBUG, chan, "got command '%s'\n", input);

				if (strlen(input) < 4)
					continue;

				if (input[0] == 'S') {
					if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
						ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
						send_child_event(child_events, 'Z', NULL, chan);
						strcpy(&input[2], "exception");
					}
					if (!u->abort_current_sound && !u->playing_silence)
						send_child_event(child_events, 'T', NULL, chan);
					AST_LIST_LOCK(&u->playlist);
					while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list))) {
						send_child_event(child_events, 'D', entry->filename, chan);
						free(entry);
					}
					if (!u->playing_silence)
						u->abort_current_sound = 1;
					entry = make_entry(&input[2]);
					if (entry)
						AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
					AST_LIST_UNLOCK(&u->playlist);
				} else if (input[0] == 'A') {
					if (ast_fileexists(&input[2], NULL, u->chan->language) == -1) {
						ast_chan_log(LOG_WARNING, chan, "Unknown file requested '%s'\n", &input[2]);
						send_child_event(child_events, 'Z', NULL, chan);
						strcpy(&input[2], "exception");
					}
					entry = make_entry(&input[2]);
					if (entry) {
						AST_LIST_LOCK(&u->playlist);
						AST_LIST_INSERT_TAIL(&u->playlist, entry, list);
						AST_LIST_UNLOCK(&u->playlist);
					}
				} else if (input[0] == 'H') {
					ast_chan_log(LOG_NOTICE, chan, "Hanging up: %s\n", &input[2]);
					send_child_event(child_events, 'H', NULL, chan);
					break;
				} else if (input[0] == 'O') {
					if (!strcasecmp(&input[2], "autoclear"))
						u->option_autoclear = 1;
					else if (!strcasecmp(&input[2], "noautoclear"))
						u->option_autoclear = 0;
					else
						ast_chan_log(LOG_WARNING, chan, "Unknown option requested '%s'\n", &input[2]);
				}
			} else if (ready_fd == child_errors_fd) {
				char input[1024];

				if (exception || (dup2(child_commands_fd, test_available_fd) == -1) || feof(child_errors)) {
					ast_chan_log(LOG_WARNING, chan, "Child process went away\n");
					res = -1;
					break;
				}

				if (fgets(input, sizeof(input), child_errors)) {
					command = ast_strip(input);
					ast_chan_log(LOG_NOTICE, chan, "stderr: %s\n", command);
				}
			} else if ((ready_fd < 0) && ms) { 
				if (errno == 0 || errno == EINTR)
					continue;

				ast_chan_log(LOG_WARNING, chan, "Wait failed (%s)\n", strerror(errno));
				break;
			}
		}
	}

 exit:
	if (gen_active)
		ast_deactivate_generator(chan);

	if (child_events)
		fclose(child_events);

	if (child_commands)
		fclose(child_commands);

	if (child_errors)
		fclose(child_errors);

	if (test_available_fd > -1) {
		close(test_available_fd);
	}

	if (child_stdin[0])
		close(child_stdin[0]);

	if (child_stdin[1])
		close(child_stdin[1]);

	if (child_stdout[0])
		close(child_stdout[0]);

	if (child_stdout[1])
		close(child_stdout[1]);

	if (child_stderr[0])
		close(child_stderr[0]);

	if (child_stderr[1])
		close(child_stderr[1]);

	while ((entry = AST_LIST_REMOVE_HEAD(&u->playlist, list)))
		free(entry);

	ast_module_user_remove(lu);

	return res;
}
/* Start monitoring a channel */
int ast_monitor_start(	struct ast_channel *chan, const char *format_spec,
		const char *fname_base, int need_lock)
{
	int res = 0;
	char tmp[256];

	LOCK_IF_NEEDED(chan, need_lock);

	if (!(chan->monitor)) {
		struct ast_channel_monitor *monitor;
		char *channel_name, *p;

		/* Create monitoring directory if needed */
		if (mkdir(ast_config_AST_MONITOR_DIR, 0770) < 0) {
			if (errno != EEXIST) {
				ast_log(LOG_WARNING, "Unable to create audio monitor directory: %s\n",
					strerror(errno));
			}
		}

		if (!(monitor = ast_calloc(1, sizeof(*monitor)))) {
			UNLOCK_IF_NEEDED(chan, need_lock);
			return -1;
		}

		/* Determine file names */
		if (!ast_strlen_zero(fname_base)) {
			int directory = strchr(fname_base, '/') ? 1 : 0;
			/* try creating the directory just in case it doesn't exist */
			if (directory) {
				char *name = strdup(fname_base);
				snprintf(tmp, sizeof(tmp), "mkdir -p \"%s\"",dirname(name));
				free(name);
				ast_safe_system(tmp);
			}
			snprintf(monitor->read_filename, FILENAME_MAX, "%s/%s-in",
						directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
			snprintf(monitor->write_filename, FILENAME_MAX, "%s/%s-out",
						directory ? "" : ast_config_AST_MONITOR_DIR, fname_base);
			ast_copy_string(monitor->filename_base, fname_base, sizeof(monitor->filename_base));
		} else {
			ast_mutex_lock(&monitorlock);
			snprintf(monitor->read_filename, FILENAME_MAX, "%s/audio-in-%ld",
						ast_config_AST_MONITOR_DIR, seq);
			snprintf(monitor->write_filename, FILENAME_MAX, "%s/audio-out-%ld",
						ast_config_AST_MONITOR_DIR, seq);
			seq++;
			ast_mutex_unlock(&monitorlock);

			channel_name = ast_strdupa(chan->name);
			while ((p = strchr(channel_name, '/'))) {
				*p = '-';
			}
			snprintf(monitor->filename_base, FILENAME_MAX, "%s/%d-%s",
					 ast_config_AST_MONITOR_DIR, (int)time(NULL), channel_name);
			monitor->filename_changed = 1;
		}

		monitor->stop = ast_monitor_stop;

		/* Determine file format */
		if (!ast_strlen_zero(format_spec)) {
			monitor->format = strdup(format_spec);
		} else {
			monitor->format = strdup("wav");
		}
		
		/* open files */
		if (ast_fileexists(monitor->read_filename, NULL, NULL) > 0) {
			ast_filedelete(monitor->read_filename, NULL);
		}
		if (!(monitor->read_stream = ast_writefile(monitor->read_filename,
						monitor->format, NULL,
						O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
			ast_log(LOG_WARNING, "Could not create file %s\n",
						monitor->read_filename);
			free(monitor);
			ast_channel_unlock(chan);
			return -1;
		}
		if (ast_fileexists(monitor->write_filename, NULL, NULL) > 0) {
			ast_filedelete(monitor->write_filename, NULL);
		}
		if (!(monitor->write_stream = ast_writefile(monitor->write_filename,
						monitor->format, NULL,
						O_CREAT|O_TRUNC|O_WRONLY, 0, 0644))) {
			ast_log(LOG_WARNING, "Could not create file %s\n",
						monitor->write_filename);
			ast_closestream(monitor->read_stream);
			free(monitor);
			ast_channel_unlock(chan);
			return -1;
		}
		chan->monitor = monitor;
		ast_monitor_set_state(chan, AST_MONITOR_RUNNING);
		/* so we know this call has been monitored in case we need to bill for it or something */
		pbx_builtin_setvar_helper(chan, "__MONITORED","true");
	} else {
		ast_log(LOG_DEBUG,"Cannot start monitoring %s, already monitored\n",
					chan->name);
		res = -1;
	}

	UNLOCK_IF_NEEDED(chan, need_lock);

	return res;
}
/* play name of mailbox owner.
 * returns:  -1 for bad or missing extension
 *           '1' for selected entry from directory
 *           '*' for skipped entry from directory
 */
static int play_mailbox_owner(struct ast_channel *chan, char *context, char *dialcontext, char *ext, char *name, int fromappvm) {
	int res = 0;
	int loop = 3;
	char fn[256];
	char fn2[256];

	/* Check for the VoiceMail2 greeting first */
	snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
		(char *)ast_config_AST_SPOOL_DIR, context, ext);
#ifdef USE_ODBC_STORAGE
	retrieve_file(fn);
#endif

	/* Otherwise, check for an old-style Voicemail greeting */
	snprintf(fn2, sizeof(fn2), "%s/vm/%s/greet",
		(char *)ast_config_AST_SPOOL_DIR, ext);
#ifdef USE_ODBC_STORAGE
	retrieve_file(fn2);
#endif

	if (ast_fileexists(fn, NULL, chan->language) > 0) {
		res = ast_streamfile(chan, fn, chan->language);
		if (!res) {
			res = ast_waitstream(chan, AST_DIGIT_ANY);
		}
		ast_stopstream(chan);
	} else if (ast_fileexists(fn2, NULL, chan->language) > 0) {
		res = ast_streamfile(chan, fn2, chan->language);
		if (!res) {
			res = ast_waitstream(chan, AST_DIGIT_ANY);
		}
		ast_stopstream(chan);
	} else {
		res = ast_say_character_str(chan, !ast_strlen_zero(name) ? name : ext,
					AST_DIGIT_ANY, chan->language);
	}
#ifdef USE_ODBC_STORAGE
	ast_filedelete(fn, NULL);	
	ast_filedelete(fn2, NULL);	
#endif

	while (loop) {
		if (!res) {
			res = ast_streamfile(chan, "dir-instr", chan->language);
		}
		if (!res) {
			res = ast_waitstream(chan, AST_DIGIT_ANY);
		}
		if (!res) {
			res = ast_waitfordigit(chan, 3000);
		}
		ast_stopstream(chan);
	
		if (res > -1) {
			switch (res) {
				case '1':
					/* Name selected */
					loop = 0;
					if (fromappvm) {
						/* We still want to set the exten */
						ast_copy_string(chan->exten, ext, sizeof(chan->exten));
					} else {
						if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
							ast_log(LOG_WARNING,
								"Can't find extension '%s' in context '%s'.  "
								"Did you pass the wrong context to Directory?\n",
								ext, dialcontext);
							res = -1;
						}
					}
					break;
	
				case '*':   
					/* Skip to next match in list */
					loop = 0;
					break;
	
				default:
					/* Not '1', or '*', so decrement number of tries */
					res = 0;
					loop--;
					break;
			} /* end switch */
		} /* end if */
		else {
			/* User hungup, so jump out now */
			loop = 0;
		}
	} /* end while */

	return(res);
}
/* play name of mailbox owner.
 * returns:  -1 for bad or missing extension
 *           '1' for selected entry from directory
 *           '*' for skipped entry from directory
 */
static int play_mailbox_owner(struct ast_channel *chan, char *context,
		char *dialcontext, char *ext, char *name, int readext,
		int fromappvm)
{
	int res = 0;
	int loop;
	char fn[256];

	/* Check for the VoiceMail2 greeting first */
	snprintf(fn, sizeof(fn), "%s/voicemail/%s/%s/greet",
		ast_config_AST_SPOOL_DIR, context, ext);
#ifdef ODBC_STORAGE
	retrieve_file(fn);
#endif

	if (ast_fileexists(fn, NULL, chan->language) <= 0) {
		/* no file, check for an old-style Voicemail greeting */
		snprintf(fn, sizeof(fn), "%s/vm/%s/greet",
			ast_config_AST_SPOOL_DIR, ext);
	}
#ifdef ODBC_STORAGE
	retrieve_file(fn);
#endif

	if (ast_fileexists(fn, NULL, chan->language) > 0) {
		res = ast_stream_and_wait(chan, fn, chan->language, AST_DIGIT_ANY);
		ast_stopstream(chan);
		/* If Option 'e' was specified, also read the extension number with the name */
		if (readext) {
			ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
		}
	} else {
		res = ast_say_character_str(chan, S_OR(name, ext), AST_DIGIT_ANY, chan->language);
		if (!ast_strlen_zero(name) && readext) {
			ast_stream_and_wait(chan, "vm-extension", chan->language, AST_DIGIT_ANY);
			res = ast_say_character_str(chan, ext, AST_DIGIT_ANY, chan->language);
		}
	}
#ifdef ODBC_STORAGE
	ast_filedelete(fn, NULL);	
#endif

	for (loop = 3 ; loop > 0; loop--) {
		if (!res)
			res = ast_stream_and_wait(chan, "dir-instr", chan->language, AST_DIGIT_ANY);
		if (!res)
			res = ast_waitfordigit(chan, 3000);
		ast_stopstream(chan);
	
		if (res < 0) /* User hungup, so jump out now */
			break;
		if (res == '1') {	/* Name selected */
			if (fromappvm) {
				/* We still want to set the exten though */
				ast_copy_string(chan->exten, ext, sizeof(chan->exten));
			} else {
				if (ast_goto_if_exists(chan, dialcontext, ext, 1)) {
					ast_log(LOG_WARNING,
						"Can't find extension '%s' in context '%s'.  "
						"Did you pass the wrong context to Directory?\n",
						ext, dialcontext);
					res = -1;
				}
			}
			break;
		}
		if (res == '*') /* Skip to next match in list */
			break;

		/* Not '1', or '*', so decrement number of tries */
		res = 0;
	}

	return(res);
}
Beispiel #10
0
static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
	int volfactor, const int fd, const char *mygroup, const char *myenforced,
	const char *spec, const char *exten, const char *context)
{
	char nameprefix[AST_NAME_STRLEN];
	char peer_name[AST_NAME_STRLEN + 5];
	char exitcontext[AST_MAX_CONTEXT] = "";
	signed char zero_volume = 0;
	int waitms;
	int res;
	char *ptr;
	int num;
	int num_spyed_upon = 1;
	struct chanspy_ds chanspy_ds;

	if (ast_test_flag(flags, OPTION_EXIT)) {
		const char *c;
		if ((c = pbx_builtin_getvar_helper(chan, "SPY_EXIT_CONTEXT")))
			ast_copy_string(exitcontext, c, sizeof(exitcontext));
		else if (!ast_strlen_zero(chan->macrocontext))
			ast_copy_string(exitcontext, chan->macrocontext, sizeof(exitcontext));
		else
			ast_copy_string(exitcontext, chan->context, sizeof(exitcontext));
	}

	ast_mutex_init(&chanspy_ds.lock);

	snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));

	if (chan->_state != AST_STATE_UP)
		ast_answer(chan);

	ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */

	waitms = 100;

	for (;;) {
		struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
		struct ast_channel *prev = NULL, *peer = NULL;

		if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
			res = ast_streamfile(chan, "beep", chan->language);
			if (!res)
				res = ast_waitstream(chan, "");
			else if (res < 0) {
				ast_clear_flag(chan, AST_FLAG_SPYING);
				break;
			}
			if (!ast_strlen_zero(exitcontext)) {
				char tmp[2];
				tmp[0] = res;
				tmp[1] = '\0';
				if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
					goto exit;
				else
					ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
			}
		}

		res = ast_waitfordigit(chan, waitms);
		if (res < 0) {
			ast_clear_flag(chan, AST_FLAG_SPYING);
			break;
		}
		if (!ast_strlen_zero(exitcontext)) {
			char tmp[2];
			tmp[0] = res;
			tmp[1] = '\0';
			if (!ast_goto_if_exists(chan, exitcontext, tmp, 1))
				goto exit;
			else
				ast_debug(2, "Exit by single digit did not work in chanspy. Extension %s does not exist in context %s\n", tmp, exitcontext);
		}

		/* reset for the next loop around, unless overridden later */
		waitms = 100;
		num_spyed_upon = 0;

		for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
		     peer_chanspy_ds;
			 chanspy_ds_free(peer_chanspy_ds), prev = peer,
		     peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
			 	next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
			const char *group;
			int igrp = !mygroup;
			char *groups[25];
			int num_groups = 0;
			char dup_group[512];
			int x;
			char *s;
			char *buffer;
			char *end;
			char *ext;
			char *form_enforced;
			int ienf = !myenforced;

			peer = peer_chanspy_ds->chan;

			ast_mutex_unlock(&peer_chanspy_ds->lock);

			if (peer == prev) {
				ast_channel_unlock(peer);
				chanspy_ds_free(peer_chanspy_ds);
				break;
			}

			if (ast_check_hangup(chan)) {
				ast_channel_unlock(peer);
				chanspy_ds_free(peer_chanspy_ds);
				break;
			}

			if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
				ast_channel_unlock(peer);
				continue;
			}

			if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
				ast_channel_unlock(peer);
				continue;
			}

			if (mygroup) {
				if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
					ast_copy_string(dup_group, group, sizeof(dup_group));
					num_groups = ast_app_separate_args(dup_group, ':', groups,
						ARRAY_LEN(groups));
				}

				for (x = 0; x < num_groups; x++) {
					if (!strcmp(mygroup, groups[x])) {
						igrp = 1;
						break;
					}
				}
			}

			if (!igrp) {
				ast_channel_unlock(peer);
				continue;
			}

			if (myenforced) {

				/* We don't need to allocate more space than just the
				length of (peer->name) for ext as we will cut the
				channel name's ending before copying into ext */

				ext = alloca(strlen(peer->name));

				form_enforced = alloca(strlen(myenforced) + 3);

				strcpy(form_enforced, ":");
				strcat(form_enforced, myenforced);
				strcat(form_enforced, ":");

				buffer = ast_strdupa(peer->name);
				
				if ((end = strchr(buffer, '-'))) {
					*end++ = ':';
					*end = '\0';
				}

				strcpy(ext, ":");
				strcat(ext, buffer);

				if (strcasestr(form_enforced, ext))
					ienf = 1;
			}

			if (!ienf)
				continue;

			strcpy(peer_name, "spy-");
			strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
			ptr = strchr(peer_name, '/');
			*ptr++ = '\0';

			for (s = peer_name; s < ptr; s++)
				*s = tolower(*s);
			/* We have to unlock the peer channel here to avoid a deadlock.
			 * So, when we need to dereference it again, we have to lock the 
			 * datastore and get the pointer from there to see if the channel 
			 * is still valid. */
			ast_channel_unlock(peer);

			if (!ast_test_flag(flags, OPTION_QUIET)) {
				if (ast_fileexists(peer_name, NULL, NULL) != -1) {
					res = ast_streamfile(chan, peer_name, chan->language);
					if (!res)
						res = ast_waitstream(chan, "");
					if (res) {
						chanspy_ds_free(peer_chanspy_ds);
						break;
					}
				} else
					res = ast_say_character_str(chan, peer_name, "", chan->language);
				if ((num = atoi(ptr)))
					ast_say_digits(chan, atoi(ptr), "", chan->language);
			}

			res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags, exitcontext);
			num_spyed_upon++;	

			if (res == -1) {
				chanspy_ds_free(peer_chanspy_ds);
				goto exit;
			} else if (res == -2) {
				res = 0;
				chanspy_ds_free(peer_chanspy_ds);
				goto exit;
			} else if (res > 1 && spec) {
				struct ast_channel *next;

				snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);

				if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
					peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
					next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
				} else {
					/* stay on this channel, if it is still valid */

					ast_mutex_lock(&peer_chanspy_ds->lock);
					if (peer_chanspy_ds->chan) {
						ast_channel_lock(peer_chanspy_ds->chan);
						next_chanspy_ds = peer_chanspy_ds;
						peer_chanspy_ds = NULL;
					} else {
						/* the channel is gone */
						ast_mutex_unlock(&peer_chanspy_ds->lock);
						next_chanspy_ds = NULL;
					}
				}

				peer = NULL;
			}
		}
		if (res == -1 || ast_check_hangup(chan))
			break;
	}
exit:

	ast_clear_flag(chan, AST_FLAG_SPYING);

	ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);

	ast_mutex_lock(&chanspy_ds.lock);
	ast_mutex_unlock(&chanspy_ds.lock);
	ast_mutex_destroy(&chanspy_ds.lock);

	return res;
}
static int chanspy_exec(struct ast_channel *chan, void *data)
{
	struct localuser *u;
	struct ast_channel *peer=NULL, *prev=NULL;
	char name[AST_NAME_STRLEN],
		peer_name[AST_NAME_STRLEN + 5],
		*args,
		*ptr = NULL,
		*options = NULL,
		*spec = NULL,
		*argv[5],
		*mygroup = NULL,
		*recbase = NULL;
	int res = -1,
		volfactor = 0,
		silent = 0,
		argc = 0,
		bronly = 0,
		chosen = 0,
		count=0,
		waitms = 100,
		num = 0,
		oldrf = 0,
		oldwf = 0,
		fd = 0;
	struct ast_flags flags;
	signed char zero_volume = 0;

	if (!(args = ast_strdupa((char *)data))) {
		ast_log(LOG_ERROR, "Out of memory!\n");
		return -1;
	}

	LOCAL_USER_ADD(u);

	oldrf = chan->readformat;
	oldwf = chan->writeformat;
	if (ast_set_read_format(chan, AST_FORMAT_SLINEAR) < 0) {
		ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
		LOCAL_USER_REMOVE(u);
		return -1;
	}
	
	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR) < 0) {
		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
		LOCAL_USER_REMOVE(u);
		return -1;
	}

	ast_answer(chan);

	ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */

	if ((argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])))) {
		spec = argv[0];
		if ( argc > 1) {
			options = argv[1];
		}
		if (ast_strlen_zero(spec) || !strcmp(spec, "all")) {
			spec = NULL;
		}
	}
	
	if (options) {
		char *opts[OPT_ARG_ARRAY_SIZE];
		ast_app_parse_options(chanspy_opts, &flags, opts, options);
		if (ast_test_flag(&flags, OPTION_GROUP)) {
			mygroup = opts[OPT_ARG_GROUP];
		}
		if (ast_test_flag(&flags, OPTION_RECORD)) {
			if (!(recbase = opts[OPT_ARG_RECORD])) {
				recbase = "chanspy";
			}
		}
		silent = ast_test_flag(&flags, OPTION_QUIET);
		bronly = ast_test_flag(&flags, OPTION_BRIDGED);
		if (ast_test_flag(&flags, OPTION_VOLUME) && opts[OPT_ARG_VOLUME]) {
			int vol;

			if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &vol) != 1) || (vol > 4) || (vol < -4))
				ast_log(LOG_NOTICE, "Volume factor must be a number between -4 and 4\n");
			else
				volfactor = vol;
			}
	}
	else 
		ast_clear_flag(&flags, AST_FLAGS_ALL);

	if (recbase) {
		char filename[512];
		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, 0644)) <= 0) {
			ast_log(LOG_WARNING, "Cannot open %s for recording\n", filename);
			fd = 0;
		}
	}

	for(;;) {
		if (!silent) {
			res = ast_streamfile(chan, "beep", chan->language);
			if (!res)
				res = ast_waitstream(chan, "");
			if (res < 0) {
				ast_clear_flag(chan, AST_FLAG_SPYING);
				break;
			}
		}

		count = 0;
		res = ast_waitfordigit(chan, waitms);
		if (res < 0) {
			ast_clear_flag(chan, AST_FLAG_SPYING);
			break;
		}
				
		peer = local_channel_walk(NULL);
		prev=NULL;
		while(peer) {
			if (peer != chan) {
				char *group = NULL;
				int igrp = 1;

				if (peer == prev && !chosen) {
					break;
				}
				chosen = 0;
				group = pbx_builtin_getvar_helper(peer, "SPYGROUP");
				if (mygroup) {
					if (!group || strcmp(mygroup, group)) {
						igrp = 0;
					}
				}
				
				if (igrp && (!spec || ((strlen(spec) <= strlen(peer->name) &&
							!strncasecmp(peer->name, spec, strlen(spec)))))) {
					if (peer && (!bronly || ast_bridged_channel(peer)) &&
					    !ast_check_hangup(peer) && !ast_test_flag(peer, AST_FLAG_SPYING)) {
						int x = 0;
						strncpy(peer_name, "spy-", 5);
						strncpy(peer_name + strlen(peer_name), peer->name, AST_NAME_STRLEN);
						ptr = strchr(peer_name, '/');
						*ptr = '\0';
						ptr++;
						for (x = 0 ; x < strlen(peer_name) ; x++) {
							if (peer_name[x] == '/') {
								break;
							}
							peer_name[x] = tolower(peer_name[x]);
						}

						if (!silent) {
							if (ast_fileexists(peer_name, NULL, NULL) != -1) {
								res = ast_streamfile(chan, peer_name, chan->language);
								if (!res)
									res = ast_waitstream(chan, "");
								if (res)
									break;
							} else
								res = ast_say_character_str(chan, peer_name, "", chan->language);
							if ((num=atoi(ptr))) 
								ast_say_digits(chan, atoi(ptr), "", chan->language);
						}
						count++;
						prev = peer;
						res = channel_spy(chan, peer, &volfactor, fd);
						if (res == -1) {
							break;
						} else if (res > 1 && spec) {
							snprintf(name, AST_NAME_STRLEN, "%s/%d", spec, res);
							if ((peer = local_get_channel_begin_name(name))) {
								chosen = 1;
							}
							continue;
						}
					}
				}
			}
			if ((peer = local_channel_walk(peer)) == NULL) {
				break;
			}
		}
		waitms = count ? 100 : 5000;
	}
	

	if (fd > 0) {
		close(fd);
	}

	if (oldrf && ast_set_read_format(chan, oldrf) < 0) {
		ast_log(LOG_ERROR, "Could Not Set Read Format.\n");
	}
	
	if (oldwf && ast_set_write_format(chan, oldwf) < 0) {
		ast_log(LOG_ERROR, "Could Not Set Write Format.\n");
	}

	ast_clear_flag(chan, AST_FLAG_SPYING);

	ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);

	ALL_DONE(u, res);
}
Beispiel #12
0
static int common_exec(struct ast_channel *chan, const struct ast_flags *flags,
		       int volfactor, const int fd, const char *mygroup, const char *spec,
		       const char *exten, const char *context)
{
	char nameprefix[AST_NAME_STRLEN];
	char peer_name[AST_NAME_STRLEN + 5];
	signed char zero_volume = 0;
	int waitms;
	int res;
	char *ptr;
	int num;
	int num_spyed_upon = 1;
	struct chanspy_ds chanspy_ds = { 0, };

	ast_mutex_init(&chanspy_ds.lock);

	snprintf(chanspy_ds.unique_id, sizeof(chanspy_ds.unique_id), "%d", ast_atomic_fetchadd_int(&next_unique_id_to_use, +1));

	if (chan->_state != AST_STATE_UP)
		ast_answer(chan);

	ast_set_flag(chan, AST_FLAG_SPYING); /* so nobody can spy on us while we are spying */

	waitms = 100;

	for (;;) {
		struct chanspy_ds *peer_chanspy_ds = NULL, *next_chanspy_ds = NULL;
		struct ast_channel *prev = NULL, *peer = NULL;

		if (!ast_test_flag(flags, OPTION_QUIET) && num_spyed_upon) {
			res = ast_streamfile(chan, "beep", chan->language);
			if (!res)
				res = ast_waitstream(chan, "");
			else if (res < 0) {
				ast_clear_flag(chan, AST_FLAG_SPYING);
				break;
			}
		}

		res = ast_waitfordigit(chan, waitms);
		if (res < 0) {
			ast_clear_flag(chan, AST_FLAG_SPYING);
			break;
		}
				
		/* reset for the next loop around, unless overridden later */
		waitms = 100;
		num_spyed_upon = 0;

		for (peer_chanspy_ds = next_channel(chan, prev, spec, exten, context, &chanspy_ds);
		     peer_chanspy_ds;
			 chanspy_ds_free(peer_chanspy_ds), prev = peer,
		     peer_chanspy_ds = next_chanspy_ds ? next_chanspy_ds : 
			 	next_channel(chan, prev, spec, exten, context, &chanspy_ds), next_chanspy_ds = NULL) {
			const char *group;
			int igrp = !mygroup;
			char *groups[25];
			int num_groups = 0;
			char dup_group[512];
			int x;
			char *s;

			peer = peer_chanspy_ds->chan;

			ast_mutex_unlock(&peer_chanspy_ds->lock);

			if (peer == prev) {
				ast_channel_unlock(peer);
				chanspy_ds_free(peer_chanspy_ds);
				break;
			}

			if (ast_check_hangup(chan)) {
				ast_channel_unlock(peer);
				chanspy_ds_free(peer_chanspy_ds);
				break;
			}

			if (ast_test_flag(flags, OPTION_BRIDGED) && !ast_bridged_channel(peer)) {
				ast_channel_unlock(peer);
				continue;
			}

			if (ast_check_hangup(peer) || ast_test_flag(peer, AST_FLAG_SPYING)) {
				ast_channel_unlock(peer);
				continue;
			}

			if (mygroup) {
				if ((group = pbx_builtin_getvar_helper(peer, "SPYGROUP"))) {
					ast_copy_string(dup_group, group, sizeof(dup_group));
					num_groups = ast_app_separate_args(dup_group, ':', groups,
									   sizeof(groups) / sizeof(groups[0]));
				}
				
				for (x = 0; x < num_groups; x++) {
					if (!strcmp(mygroup, groups[x])) {
						igrp = 1;
						break;
					}
				}
			}
			
			if (!igrp) {
				ast_channel_unlock(peer);
				continue;
			}

			strcpy(peer_name, "spy-");
			strncat(peer_name, peer->name, AST_NAME_STRLEN - 4 - 1);
			ptr = strchr(peer_name, '/');
			*ptr++ = '\0';
			
			for (s = peer_name; s < ptr; s++)
				*s = tolower(*s);

			/* We have to unlock the peer channel here to avoid a deadlock.
			 * So, when we need to dereference it again, we have to lock the 
			 * datastore and get the pointer from there to see if the channel 
			 * is still valid. */
			ast_channel_unlock(peer);

			if (!ast_test_flag(flags, OPTION_QUIET)) {
				if (ast_fileexists(peer_name, NULL, NULL) != -1) {
					res = ast_streamfile(chan, peer_name, chan->language);
					if (!res)
						res = ast_waitstream(chan, "");
					if (res) {
						chanspy_ds_free(peer_chanspy_ds);
						break;
					}
				} else
					res = ast_say_character_str(chan, peer_name, "", chan->language);
				if ((num = atoi(ptr))) 
					ast_say_digits(chan, atoi(ptr), "", chan->language);
			}
			
			res = channel_spy(chan, peer_chanspy_ds, &volfactor, fd, flags);
			num_spyed_upon++;	

			if (res == -1) {
				chanspy_ds_free(peer_chanspy_ds);
				break;
			} else if (res > 1 && spec) {
				struct ast_channel *next;

				snprintf(nameprefix, AST_NAME_STRLEN, "%s/%d", spec, res);

				if ((next = ast_get_channel_by_name_prefix_locked(nameprefix, strlen(nameprefix)))) {
					peer_chanspy_ds = chanspy_ds_free(peer_chanspy_ds);
					next_chanspy_ds = setup_chanspy_ds(next, &chanspy_ds);
				} else {
					/* stay on this channel, if it is still valid */

					ast_mutex_lock(&peer_chanspy_ds->lock);
					if (peer_chanspy_ds->chan) {
						ast_channel_lock(peer_chanspy_ds->chan);
						next_chanspy_ds = peer_chanspy_ds;
						peer_chanspy_ds = NULL;
					} else {
						/* the channel is gone */
						ast_mutex_unlock(&peer_chanspy_ds->lock);
						next_chanspy_ds = NULL;
					}
				}

				peer = NULL;
			}
		}
		if (res == -1 || ast_check_hangup(chan))
			break;
	}
	
	ast_clear_flag(chan, AST_FLAG_SPYING);

	ast_channel_setoption(chan, AST_OPTION_TXGAIN, &zero_volume, sizeof(zero_volume), 0);

	ast_mutex_lock(&chanspy_ds.lock);
	ast_mutex_unlock(&chanspy_ds.lock);
	ast_mutex_destroy(&chanspy_ds.lock);

	return res;
}
Beispiel #13
0
static int record_exec(struct ast_channel *chan, void *data)
{
    int res = 0;
    int count = 0;
    int percentflag = 0;
    char *filename, *ext = NULL, *silstr, *maxstr, *options;
    char *vdata, *p;
    int i = 0;
    char tmp[256];

    struct ast_filestream *s = '\0';
    struct ast_module_user *u;
    struct ast_frame *f = NULL;

    struct ast_dsp *sildet = NULL;   	/* silence detector dsp */
    int totalsilence = 0;
    int dspsilence = 0;
    int silence = 0;		/* amount of silence to allow */
    int gotsilence = 0;		/* did we timeout for silence? */
    int maxduration = 0;		/* max duration of recording in milliseconds */
    int gottimeout = 0;		/* did we timeout for maxduration exceeded? */
    int option_skip = 0;
    int option_noanswer = 0;
    int option_append = 0;
    int terminator = '#';
    int option_quiet = 0;
    int rfmt = 0;
    int flags;
    int waitres;
    struct ast_silence_generator *silgen = NULL;

    /* The next few lines of code parse out the filename and header from the input string */
    if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
        ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
        return -1;
    }

    u = ast_module_user_add(chan);

    /* Yay for strsep being easy */
    vdata = ast_strdupa(data);

    p = vdata;
    filename = strsep(&p, "|");
    silstr = strsep(&p, "|");
    maxstr = strsep(&p, "|");
    options = strsep(&p, "|");

    if (filename) {
        if (strstr(filename, "%d"))
            percentflag = 1;
        ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */
        if (!ext)
            ext = strchr(filename, ':');
        if (ext) {
            *ext = '\0';
            ext++;
        }
    }
    if (!ext) {
        ast_log(LOG_WARNING, "No extension specified to filename!\n");
        ast_module_user_remove(u);
        return -1;
    }
    if (silstr) {
        if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) {
            silence = i * 1000;
        } else if (!ast_strlen_zero(silstr)) {
            ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr);
        }
    }

    if (maxstr) {
        if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1))
            /* Convert duration to milliseconds */
            maxduration = i * 1000;
        else if (!ast_strlen_zero(maxstr))
            ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr);
    }
    if (options) {
        /* Retain backwards compatibility with old style options */
        if (!strcasecmp(options, "skip"))
            option_skip = 1;
        else if (!strcasecmp(options, "noanswer"))
            option_noanswer = 1;
        else {
            if (strchr(options, 's'))
                option_skip = 1;
            if (strchr(options, 'n'))
                option_noanswer = 1;
            if (strchr(options, 'a'))
                option_append = 1;
            if (strchr(options, 't'))
                terminator = '*';
            if (strchr(options, 'x'))
                terminator = 0;
            if (strchr(options, 'q'))
                option_quiet = 1;
        }
    }

    /* done parsing */

    /* these are to allow the use of the %d in the config file for a wild card of sort to
      create a new file with the inputed name scheme */
    if (percentflag) {
        AST_DECLARE_APP_ARGS(fname,
                             AST_APP_ARG(piece)[100];
                            );
        char *tmp2 = ast_strdupa(filename);
        char countstring[15];
        int i;

        /* Separate each piece out by the format specifier */
        AST_NONSTANDARD_APP_ARGS(fname, tmp2, '%');
        do {
            int tmplen;
            /* First piece has no leading percent, so it's copied verbatim */
            ast_copy_string(tmp, fname.piece[0], sizeof(tmp));
            tmplen = strlen(tmp);
            for (i = 1; i < fname.argc; i++) {
                if (fname.piece[i][0] == 'd') {
                    /* Substitute the count */
                    snprintf(countstring, sizeof(countstring), "%d", count);
                    ast_copy_string(tmp + tmplen, countstring, sizeof(tmp) - tmplen);
                    tmplen += strlen(countstring);
                } else if (tmplen + 2 < sizeof(tmp)) {
                    /* Unknown format specifier - just copy it verbatim */
                    tmp[tmplen++] = '%';
                    tmp[tmplen++] = fname.piece[i][0];
                }
                /* Copy the remaining portion of the piece */
                ast_copy_string(tmp + tmplen, &(fname.piece[i][1]), sizeof(tmp) - tmplen);
            }
            count++;
        } while (ast_fileexists(tmp, ext, chan->language) > 0);
        pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
    } else
static int record_exec(struct ast_channel *chan, void *data)
{
	int res = 0;
	int count = 0;
	int percentflag = 0;
	char *filename, *ext = NULL, *silstr, *maxstr, *options;
	char *vdata, *p;
	int i = 0;
	char tmp[256];

	struct ast_filestream *s = '\0';
	struct localuser *u;
	struct ast_frame *f = NULL;
	
	struct ast_dsp *sildet = NULL;   	/* silence detector dsp */
	int totalsilence = 0;
	int dspsilence = 0;
	int silence = 0;		/* amount of silence to allow */
	int gotsilence = 0;		/* did we timeout for silence? */
	int maxduration = 0;		/* max duration of recording in milliseconds */
	int gottimeout = 0;		/* did we timeout for maxduration exceeded? */
	int option_skip = 0;
	int option_noanswer = 0;
	int option_append = 0;
	int terminator = '#';
	int option_quiet = 0;
	int rfmt = 0;
	int flags;
	int waitres;
	struct ast_silence_generator *silgen = NULL;
	
	/* The next few lines of code parse out the filename and header from the input string */
	if (ast_strlen_zero(data)) { /* no data implies no filename or anything is present */
		ast_log(LOG_WARNING, "Record requires an argument (filename)\n");
		return -1;
	}

	LOCAL_USER_ADD(u);

	/* Yay for strsep being easy */
	vdata = ast_strdupa(data);
	if (!vdata) {
		ast_log(LOG_ERROR, "Out of memory\n");
		LOCAL_USER_REMOVE(u);
		return -1;
	}

	p = vdata;
	filename = strsep(&p, "|");
	silstr = strsep(&p, "|");
	maxstr = strsep(&p, "|");	
	options = strsep(&p, "|");
	
	if (filename) {
		if (strstr(filename, "%d"))
			percentflag = 1;
		ext = strrchr(filename, '.'); /* to support filename with a . in the filename, not format */
		if (!ext)
			ext = strchr(filename, ':');
		if (ext) {
			*ext = '\0';
			ext++;
		}
	}
	if (!ext) {
		ast_log(LOG_WARNING, "No extension specified to filename!\n");
		LOCAL_USER_REMOVE(u);
		return -1;
	}
	if (silstr) {
		if ((sscanf(silstr, "%d", &i) == 1) && (i > -1)) {
			silence = i * 1000;
		} else if (!ast_strlen_zero(silstr)) {
			ast_log(LOG_WARNING, "'%s' is not a valid silence duration\n", silstr);
		}
	}
	
	if (maxstr) {
		if ((sscanf(maxstr, "%d", &i) == 1) && (i > -1))
			/* Convert duration to milliseconds */
			maxduration = i * 1000;
		else if (!ast_strlen_zero(maxstr))
			ast_log(LOG_WARNING, "'%s' is not a valid maximum duration\n", maxstr);
	}
	if (options) {
		/* Retain backwards compatibility with old style options */
		if (!strcasecmp(options, "skip"))
			option_skip = 1;
		else if (!strcasecmp(options, "noanswer"))
			option_noanswer = 1;
		else {
			if (strchr(options, 's'))
				option_skip = 1;
			if (strchr(options, 'n'))
				option_noanswer = 1;
			if (strchr(options, 'a'))
				option_append = 1;
			if (strchr(options, 't'))
				terminator = '*';
			if (strchr(options, 'q'))
				option_quiet = 1;
		}
	}
	
	/* done parsing */
	
	/* these are to allow the use of the %d in the config file for a wild card of sort to
	  create a new file with the inputed name scheme */
	if (percentflag) {
		do {
			snprintf(tmp, sizeof(tmp), filename, count);
			count++;
		} while ( ast_fileexists(tmp, ext, chan->language) != -1 );
		pbx_builtin_setvar_helper(chan, "RECORDED_FILE", tmp);
	} else
		strncpy(tmp, filename, sizeof(tmp)-1);
	/* end of routine mentioned */
	
	
	
	if (chan->_state != AST_STATE_UP) {
		if (option_skip) {
			/* At the user's option, skip if the line is not up */
			LOCAL_USER_REMOVE(u);
			return 0;
		} else if (!option_noanswer) {
			/* Otherwise answer unless we're supposed to record while on-hook */
			res = ast_answer(chan);
		}
	}
	
	if (res) {
		ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
		goto out;
	}
	
	if (!option_quiet) {
		/* Some code to play a nice little beep to signify the start of the record operation */
		res = ast_streamfile(chan, "beep", chan->language);
		if (!res) {
			res = ast_waitstream(chan, "");
		} else {
			ast_log(LOG_WARNING, "ast_streamfile failed on %s\n", chan->name);
		}
		ast_stopstream(chan);
	}
		
	/* The end of beep code.  Now the recording starts */
		
	if (silence > 0) {
		rfmt = chan->readformat;
		res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
		if (res < 0) {
			ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
			LOCAL_USER_REMOVE(u);
			return -1;
		}
		sildet = ast_dsp_new();
		if (!sildet) {
			ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
			LOCAL_USER_REMOVE(u);
			return -1;
		}
		ast_dsp_set_threshold(sildet, 256);
	} 
		
		
	flags = option_append ? O_CREAT|O_APPEND|O_WRONLY : O_CREAT|O_TRUNC|O_WRONLY;
	s = ast_writefile( tmp, ext, NULL, flags , 0, 0644);
		
	if (!s) {
		ast_log(LOG_WARNING, "Could not create file %s\n", filename);
		goto out;
	}

	if (option_transmit_silence_during_record)
		silgen = ast_channel_start_silence_generator(chan);
	
	/* Request a video update */
	ast_indicate(chan, AST_CONTROL_VIDUPDATE);
	
	if (maxduration <= 0)
		maxduration = -1;
	
	while ((waitres = ast_waitfor(chan, maxduration)) > -1) {
		if (maxduration > 0) {
			if (waitres == 0) {
				gottimeout = 1;
				break;
			}
			maxduration = waitres;
		}
		
		f = ast_read(chan);
		if (!f) {
			res = -1;
			break;
		}
		if (f->frametype == AST_FRAME_VOICE) {
			res = ast_writestream(s, f);
			
			if (res) {
				ast_log(LOG_WARNING, "Problem writing frame\n");
				ast_frfree(f);
				break;
			}
			
			if (silence > 0) {
				dspsilence = 0;
				ast_dsp_silence(sildet, f, &dspsilence);
				if (dspsilence) {
					totalsilence = dspsilence;
				} else {
					totalsilence = 0;
				}
				if (totalsilence > silence) {
					/* Ended happily with silence */
					ast_frfree(f);
					gotsilence = 1;
					break;
				}
			}
		} else if (f->frametype == AST_FRAME_VIDEO) {
			res = ast_writestream(s, f);
			
			if (res) {
				ast_log(LOG_WARNING, "Problem writing frame\n");
				ast_frfree(f);
				break;
			}
		} else if ((f->frametype == AST_FRAME_DTMF) &&
		    (f->subclass == terminator)) {
			ast_frfree(f);
			break;
		}
		ast_frfree(f);
	}
	if (!f) {
		ast_log(LOG_DEBUG, "Got hangup\n");
		res = -1;
	}
			
	if (gotsilence) {
		ast_stream_rewind(s, silence-1000);
		ast_truncstream(s);
	} else if (!gottimeout) {
		/* Strip off the last 1/4 second of it */
		ast_stream_rewind(s, 250);
		ast_truncstream(s);
	}
	ast_closestream(s);

	if (silgen)
		ast_channel_stop_silence_generator(chan, silgen);
	
 out:
	if ((silence > 0) && rfmt) {
		res = ast_set_read_format(chan, rfmt);
		if (res)
			ast_log(LOG_WARNING, "Unable to restore read format on '%s'\n", chan->name);
		if (sildet)
			ast_dsp_free(sildet);
	}

	LOCAL_USER_REMOVE(u);

	return res;
}