diff options
-rw-r--r-- | addons/app_mysql.c | 54 | ||||
-rw-r--r-- | funcs/func_realtime.c | 17 | ||||
-rw-r--r-- | include/asterisk/channel.h | 15 | ||||
-rw-r--r-- | main/config.c | 11 | ||||
-rw-r--r-- | main/features.c | 5 | ||||
-rw-r--r-- | main/manager.c | 139 |
6 files changed, 170 insertions, 71 deletions
diff --git a/addons/app_mysql.c b/addons/app_mysql.c index dda45243b..2e1b4f4dc 100644 --- a/addons/app_mysql.c +++ b/addons/app_mysql.c @@ -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; - - AST_NONSTANDARD_APP_ARGS(args, data, ' '); + char *parse = ast_strdupa(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; diff --git a/funcs/func_realtime.c b/funcs/func_realtime.c index bd4b37dfe..886b5b456 100644 --- a/funcs/func_realtime.c +++ b/funcs/func_realtime.c @@ -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); diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 9b408660b..858657ab0 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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 */ diff --git a/main/config.c b/main/config.c index f56421ee0..cf2b84c72 100644 --- a/main/config.c +++ b/main/config.c @@ -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 */ diff --git a/main/features.c b/main/features.c index 44f140e16..2f2716eb7 100644 --- a/main/features.c +++ b/main/features.c @@ -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) { diff --git a/main/manager.c b/main/manager.c index ba5beb42e..99d03fbcf 100644 --- a/main/manager.c +++ b/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"); - 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; } - 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"); + 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 (!(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"); + } + chan = ast_channel_unref(chan); + return 0; } - if (chan2 && ast_check_hangup_locked(chan2)) { - astman_send_error(s, m, "Redirect failed, extra channel not up."); + 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; } + /* Dual channel redirect in progress. */ 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 */ + /* 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); + /* 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(name2)) { - if (chan2) { - 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 */ - ast_channel_unlock(chan2); - } - 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"); - } + if (!ast_strlen_zero(context2)) { + res = ast_async_goto(chan2, context2, exten2, pi2); } else { - astman_send_ack(s, m, "Redirect successful"); + 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"); } } 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; } |