Merged revisions 378374,378377,378384 via svnmerge from
file:///srv/subversion/repos/asterisk/trunk ................ r378374 | rmudgett | 2013-01-02 15:23:16 -0600 (Wed, 02 Jan 2013) | 33 lines Fix AMI redirect action with two channels failing to redirect both channels. The AMI redirect action can fail to redirect two channels that are bridged together. There is a race between the AMI thread redirecting the two channels and the bridge thread noticing that a channel is hungup from the redirects. * Made the bridge wait for both channels to be redirected before exiting. * Made the AMI redirect check that all required headers are present before proceeding with the redirection. * Made the AMI redirect require that any supplied ExtraChannel exist before proceeding. Previously the code fell back to a single channel redirect operation. (closes issue ASTERISK-18975) Reported by: Ben Klang (closes issue ASTERISK-19948) Reported by: Brent Dalgleish Patches: jira_asterisk_19948_v11.patch (license #5621) patch uploaded by rmudgett Tested by: rmudgett, Thomas Sevestre, Deepak Lohani, Kayode Review: https://reviewboard.asterisk.org/r/2243/ ........ Merged revisions 378356 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 378358 from http://svn.asterisk.org/svn/asterisk/branches/11 ................ r378377 | mjordan | 2013-01-02 16:10:32 -0600 (Wed, 02 Jan 2013) | 24 lines Prevent crashes from occurring when reading from data sources with large values When reading configuration data from an Asterisk .conf file or when pulling data from an Asterisk RealTime backend, Asterisk was copying the data on the stack for manipulation. Unfortunately, it is possible to read configuration data or realtime data from some data source that provides a large blob of characters. This could potentially cause a crash via a stack overflow. This patch prevents large sets of data from being read from an ARA backend or from an Asterisk conf file. (issue ASTERISK-20658) Reported by: wdoekes Tested by: wdoekes, mmichelson patches: * issueA20658_dont_process_overlong_config_lines.patch uploaded by wdoekes (license 5674) * issueA20658_func_realtime_limit.patch uploaded by wdoekes (license 5674) ........ Merged revisions 378375 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 378376 from http://svn.asterisk.org/svn/asterisk/branches/11 ................ r378384 | mjordan | 2013-01-02 16:19:32 -0600 (Wed, 02 Jan 2013) | 11 lines Clean up app_mysql's application entry points to properly parse arguments When parsing arguments, application entry points should not attempt to directly modify the parameters to the function. This patch properly duplicates the passed in parameters before attempting to parse them. (issue ASTERISK-20658) Reported by: wdoekes patches: issueA20658_sanitize_app_mysql.patch uploaded by wdoekes (license 5674) ................ git-svn-id: https://origsvn.digium.com/svn/asterisk/team/mmichelson/threadpool@378385 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
parent
675914bb17
commit
b375abae50
|
@ -292,16 +292,17 @@ static int safe_scan_int(char **data, char *delim, int def)
|
|||
return res;
|
||||
}
|
||||
|
||||
static int aMYSQL_set(struct ast_channel *chan, char *data)
|
||||
static int aMYSQL_set(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
char *var, *tmp;
|
||||
char *var, *tmp, *parse;
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(set);
|
||||
AST_APP_ARG(variable);
|
||||
AST_APP_ARG(value);
|
||||
);
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args, data, ' ');
|
||||
parse = ast_strdupa(data);
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
|
||||
if (args.argc == 3) {
|
||||
var = ast_alloca(6 + strlen(args.variable) + 1);
|
||||
|
@ -317,7 +318,7 @@ static int aMYSQL_set(struct ast_channel *chan, char *data)
|
|||
}
|
||||
|
||||
/* MYSQL operations */
|
||||
static int aMYSQL_connect(struct ast_channel *chan, char *data)
|
||||
static int aMYSQL_connect(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(connect);
|
||||
|
@ -333,8 +334,9 @@ static int aMYSQL_connect(struct ast_channel *chan, char *data)
|
|||
const char *ctimeout;
|
||||
unsigned int port = 0;
|
||||
char *port_str;
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args, data, ' ');
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
|
||||
if (args.argc < 6) {
|
||||
ast_log(LOG_WARNING, "MYSQL_connect is missing some arguments\n");
|
||||
|
@ -385,7 +387,7 @@ static int aMYSQL_connect(struct ast_channel *chan, char *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int aMYSQL_query(struct ast_channel *chan, char *data)
|
||||
static int aMYSQL_query(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
AST_DECLARE_APP_ARGS(args,
|
||||
AST_APP_ARG(query);
|
||||
|
@ -397,8 +399,9 @@ static int aMYSQL_query(struct ast_channel *chan, char *data)
|
|||
MYSQL_RES *mysqlres;
|
||||
int connid;
|
||||
int mysql_query_res;
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args, data, ' ');
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
|
||||
if (args.argc != 4 || (connid = atoi(args.connid)) == 0) {
|
||||
ast_log(LOG_WARNING, "missing some arguments\n");
|
||||
|
@ -426,7 +429,7 @@ static int aMYSQL_query(struct ast_channel *chan, char *data)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int aMYSQL_nextresult(struct ast_channel *chan, char *data)
|
||||
static int aMYSQL_nextresult(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL *mysql;
|
||||
MYSQL_RES *mysqlres;
|
||||
|
@ -436,8 +439,9 @@ static int aMYSQL_nextresult(struct ast_channel *chan, char *data)
|
|||
AST_APP_ARG(connid);
|
||||
);
|
||||
int connid = -1;
|
||||
char *parse = ast_strdupa(data);
|
||||
|
||||
AST_NONSTANDARD_APP_ARGS(args, data, ' ');
|
||||
AST_NONSTANDARD_APP_ARGS(args, parse, ' ');
|
||||
sscanf(args.connid, "%30d", &connid);
|
||||
|
||||
if (args.argc != 3 || connid <= 0) {
|
||||
|
@ -466,7 +470,7 @@ static int aMYSQL_nextresult(struct ast_channel *chan, char *data)
|
|||
}
|
||||
|
||||
|
||||
static int aMYSQL_fetch(struct ast_channel *chan, char *data)
|
||||
static int aMYSQL_fetch(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL_RES *mysqlres;
|
||||
MYSQL_ROW mysqlrow;
|
||||
|
@ -518,13 +522,14 @@ static int aMYSQL_fetch(struct ast_channel *chan, char *data)
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int aMYSQL_clear(struct ast_channel *chan, char *data)
|
||||
static int aMYSQL_clear(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL_RES *mysqlres;
|
||||
|
||||
int id;
|
||||
strsep(&data, " "); /* eat the first token, we already know it :P */
|
||||
id = safe_scan_int(&data, " \n", -1);
|
||||
char *parse = ast_strdupa(data);
|
||||
strsep(&parse, " "); /* eat the first token, we already know it :P */
|
||||
id = safe_scan_int(&parse, " \n", -1);
|
||||
if ((mysqlres = find_identifier(id, AST_MYSQL_ID_RESID)) == NULL) {
|
||||
ast_log(LOG_WARNING, "Invalid result identifier %d passed in aMYSQL_clear\n", id);
|
||||
} else {
|
||||
|
@ -535,13 +540,14 @@ static int aMYSQL_clear(struct ast_channel *chan, char *data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int aMYSQL_disconnect(struct ast_channel *chan, char *data)
|
||||
static int aMYSQL_disconnect(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
MYSQL *mysql;
|
||||
int id;
|
||||
strsep(&data, " "); /* eat the first token, we already know it :P */
|
||||
char *parse = ast_strdupa(data);
|
||||
strsep(&parse, " "); /* eat the first token, we already know it :P */
|
||||
|
||||
id = safe_scan_int(&data, " \n", -1);
|
||||
id = safe_scan_int(&parse, " \n", -1);
|
||||
if ((mysql = find_identifier(id, AST_MYSQL_ID_CONNID)) == NULL) {
|
||||
ast_log(LOG_WARNING, "Invalid connection identifier %d passed in aMYSQL_disconnect\n", id);
|
||||
} else {
|
||||
|
@ -584,19 +590,19 @@ static int MYSQL_exec(struct ast_channel *chan, const char *data)
|
|||
ast_mutex_lock(&_mysql_mutex);
|
||||
|
||||
if (strncasecmp("connect", data, strlen("connect")) == 0) {
|
||||
result = aMYSQL_connect(chan, ast_strdupa(data));
|
||||
result = aMYSQL_connect(chan, data);
|
||||
} else if (strncasecmp("query", data, strlen("query")) == 0) {
|
||||
result = aMYSQL_query(chan, ast_strdupa(data));
|
||||
result = aMYSQL_query(chan, data);
|
||||
} else if (strncasecmp("nextresult", data, strlen("nextresult")) == 0) {
|
||||
result = aMYSQL_nextresult(chan, ast_strdupa(data));
|
||||
result = aMYSQL_nextresult(chan, data);
|
||||
} else if (strncasecmp("fetch", data, strlen("fetch")) == 0) {
|
||||
result = aMYSQL_fetch(chan, ast_strdupa(data));
|
||||
result = aMYSQL_fetch(chan, data);
|
||||
} else if (strncasecmp("clear", data, strlen("clear")) == 0) {
|
||||
result = aMYSQL_clear(chan, ast_strdupa(data));
|
||||
result = aMYSQL_clear(chan, data);
|
||||
} else if (strncasecmp("disconnect", data, strlen("disconnect")) == 0) {
|
||||
result = aMYSQL_disconnect(chan, ast_strdupa(data));
|
||||
result = aMYSQL_disconnect(chan, data);
|
||||
} else if (strncasecmp("set", data, 3) == 0) {
|
||||
result = aMYSQL_set(chan, ast_strdupa(data));
|
||||
result = aMYSQL_set(chan, data);
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Unknown argument to MYSQL application : %s\n", data);
|
||||
result = -1;
|
||||
|
|
|
@ -219,6 +219,13 @@ static int function_realtime_read(struct ast_channel *chan, const char *cmd, cha
|
|||
/* add space for delimiters and final '\0' */
|
||||
resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
|
||||
|
||||
if (resultslen > len) {
|
||||
ast_log(LOG_WARNING, "Failed to fetch. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* len is going to be sensible, so we don't need to check for stack
|
||||
* overflows here. */
|
||||
out = ast_str_alloca(resultslen);
|
||||
for (var = head; var; var = var->next)
|
||||
ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
|
||||
|
@ -439,6 +446,16 @@ static int function_realtime_readdestroy(struct ast_channel *chan, const char *c
|
|||
/* add space for delimiters and final '\0' */
|
||||
resultslen += n * (strlen(args.delim1) + strlen(args.delim2)) + 1;
|
||||
|
||||
if (resultslen > len) {
|
||||
/* Unfortunately this does mean that we cannot destroy the row
|
||||
* anymore. But OTOH, we're not destroying someones data without
|
||||
* giving him the chance to look at it. */
|
||||
ast_log(LOG_WARNING, "Failed to fetch/destroy. Realtime data is too large: need %zu, have %zu.\n", resultslen, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* len is going to be sensible, so we don't need to check for stack
|
||||
* overflows here. */
|
||||
out = ast_str_alloca(resultslen);
|
||||
for (var = head; var; var = var->next) {
|
||||
ast_str_append(&out, 0, "%s%s%s%s", var->name, args.delim2, var->value, args.delim1);
|
||||
|
|
|
@ -903,12 +903,19 @@ enum {
|
|||
* some non-traditional dialplans (like AGI) to continue to function.
|
||||
*/
|
||||
AST_FLAG_DISABLE_WORKAROUNDS = (1 << 20),
|
||||
/*! Disable device state event caching. This allows allows channel
|
||||
* drivers to selectively prevent device state events from being cached
|
||||
* by certain channels such as anonymous calls which have no persistent
|
||||
* represenatation that can be tracked.
|
||||
/*!
|
||||
* Disable device state event caching. This allows channel
|
||||
* drivers to selectively prevent device state events from being
|
||||
* cached by certain channels such as anonymous calls which have
|
||||
* no persistent represenatation that can be tracked.
|
||||
*/
|
||||
AST_FLAG_DISABLE_DEVSTATE_CACHE = (1 << 21),
|
||||
/*!
|
||||
* This flag indicates that a dual channel redirect is in
|
||||
* progress. The bridge needs to wait until the flag is cleared
|
||||
* to continue.
|
||||
*/
|
||||
AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT = (1 << 22),
|
||||
};
|
||||
|
||||
/*! \brief ast_bridge_config flags */
|
||||
|
|
|
@ -1646,6 +1646,17 @@ static struct ast_config *config_text_file_load(const char *database, const char
|
|||
while (!feof(f)) {
|
||||
lineno++;
|
||||
if (fgets(buf, sizeof(buf), f)) {
|
||||
/* Skip lines that are too long */
|
||||
if (strlen(buf) == sizeof(buf) - 1 && buf[sizeof(buf) - 1] != '\n') {
|
||||
ast_log(LOG_WARNING, "Line %d too long, skipping. It begins with: %.32s...\n", lineno, buf);
|
||||
while (fgets(buf, sizeof(buf), f)) {
|
||||
if (strlen(buf) != sizeof(buf) - 1 || buf[sizeof(buf) - 1] == '\n') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, CONFIG_FLAG_WITHCOMMENTS) && lline_buffer && ast_str_strlen(lline_buffer)) {
|
||||
CB_ADD(&comment_buffer, ast_str_buffer(lline_buffer)); /* add the current lline buffer to the comment buffer */
|
||||
ast_str_reset(lline_buffer); /* erase the lline buffer */
|
||||
|
|
|
@ -4772,6 +4772,11 @@ before_you_go:
|
|||
silgen = NULL;
|
||||
}
|
||||
|
||||
/* Wait for any dual redirect to complete. */
|
||||
while (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) {
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT)) {
|
||||
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT); /* its job is done */
|
||||
if (bridge_cdr) {
|
||||
|
|
131
main/manager.c
131
main/manager.c
|
@ -3709,6 +3709,7 @@ static int action_sendtext(struct mansession *s, const struct message *m)
|
|||
/*! \brief action_redirect: The redirect manager command */
|
||||
static int action_redirect(struct mansession *s, const struct message *m)
|
||||
{
|
||||
char buf[256];
|
||||
const char *name = astman_get_header(m, "Channel");
|
||||
const char *name2 = astman_get_header(m, "ExtraChannel");
|
||||
const char *exten = astman_get_header(m, "Exten");
|
||||
|
@ -3717,8 +3718,10 @@ static int action_redirect(struct mansession *s, const struct message *m)
|
|||
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, *chan2 = NULL;
|
||||
int pi, pi2 = 0;
|
||||
struct ast_channel *chan;
|
||||
struct ast_channel *chan2;
|
||||
int pi = 0;
|
||||
int pi2 = 0;
|
||||
int res;
|
||||
|
||||
if (ast_strlen_zero(name)) {
|
||||
|
@ -3726,84 +3729,134 @@ static int action_redirect(struct mansession *s, const struct message *m)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(priority) && (sscanf(priority, "%30d", &pi) != 1)) {
|
||||
if ((pi = ast_findlabel_extension(NULL, context, exten, priority, NULL)) < 1) {
|
||||
astman_send_error(s, m, "Invalid priority");
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(priority2) && (sscanf(priority2, "%30d", &pi2) != 1)) {
|
||||
if ((pi2 = ast_findlabel_extension(NULL, context2, exten2, priority2, NULL)) < 1) {
|
||||
astman_send_error(s, m, "Invalid ExtraPriority");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(chan = ast_channel_get_by_name(name))) {
|
||||
char buf[256];
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_check_hangup_locked(chan)) {
|
||||
astman_send_error(s, m, "Redirect failed, channel not up.");
|
||||
chan = ast_channel_unref(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(name2)) {
|
||||
chan2 = ast_channel_get_by_name(name2);
|
||||
if (ast_strlen_zero(name2)) {
|
||||
/* Single channel redirect in progress. */
|
||||
if (ast_channel_pbx(chan)) {
|
||||
ast_channel_lock(chan);
|
||||
/* don't let the after-bridge code run the h-exten */
|
||||
ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT);
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
if (chan2 && ast_check_hangup_locked(chan2)) {
|
||||
astman_send_error(s, m, "Redirect failed, extra channel not up.");
|
||||
chan = ast_channel_unref(chan);
|
||||
chan2 = ast_channel_unref(chan2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (ast_channel_pbx(chan)) {
|
||||
ast_channel_lock(chan);
|
||||
ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
|
||||
ast_channel_unlock(chan);
|
||||
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);
|
||||
chan = ast_channel_unref(chan);
|
||||
return 0;
|
||||
}
|
||||
if (ast_check_hangup_locked(chan2)) {
|
||||
astman_send_error(s, m, "Redirect failed, extra channel not up.");
|
||||
chan2 = ast_channel_unref(chan2);
|
||||
chan = ast_channel_unref(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
res = ast_async_goto(chan, context, exten, pi);
|
||||
if (!res) {
|
||||
if (!ast_strlen_zero(name2)) {
|
||||
if (chan2) {
|
||||
/* Dual channel redirect in progress. */
|
||||
if (ast_channel_pbx(chan)) {
|
||||
ast_channel_lock(chan);
|
||||
/* don't let the after-bridge code run the h-exten */
|
||||
ast_set_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_HANGUP_DONT
|
||||
| AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
if (ast_channel_pbx(chan2)) {
|
||||
ast_channel_lock(chan2);
|
||||
ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
|
||||
/* don't let the after-bridge code run the h-exten */
|
||||
ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_HANGUP_DONT
|
||||
| AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
|
||||
ast_channel_unlock(chan2);
|
||||
}
|
||||
res = ast_async_goto(chan, context, exten, pi);
|
||||
if (!res) {
|
||||
if (!ast_strlen_zero(context2)) {
|
||||
res = ast_async_goto(chan2, context2, exten2, pi2);
|
||||
} else {
|
||||
res = ast_async_goto(chan2, context, exten, pi);
|
||||
}
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
if (!res) {
|
||||
astman_send_ack(s, m, "Dual Redirect successful");
|
||||
} else {
|
||||
astman_send_error(s, m, "Secondary redirect failed");
|
||||
}
|
||||
} else {
|
||||
astman_send_ack(s, m, "Redirect successful");
|
||||
}
|
||||
} else {
|
||||
astman_send_error(s, m, "Redirect failed");
|
||||
}
|
||||
|
||||
chan = ast_channel_unref(chan);
|
||||
if (chan2) {
|
||||
chan2 = ast_channel_unref(chan2);
|
||||
/* Release the bridge wait. */
|
||||
if (ast_channel_pbx(chan)) {
|
||||
ast_channel_lock(chan);
|
||||
ast_clear_flag(ast_channel_flags(chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
|
||||
ast_channel_unlock(chan);
|
||||
}
|
||||
if (ast_channel_pbx(chan2)) {
|
||||
ast_channel_lock(chan2);
|
||||
ast_clear_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT);
|
||||
ast_channel_unlock(chan2);
|
||||
}
|
||||
|
||||
chan2 = ast_channel_unref(chan2);
|
||||
chan = ast_channel_unref(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue