Skip to content
Snippets Groups Projects
manager.c 282 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			ast_module_reload(rld);
    		}
    	} else {
    		ast_config_destroy(cfg);
    		switch(result) {
    		case UNKNOWN_ACTION:
    			astman_send_error(s, m, "Unknown action command");
    			break;
    		case UNKNOWN_CATEGORY:
    			astman_send_error(s, m, "Given category does not exist");
    			break;
    		case UNSPECIFIED_CATEGORY:
    			astman_send_error(s, m, "Category not specified");
    			break;
    		case UNSPECIFIED_ARGUMENT:
    			astman_send_error(s, m, "Problem with category, value, or line (if required)");
    			break;
    		case FAILURE_ALLOCATION:
    			astman_send_error(s, m, "Memory allocation failure, this should not happen");
    			break;
    
    Pari Nannapaneni's avatar
     
    Pari Nannapaneni committed
    		case FAILURE_NEWCAT:
    			astman_send_error(s, m, "Create category did not complete successfully");
    			break;
    
    		case FAILURE_DELCAT:
    			astman_send_error(s, m, "Delete category did not complete successfully");
    			break;
    		case FAILURE_EMPTYCAT:
    			astman_send_error(s, m, "Empty category did not complete successfully");
    			break;
    		case FAILURE_UPDATE:
    			astman_send_error(s, m, "Update did not complete successfully");
    			break;
    		case FAILURE_DELETE:
    			astman_send_error(s, m, "Delete did not complete successfully");
    			break;
    		case FAILURE_APPEND:
    			astman_send_error(s, m, "Append did not complete successfully");
    			break;
    
    		case FAILURE_TEMPLATE:
    			astman_send_error(s, m, "Template category not found");
    			break;
    
    static int action_createconfig(struct mansession *s, const struct message *m)
    {
    	int fd;
    	const char *fn = astman_get_header(m, "Filename");
    	struct ast_str *filepath = ast_str_alloca(PATH_MAX);
    	ast_str_set(&filepath, 0, "%s/", ast_config_AST_CONFIG_DIR);
    	ast_str_append(&filepath, 0, "%s", fn);
    
    
    	if ((fd = open(ast_str_buffer(filepath), O_CREAT | O_EXCL, AST_FILE_MODE)) != -1) {
    
    		close(fd);
    		astman_send_ack(s, m, "New configuration file created successfully");
    
    		astman_send_error(s, m, strerror(errno));
    
    static int action_waitevent(struct mansession *s, const struct message *m)
    
    	const char *timeouts = astman_get_header(m, "Timeout");
    
    	const char *id = astman_get_header(m, "ActionID");
    	char idText[256];
    
    		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		sscanf(timeouts, "%30i", &timeout);
    
    		/* XXX maybe put an upper bound, or prevent the use of 0 ? */
    
    	ao2_lock(s->session);
    
    	if (s->session->waiting_thread != AST_PTHREADT_NULL) {
    
    		pthread_kill(s->session->waiting_thread, SIGURG);
    
    	if (s->session->managerid) { /* AMI-over-HTTP session */
    
    		/*
    		 * Make sure the timeout is within the expire time of the session,
    		 * as the client will likely abort the request if it does not see
    		 * data coming after some amount of time.
    		 */
    		time_t now = time(NULL);
    
    		int max = s->session->sessiontimeout - now - 10;
    
    		if (max < 0) {	/* We are already late. Strange but possible. */
    
    		}
    		if (!s->session->send_events) {	/* make sure we record events */
    
    			s->session->send_events = -1;
    
    	ao2_unlock(s->session);
    
    
    	/* XXX should this go inside the lock ? */
    
    	s->session->waiting_thread = pthread_self();	/* let new events wake up this thread */
    
    	ast_debug(1, "Starting waiting for an event!\n");
    
    	for (x = 0; x < timeout || timeout < 0; x++) {
    
    		ao2_lock(s->session);
    
    		if (AST_RWLIST_NEXT(s->session->last_ev, eq_next)) {
    
    		/* We can have multiple HTTP session point to the same mansession entry.
    		 * The way we deal with it is not very nice: newcomers kick out the previous
    		 * HTTP session. XXX this needs to be improved.
    		 */
    
    		if (s->session->waiting_thread != pthread_self()) {
    
    		ao2_unlock(s->session);
    
    		if (s->session->managerid == 0) {	/* AMI session */
    
    			if (ast_wait_for_input(s->session->fd, 1000)) {
    
    		} else {	/* HTTP session */
    
    	ast_debug(1, "Finished waiting for an event!\n");
    
    	ao2_lock(s->session);
    
    	if (s->session->waiting_thread == pthread_self()) {
    
    		astman_send_response(s, m, "Success", "Waiting for Event completed.");
    
    			if (((s->session->readperm & eqe->category) == eqe->category)
    				&& ((s->session->send_events & eqe->category) == eqe->category)
    				&& match_filter(s, eqe->eventdata)) {
    
    				astman_append(s, "%s", eqe->eventdata);
    			}
    
    		s->session->waiting_thread = AST_PTHREADT_NULL;
    
    		ast_debug(1, "Abandoning event request!\n");
    
    	ao2_unlock(s->session);
    
    
    static int action_listcommands(struct mansession *s, const struct message *m)
    
    	struct manager_action *cur;
    
    	struct ast_str *temp = ast_str_alloca(MAX_AUTH_PERM_STRING);
    
    	AST_RWLIST_RDLOCK(&actions);
    
    	AST_RWLIST_TRAVERSE(&actions, cur, list) {
    
    		if ((s->session->writeperm & cur->authority) || cur->authority == 0) {
    
    			astman_append(s, "%s: %s (Priv: %s)\r\n",
    				cur->action, cur->synopsis, authority_to_str(cur->authority, &temp));
    
    	AST_RWLIST_UNLOCK(&actions);
    
    static int action_events(struct mansession *s, const struct message *m)
    
    	const char *mask = astman_get_header(m, "EventMask");
    
    	const char *id = astman_get_header(m, "ActionID");
    	char id_text[256];
    
    	if (!ast_strlen_zero(id)) {
    		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
    	} else {
    		id_text[0] = '\0';
    	}
    
    	if (broken_events_action) {
    		/* if this option is set we should not return a response on
    		 * error, or when all events are set */
    
    		if (res > 0) {
    			for (x = 0; x < ARRAY_LEN(perms); x++) {
    				if (!strcasecmp(perms[x].label, "all") && res == perms[x].num) {
    					return 0;
    				}
    			}
    
    			astman_append(s, "Response: Success\r\n%s"
    					 "Events: On\r\n\r\n", id_text);
    
    		} else if (res == 0)
    
    			astman_append(s, "Response: Success\r\n%s"
    					 "Events: Off\r\n\r\n", id_text);
    
    		astman_append(s, "Response: Success\r\n%s"
    				 "Events: On\r\n\r\n", id_text);
    
    		astman_append(s, "Response: Success\r\n%s"
    				 "Events: Off\r\n\r\n", id_text);
    
    	else
    		astman_send_error(s, m, "Invalid event mask");
    
    
    static int action_logoff(struct mansession *s, const struct message *m)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	astman_send_response(s, m, "Goodbye", "Thanks for all the fish.");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    static int action_login(struct mansession *s, const struct message *m)
    
    
    	/* still authenticated - don't process again */
    	if (s->session->authenticated) {
    		astman_send_ack(s, m, "Already authenticated");
    		return 0;
    	}
    
    
    	if (authenticate(s, m)) {
    		sleep(1);
    		astman_send_error(s, m, "Authentication failed");
    		return -1;
    	}
    
    	s->session->authenticated = 1;
    
    	ast_atomic_fetchadd_int(&unauth_sessions, -1);
    
    	if (manager_displayconnects(s->session)) {
    
    		ast_verb(2, "%sManager '%s' logged on from %s\n", (s->session->managerid ? "HTTP " : ""), s->session->username, ast_sockaddr_stringify_addr(&s->session->addr));
    
    	astman_send_ack(s, m, "Authentication accepted");
    
    	if ((s->session->send_events & EVENT_FLAG_SYSTEM)
    
    		&& (s->session->readperm & EVENT_FLAG_SYSTEM)
    
    		&& ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
    
    		struct ast_str *auth = ast_str_alloca(MAX_AUTH_PERM_STRING);
    
    		const char *cat_str = authority_to_str(EVENT_FLAG_SYSTEM, &auth);
    
    		astman_append(s, "Event: FullyBooted\r\n"
    			"Privilege: %s\r\n"
    			"Status: Fully Booted\r\n\r\n", cat_str);
    
    static int action_challenge(struct mansession *s, const struct message *m)
    
    	const char *authtype = astman_get_header(m, "AuthType");
    
    
    	if (!strcasecmp(authtype, "MD5")) {
    
    		if (ast_strlen_zero(s->session->challenge)) {
    
    			snprintf(s->session->challenge, sizeof(s->session->challenge), "%ld", ast_random());
    
    		astman_append(s, "Challenge: %s\r\n\r\n", s->session->challenge);
    
    	} else {
    		astman_send_error(s, m, "Must specify AuthType");
    	}
    	return 0;
    }
    
    
    static int action_hangup(struct mansession *s, const struct message *m)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *c = NULL;
    
    	int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
    
    	const char *id = astman_get_header(m, "ActionID");
    	const char *name_or_regex = astman_get_header(m, "Channel");
    
    	const char *cause = astman_get_header(m, "Cause");
    
    	regex_t regexbuf;
    	struct ast_channel_iterator *iter = NULL;
    	struct ast_str *regex_string;
    	int channels_matched = 0;
    
    	if (ast_strlen_zero(name_or_regex)) {
    
    		astman_send_error(s, m, "No channel specified");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	if (!ast_strlen_zero(id)) {
    		snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
    
    	if (!ast_strlen_zero(cause)) {
    		char *endptr;
    		causecode = strtol(cause, &endptr, 10);
    		if (causecode < 0 || causecode > 127 || *endptr != '\0') {
    			ast_log(LOG_NOTICE, "Invalid 'Cause: %s' in manager action Hangup\n", cause);
    			/* keep going, better to hangup without cause than to not hang up at all */
    			causecode = 0; /* do not set channel's hangupcause */
    		}
    	}
    
    	/************************************************/
    	/* Regular explicit match channel byname hangup */
    
    	if (name_or_regex[0] != '/') {
    		if (!(c = ast_channel_get_by_name(name_or_regex))) {
    
    			ast_log(LOG_NOTICE, "Request to hangup non-existent channel: %s\n",
    				name_or_regex);
    
    			astman_send_error(s, m, "No such channel");
    			return 0;
    		}
    
    		ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
    			(s->session->managerid ? "HTTP " : ""),
    			s->session->username,
    
    			ast_sockaddr_stringify_addr(&s->session->addr),
    
    			ast_channel_name(c));
    
    		ast_channel_softhangup_withcause_locked(c, causecode);
    		c = ast_channel_unref(c);
    
    		astman_send_ack(s, m, "Channel Hungup");
    
    		return 0;
    	}
    
    	/***********************************************/
    	/* find and hangup any channels matching regex */
    
    	regex_string = ast_str_create(strlen(name_or_regex));
    
    	if (!regex_string) {
    		astman_send_error(s, m, "Memory Allocation Failure");
    		return 0;
    	}
    
    
    	/* Make "/regex/" into "regex" */
    
    	if (ast_regex_string_to_regex_pattern(name_or_regex, &regex_string) != 0) {
    
    		astman_send_error(s, m, "Regex format invalid, Channel param should be /regex/");
    		ast_free(regex_string);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	/* if regex compilation fails, hangup fails */
    	if (regcomp(&regexbuf, ast_str_buffer(regex_string), REG_EXTENDED | REG_NOSUB)) {
    
    		astman_send_error_va(s, m, "Regex compile failed on: %s", name_or_regex);
    
    		ast_free(regex_string);
    		return 0;
    	}
    
    	astman_send_listack(s, m, "Channels hung up will follow", "start");
    
    
    	iter = ast_channel_iterator_all_new();
    	if (iter) {
    		for (; (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
    			if (regexec(&regexbuf, ast_channel_name(c), 0, NULL, 0)) {
    				continue;
    			}
    
    			ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
    				(s->session->managerid ? "HTTP " : ""),
    				s->session->username,
    
    				ast_sockaddr_stringify_addr(&s->session->addr),
    
    			ast_channel_softhangup_withcause_locked(c, causecode);
    			channels_matched++;
    
    			astman_append(s,
    				"Event: ChannelHungup\r\n"
    				"Channel: %s\r\n"
    				"%s"
    				"\r\n", ast_channel_name(c), idText);
    		}
    		ast_channel_iterator_destroy(iter);
    
    	regfree(&regexbuf);
    	ast_free(regex_string);
    
    	astman_send_list_complete_start(s, m, "ChannelsHungupListComplete", channels_matched);
    	astman_send_list_complete_end(s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static int action_setvar(struct mansession *s, const struct message *m)
    
    	const char *name = astman_get_header(m, "Channel");
    	const char *varname = astman_get_header(m, "Variable");
    	const char *varval = astman_get_header(m, "Value");
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (ast_strlen_zero(varname)) {
    
    		astman_send_error(s, m, "No variable specified");
    		return 0;
    	}
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	if (!ast_strlen_zero(name)) {
    
    		if (!(c = ast_channel_get_by_name(name))) {
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			astman_send_error(s, m, "No such channel");
    			return 0;
    		}
    
    
    	res = pbx_builtin_setvar_helper(c, varname, S_OR(varval, ""));
    
    		astman_send_ack(s, m, "Variable Set");
    
    	} else {
    		astman_send_error(s, m, "Variable not set");
    	}
    
    static int action_getvar(struct mansession *s, const struct message *m)
    
    	const char *name = astman_get_header(m, "Channel");
    	const char *varname = astman_get_header(m, "Variable");
    
    	char workspace[1024];
    
    		astman_send_error(s, m, "No variable specified");
    		return 0;
    	}
    
    
    	/* We don't want users with insufficient permissions using certain functions. */
    	if (!(function_capable_string_allowed_with_auths(varname, s->session->writeperm))) {
    		astman_send_error(s, m, "GetVar Access Forbidden: Variable");
    		return 0;
    	}
    
    
    		if (!(c = ast_channel_get_by_name(name))) {
    
    			astman_send_error(s, m, "No such channel");
    			return 0;
    		}
    
    	workspace[0] = '\0';
    
    			c = ast_dummy_channel_alloc();
    
    			if (c) {
    				ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
    			} else
    				ast_log(LOG_ERROR, "Unable to allocate bogus channel for variable substitution.  Function results may be blank.\n");
    
    			ast_func_read(c, (char *) varname, workspace, sizeof(workspace));
    
    		varval = workspace;
    
    	} else {
    		pbx_retrieve_variable(c, varname, &varval, workspace, sizeof(workspace), NULL);
    	}
    
    	astman_append(s, "Variable: %s\r\nValue: %s\r\n\r\n", varname, S_OR(varval, ""));
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Manager "status" command to show channels */
    
    /* Needs documentation... */
    
    static int action_status(struct mansession *s, const struct message *m)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	const char *name = astman_get_header(m, "Channel");
    
    	const char *chan_variables = astman_get_header(m, "Variables");
    	const char *id = astman_get_header(m, "ActionID");
    	char *variables = ast_strdupa(S_OR(chan_variables, ""));
    	struct ast_str *variable_str = ast_str_create(1024);
    	struct ast_str *write_transpath = ast_str_alloca(256);
    	struct ast_str *read_transpath = ast_str_alloca(256);
    	struct ast_channel *chan;
    
    	struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
    
    Olle Johansson's avatar
    Olle Johansson committed
    	int channels = 0;
    
    	int all = ast_strlen_zero(name); /* set if we want all channels */
    
    	char id_text[256];
    	struct ast_channel_iterator *it_chans = NULL;
    
    	AST_DECLARE_APP_ARGS(vars,
    		AST_APP_ARG(name)[100];
    	);
    
    	if (!variable_str) {
    		astman_send_error(s, m, "Memory Allocation Failure");
    		return 1;
    
    	if (!(function_capable_string_allowed_with_auths(variables, s->session->writeperm))) {
    
    		astman_send_error(s, m, "Status Access Forbidden: Variables");
    		return 0;
    	}
    
    
    		if (!(it_chans = ast_channel_iterator_all_new())) {
    			ast_free(variable_str);
    
    			astman_send_error(s, m, "Memory Allocation Failure");
    			return 1;
    		}
    
    		chan = ast_channel_iterator_next(it_chans);
    
    		chan = ast_channel_get_by_name(name);
    		if (!chan) {
    
    			astman_send_error(s, m, "No such channel");
    
    	astman_send_listack(s, m, "Channel status will follow", "start");
    
    Olle Johansson's avatar
    Olle Johansson committed
    
    
    	if (!ast_strlen_zero(id)) {
    		snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", id);
    	} else {
    		id_text[0] = '\0';
    	}
    
    	if (!ast_strlen_zero(chan_variables)) {
    
    	/* if we look by name, we break after the first iteration */
    
    	for (; chan; all ? chan = ast_channel_iterator_next(it_chans) : 0) {
    		struct timeval now;
    		long elapsed_seconds;
    		struct ast_bridge *bridge;
    
    		ast_channel_lock(chan);
    
    		now = ast_tvnow();
    		elapsed_seconds = ast_tvdiff_sec(now, ast_channel_creationtime(chan));
    
    		if (!ast_strlen_zero(chan_variables)) {
    
    			for (i = 0; i < vars.argc; i++) {
    				char valbuf[512], *ret = NULL;
    
    				if (vars.name[i][strlen(vars.name[i]) - 1] == ')') {
    
    					if (ast_func_read(chan, vars.name[i], valbuf, sizeof(valbuf)) < 0) {
    
    					pbx_retrieve_variable(chan, vars.name[i], &ret, valbuf, sizeof(valbuf), NULL);
    
    				ast_str_append(&variable_str, 0, "Variable: %s=%s\r\n", vars.name[i], ret);
    
    Olle Johansson's avatar
    Olle Johansson committed
    		channels++;
    
    		bridge = ast_channel_get_bridge(chan);
    
    		astman_append(s,
    			"Event: Status\r\n"
    			"Privilege: Call\r\n"
    
    			"Channel: %s\r\n"
    			"ChannelState: %u\r\n"
    			"ChannelStateDesc: %s\r\n"
    			"CallerIDNum: %s\r\n"
    			"CallerIDName: %s\r\n"
    			"ConnectedLineNum: %s\r\n"
    			"ConnectedLineName: %s\r\n"
    			"Accountcode: %s\r\n"
    			"Context: %s\r\n"
    			"Exten: %s\r\n"
    			"Priority: %d\r\n"
    			"Uniqueid: %s\r\n"
    
    			"Type: %s\r\n"
    			"DNID: %s\r\n"
    			"EffectiveConnectedLineNum: %s\r\n"
    			"EffectiveConnectedLineName: %s\r\n"
    			"TimeToHangup: %ld\r\n"
    			"BridgeID: %s\r\n"
    			"Linkedid: %s\r\n"
    			"Application: %s\r\n"
    			"Data: %s\r\n"
    			"Nativeformats: %s\r\n"
    			"Readformat: %s\r\n"
    			"Readtrans: %s\r\n"
    			"Writeformat: %s\r\n"
    			"Writetrans: %s\r\n"
    			"Callgroup: %llu\r\n"
    			"Pickupgroup: %llu\r\n"
    			"Seconds: %ld\r\n"
    			"%s"
    			"%s"
    			"\r\n",
    
    			ast_channel_name(chan),
    			ast_channel_state(chan),
    			ast_state2str(ast_channel_state(chan)),
    			S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, "<unknown>"),
    			S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, "<unknown>"),
    			S_COR(ast_channel_connected(chan)->id.number.valid, ast_channel_connected(chan)->id.number.str, "<unknown>"),
    			S_COR(ast_channel_connected(chan)->id.name.valid, ast_channel_connected(chan)->id.name.str, "<unknown>"),
    			ast_channel_accountcode(chan),
    			ast_channel_context(chan),
    			ast_channel_exten(chan),
    			ast_channel_priority(chan),
    			ast_channel_uniqueid(chan),
    			ast_channel_tech(chan)->type,
    			S_OR(ast_channel_dialed(chan)->number.str, ""),
    			S_COR(ast_channel_connected_effective_id(chan).number.valid, ast_channel_connected_effective_id(chan).number.str, "<unknown>"),
    			S_COR(ast_channel_connected_effective_id(chan).name.valid, ast_channel_connected_effective_id(chan).name.str, "<unknown>"),
    
    			(long)ast_channel_whentohangup(chan)->tv_sec,
    
    			bridge ? bridge->uniqueid : "",
    			ast_channel_linkedid(chan),
    			ast_channel_appl(chan),
    			ast_channel_data(chan),
    
    			ast_format_cap_get_names(ast_channel_nativeformats(chan), &codec_buf),
    			ast_format_get_name(ast_channel_readformat(chan)),
    
    			ast_translate_path_to_str(ast_channel_readtrans(chan), &read_transpath),
    
    			ast_format_get_name(ast_channel_writeformat(chan)),
    
    			ast_translate_path_to_str(ast_channel_writetrans(chan), &write_transpath),
    			ast_channel_callgroup(chan),
    			ast_channel_pickupgroup(chan),
    			(long)elapsed_seconds,
    			ast_str_buffer(variable_str),
    			id_text);
    
    		ao2_cleanup(bridge);
    
    		ast_channel_unlock(chan);
    		chan = ast_channel_unref(chan);
    	}
    
    	if (it_chans) {
    		ast_channel_iterator_destroy(it_chans);
    
    	astman_send_list_complete_start(s, m, "StatusComplete", channels);
    	astman_append(s, "Items: %d\r\n", channels);
    	astman_send_list_complete_end(s);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static int action_sendtext(struct mansession *s, const struct message *m)
    
    {
    	struct ast_channel *c = NULL;
    
    	const char *name = astman_get_header(m, "Channel");
    	const char *textmsg = astman_get_header(m, "Message");
    
    		astman_send_error(s, m, "No channel specified");
    		return 0;
    	}
    
    		astman_send_error(s, m, "No Message specified");
    		return 0;
    	}
    
    
    	if (!(c = ast_channel_get_by_name(name))) {
    
    		astman_send_error(s, m, "No such channel");
    		return 0;
    	}
    
    	res = ast_sendtext(c, textmsg);
    
    		astman_send_ack(s, m, "Success");
    
    		astman_send_error(s, m, "Failure");
    
    /*! \brief  action_redirect: The redirect manager command */
    
    static int action_redirect(struct mansession *s, const struct message *m)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	const char *name = astman_get_header(m, "Channel");
    	const char *name2 = astman_get_header(m, "ExtraChannel");
    	const char *exten = astman_get_header(m, "Exten");
    
    	const char *exten2 = astman_get_header(m, "ExtraExten");
    
    	const char *context = astman_get_header(m, "Context");
    
    	const char *context2 = astman_get_header(m, "ExtraContext");
    
    	const char *priority = astman_get_header(m, "Priority");
    
    	const char *priority2 = astman_get_header(m, "ExtraPriority");
    
    	struct ast_channel *chan;
    	struct ast_channel *chan2;
    	int pi = 0;
    	int pi2 = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    		astman_send_error(s, m, "Channel not specified");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    	if (ast_strlen_zero(context)) {
    		astman_send_error(s, m, "Context not specified");
    		return 0;
    	}
    	if (ast_strlen_zero(exten)) {
    		astman_send_error(s, m, "Exten not specified");
    		return 0;
    	}
    	if (ast_strlen_zero(priority)) {
    		astman_send_error(s, m, "Priority not specified");
    		return 0;
    	}
    	if (sscanf(priority, "%30d", &pi) != 1) {
    		pi = ast_findlabel_extension(NULL, context, exten, priority, NULL);
    	}
    	if (pi < 1) {
    		astman_send_error(s, m, "Priority is invalid");
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (!ast_strlen_zero(name2) && !ast_strlen_zero(context2)) {
    		/* We have an ExtraChannel and an ExtraContext */
    		if (ast_strlen_zero(exten2)) {
    			astman_send_error(s, m, "ExtraExten not specified");
    			return 0;
    		}
    		if (ast_strlen_zero(priority2)) {
    			astman_send_error(s, m, "ExtraPriority not specified");
    			return 0;
    		}
    		if (sscanf(priority2, "%30d", &pi2) != 1) {
    			pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL);
    		}
    		if (pi2 < 1) {
    			astman_send_error(s, m, "ExtraPriority is invalid");
    
    	chan = ast_channel_get_by_name(name);
    	if (!chan) {
    
    		snprintf(buf, sizeof(buf), "Channel does not exist: %s", name);
    		astman_send_error(s, m, buf);
    
    	if (ast_check_hangup_locked(chan)) {
    
    		astman_send_error(s, m, "Redirect failed, channel not up.");
    
    	if (ast_strlen_zero(name2)) {
    		/* Single channel redirect in progress. */
    		res = ast_async_goto(chan, context, exten, pi);
    		if (!res) {
    			astman_send_ack(s, m, "Redirect successful");
    		} else {
    			astman_send_error(s, m, "Redirect failed");
    		}
    		chan = ast_channel_unref(chan);
    		return 0;
    
    	chan2 = ast_channel_get_by_name(name2);
    	if (!chan2) {
    		snprintf(buf, sizeof(buf), "ExtraChannel does not exist: %s", name2);
    		astman_send_error(s, m, buf);
    
    		return 0;
    	}
    	if (ast_check_hangup_locked(chan2)) {
    		astman_send_error(s, m, "Redirect failed, extra channel not up.");
    
    	/* Dual channel redirect in progress. */
    
    	ast_channel_lock(chan);
    	if (ast_channel_is_bridged(chan)) {
    
    		ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
    
    	ast_channel_unlock(chan);
    
    	ast_channel_lock(chan2);
    	if (ast_channel_is_bridged(chan2)) {
    
    		ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
    
    	res = ast_async_goto(chan, context, exten, pi);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!res) {
    
    		if (!ast_strlen_zero(context2)) {
    			res = ast_async_goto(chan2, context2, exten2, pi2);
    
    			res = ast_async_goto(chan2, context, exten, pi);
    		}
    		if (!res) {
    			astman_send_ack(s, m, "Dual Redirect successful");
    		} else {
    			astman_send_error(s, m, "Secondary redirect failed");
    
    		astman_send_error(s, m, "Redirect failed");
    
    		ast_channel_clear_flag(chan, AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
    
    		ast_channel_clear_flag(chan2, AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
    
    	chan2 = ast_channel_unref(chan2);
    	chan = ast_channel_unref(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    static int action_blind_transfer(struct mansession *s, const struct message *m)
    {
    	const char *name = astman_get_header(m, "Channel");
    	const char *exten = astman_get_header(m, "Exten");
    	const char *context = astman_get_header(m, "Context");
    
    	struct ast_channel *chan;
    
    
    	if (ast_strlen_zero(name)) {
    		astman_send_error(s, m, "No channel specified");
    		return 0;
    	}
    
    	if (ast_strlen_zero(exten)) {
    		astman_send_error(s, m, "No extension specified");
    		return 0;
    	}
    
    	chan = ast_channel_get_by_name(name);
    	if (!chan) {
    		astman_send_error(s, m, "Channel specified does not exist");
    		return 0;
    	}
    
    	if (ast_strlen_zero(context)) {
    		context = ast_channel_context(chan);
    	}
    
    
    	switch (ast_bridge_transfer_blind(1, chan, exten, context, NULL, NULL)) {
    
    	case AST_BRIDGE_TRANSFER_NOT_PERMITTED:
    		astman_send_error(s, m, "Transfer not permitted");
    		break;
    	case AST_BRIDGE_TRANSFER_INVALID:
    		astman_send_error(s, m, "Transfer invalid");
    		break;
    	case AST_BRIDGE_TRANSFER_FAIL:
    		astman_send_error(s, m, "Transfer failed");
    		break;
    	case AST_BRIDGE_TRANSFER_SUCCESS:
    		astman_send_ack(s, m, "Transfer succeeded");
    		break;
    	}
    
    
    	ast_channel_unref(chan);
    
    static int action_atxfer(struct mansession *s, const struct message *m)
    {
    	const char *name = astman_get_header(m, "Channel");
    	const char *exten = astman_get_header(m, "Exten");
    	const char *context = astman_get_header(m, "Context");
    	struct ast_channel *chan = NULL;
    
    	char feature_code[AST_FEATURE_MAX_LEN];
    	const char *digit;
    
    		astman_send_error(s, m, "No channel specified");
    
    		return 0;
    	}
    	if (ast_strlen_zero(exten)) {
    
    		astman_send_error(s, m, "No extension specified");
    
    	if (!(chan = ast_channel_get_by_name(name))) {
    		astman_send_error(s, m, "Channel specified does not exist");
    
    	ast_channel_lock(chan);
    	if (ast_get_builtin_feature(chan, "atxfer", feature_code, sizeof(feature_code)) ||
    			ast_strlen_zero(feature_code)) {
    		ast_channel_unlock(chan);
    		astman_send_error(s, m, "No attended transfer feature code found");
    		ast_channel_unref(chan);
    
    	ast_channel_unlock(chan);
    
    	if (!ast_strlen_zero(context)) {
    		pbx_builtin_setvar_helper(chan, "TRANSFER_CONTEXT", context);
    	}
    
    
    	for (digit = feature_code; *digit; ++digit) {
    		struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
    
    	for (digit = exten; *digit; ++digit) {
    		struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
    
    	astman_send_ack(s, m, "Atxfer successfully queued");
    
    static int action_cancel_atxfer(struct mansession *s, const struct message *m)
    {
    	const char *name = astman_get_header(m, "Channel");
    	struct ast_channel *chan = NULL;
    	char *feature_code;
    	const char *digit;
    
    	if (ast_strlen_zero(name)) {
    		astman_send_error(s, m, "No channel specified");
    		return 0;
    	}
    
    	if (!(chan = ast_channel_get_by_name(name))) {
    		astman_send_error(s, m, "Channel specified does not exist");
    		return 0;
    	}
    
    	ast_channel_lock(chan);
    	feature_code = ast_get_chan_features_atxferabort(chan);
    	ast_channel_unlock(chan);
    
    	if (!feature_code) {
    		astman_send_error(s, m, "No disconnect feature code found");
    		ast_channel_unref(chan);
    		return 0;
    	}
    
    	for (digit = feature_code; *digit; ++digit) {
    		struct ast_frame f = { AST_FRAME_DTMF, .subclass.integer = *digit };
    		ast_queue_frame(chan, &f);
    	}
    	ast_free(feature_code);
    
    	chan = ast_channel_unref(chan);
    
    	astman_send_ack(s, m, "CancelAtxfer successfully queued");
    
    	return 0;
    }
    
    
    
    static int check_blacklist(const char *cmd)
    {
    	char *cmd_copy, *cur_cmd;
    
    	char *cmd_words[AST_MAX_CMD_LEN] = { NULL, };