diff options
author | Joshua Colp <jcolp@digium.com> | 2014-05-28 11:37:50 +0000 |
---|---|---|
committer | Joshua Colp <jcolp@digium.com> | 2014-05-28 11:37:50 +0000 |
commit | dcfae78574b37f64288a0b525f9306f27e3087ba (patch) | |
tree | 8e3173c80cbb2950f7f6ff8d25526e913bd249bd /res/res_config_odbc.c | |
parent | e5194c91fcf0e456b6ead82564023c69245c04b1 (diff) |
res_config_odbc: Use dynamically sized buffers to store row data so values do not get truncated.
ASTERISK-23582 #close
ASTERISk-23582 #comment Reported by: Walter Doekes
Review: https://reviewboard.asterisk.org/r/3557/
........
Merged revisions 414693 from http://svn.asterisk.org/svn/asterisk/branches/1.8
........
Merged revisions 414694 from http://svn.asterisk.org/svn/asterisk/branches/11
........
Merged revisions 414695 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@414696 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/res_config_odbc.c')
-rw-r--r-- | res/res_config_odbc.c | 136 |
1 files changed, 119 insertions, 17 deletions
diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c index 6fc9ea28b..4806bd208 100644 --- a/res/res_config_odbc.c +++ b/res/res_config_odbc.c @@ -48,6 +48,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/stringfields.h" AST_THREADSTORAGE(sql_buf); +AST_THREADSTORAGE(rowdata_buf); struct custom_prepare_struct { const char *sql; @@ -166,7 +167,7 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl SQLHSTMT stmt; char sql[1024]; char coltitle[256]; - char rowdata[2048]; + struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128); char *op; const struct ast_variable *field = fields; char *stringp; @@ -237,7 +238,6 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl return NULL; } for (x = 0; x < colcount; x++) { - rowdata[0] = '\0'; colsize = 0; collen = sizeof(coltitle); res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, @@ -250,14 +250,25 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl return NULL; } + ast_str_reset(rowdata); indicator = 0; - res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator); - if (indicator == SQL_NULL_DATA) - rowdata[0] = '\0'; - else if (ast_strlen_zero(rowdata)) { + + res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator); + ast_str_update(rowdata); + if (indicator == SQL_NULL_DATA) { + ast_str_reset(rowdata); + } else if (!ast_str_strlen(rowdata)) { /* Because we encode the empty string for a NULL, we will encode * actual empty strings as a string containing a single whitespace. */ - ast_copy_string(rowdata, " ", sizeof(rowdata)); + ast_str_set(&rowdata, -1, "%s", " "); + } else if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) { + if (indicator != ast_str_strlen(rowdata)) { + /* If the available space was not enough to contain the row data enlarge and read in the rest */ + ast_str_make_space(&rowdata, indicator + 1); + res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata), + ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator); + ast_str_update(rowdata); + } } if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { @@ -267,7 +278,8 @@ static struct ast_variable *realtime_odbc(const char *database, const char *tabl ast_odbc_release_obj(obj); return NULL; } - stringp = rowdata; + + stringp = ast_str_buffer(rowdata); while (stringp) { chunk = strsep(&stringp, ";"); if (!ast_strlen_zero(ast_strip(chunk))) { @@ -311,7 +323,7 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char * SQLHSTMT stmt; char sql[1024]; char coltitle[256]; - char rowdata[2048]; + struct ast_str *rowdata = ast_str_thread_get(&rowdata_buf, 128); const char *initfield; char *op; const struct ast_variable *field = fields; @@ -397,7 +409,6 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char * continue; } for (x=0;x<colcount;x++) { - rowdata[0] = '\0'; colsize = 0; collen = sizeof(coltitle); res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, @@ -408,17 +419,31 @@ static struct ast_config *realtime_multi_odbc(const char *database, const char * goto next_sql_fetch; } + ast_str_reset(rowdata); indicator = 0; - res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &indicator); - if (indicator == SQL_NULL_DATA) + + res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata), ast_str_size(rowdata), &indicator); + ast_str_update(rowdata); + if (indicator == SQL_NULL_DATA) { continue; + } + + if ((res == SQL_SUCCESS) || (res == SQL_SUCCESS_WITH_INFO)) { + if (indicator != ast_str_strlen(rowdata)) { + /* If the available space was not enough to contain the row data enlarge and read in the rest */ + ast_str_make_space(&rowdata, indicator + 1); + res = SQLGetData(stmt, x + 1, SQL_CHAR, ast_str_buffer(rowdata) + ast_str_strlen(rowdata), + ast_str_size(rowdata) - ast_str_strlen(rowdata), &indicator); + ast_str_update(rowdata); + } + } if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql); ast_category_destroy(cat); goto next_sql_fetch; } - stringp = rowdata; + stringp = ast_str_buffer(rowdata); while (stringp) { chunk = strsep(&stringp, ";"); if (!ast_strlen_zero(ast_strip(chunk))) { @@ -799,16 +824,41 @@ static int destroy_odbc(const char *database, const char *table, const char *key return -1; } - struct config_odbc_obj { char *sql; unsigned long cat_metric; char category[128]; char var_name[128]; - char var_val[1024]; /* changed from 128 to 1024 via bug 8251 */ + char *var_val; + unsigned long var_val_size; SQLLEN err; }; + +static SQLHSTMT length_determination_odbc_prepare(struct odbc_obj *obj, void *data) +{ + struct config_odbc_obj *q = data; + SQLHSTMT sth; + int res; + + res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &sth); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_verb(4, "Failure in AllocStatement %d\n", res); + return NULL; + } + + res = SQLPrepare(sth, (unsigned char *)q->sql, SQL_NTS); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_verb(4, "Error in PREPARE %d\n", res); + SQLFreeHandle(SQL_HANDLE_STMT, sth); + return NULL; + } + + SQLBindCol(sth, 1, SQL_C_ULONG, &q->var_val_size, sizeof(q->var_val_size), &q->err); + + return sth; +} + static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data) { struct config_odbc_obj *q = data; @@ -831,7 +881,7 @@ static SQLHSTMT config_odbc_prepare(struct odbc_obj *obj, void *data) SQLBindCol(sth, 1, SQL_C_ULONG, &q->cat_metric, sizeof(q->cat_metric), &q->err); SQLBindCol(sth, 2, SQL_C_CHAR, q->category, sizeof(q->category), &q->err); SQLBindCol(sth, 3, SQL_C_CHAR, q->var_name, sizeof(q->var_name), &q->err); - SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, sizeof(q->var_val), &q->err); + SQLBindCol(sth, 4, SQL_C_CHAR, q->var_val, q->var_val_size, &q->err); return sth; } @@ -862,16 +912,64 @@ static struct ast_config *config_odbc(const char *database, const char *table, c if (!obj) return NULL; + q.sql = sqlbuf; + + ast_build_string(&sql, &sqlleft, "SELECT MAX(LENGTH(var_val)) FROM %s WHERE filename='%s'", table, file); + + stmt = ast_odbc_prepare_and_execute(obj, length_determination_odbc_prepare, &q); + + if (!stmt) { + ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql); + ast_odbc_release_obj(obj); + return NULL; + } + + res = SQLNumResultCols(stmt, &rowcount); + + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + + if (!rowcount) { + ast_log(LOG_NOTICE, "found nothing\n"); + ast_odbc_release_obj(obj); + return cfg; + } + + /* There will be only one result for this, the maximum length of a variable value */ + if (SQLFetch(stmt) == SQL_NO_DATA) { + ast_log(LOG_NOTICE, "Failed to determine maximum length of a configuration value\n"); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + return NULL; + } + + /* Reset stuff to a fresh state for the actual query which will retrieve all configuration */ + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + sql = sqlbuf; + sqlleft = sizeof(sqlbuf); + ast_build_string(&sql, &sqlleft, "SELECT cat_metric, category, var_name, var_val FROM %s ", table); ast_build_string(&sql, &sqlleft, "WHERE filename='%s' AND commented=0 ", file); ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); - q.sql = sqlbuf; + + q.var_val_size += 1; + q.var_val = ast_malloc(q.var_val_size); + if (!q.var_val) { + ast_log(LOG_WARNING, "Could not create buffer for reading in configuration values for '%s'\n", file); + ast_odbc_release_obj(obj); + return NULL; + } stmt = ast_odbc_prepare_and_execute(obj, config_odbc_prepare, &q); if (!stmt) { ast_log(LOG_WARNING, "SQL select error!\n[%s]\n\n", sql); ast_odbc_release_obj(obj); + ast_free(q.var_val); return NULL; } @@ -881,12 +979,14 @@ static struct ast_config *config_odbc(const char *database, const char *table, c ast_log(LOG_WARNING, "SQL NumResultCols error!\n[%s]\n\n", sql); SQLFreeHandle(SQL_HANDLE_STMT, stmt); ast_odbc_release_obj(obj); + ast_free(q.var_val); return NULL; } if (!rowcount) { ast_log(LOG_NOTICE, "found nothing\n"); ast_odbc_release_obj(obj); + ast_free(q.var_val); return cfg; } @@ -897,6 +997,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c if (!ast_config_internal_load(q.var_val, cfg, loader_flags, "", who_asked)) { SQLFreeHandle(SQL_HANDLE_STMT, stmt); ast_odbc_release_obj(obj); + ast_free(q.var_val); return NULL; } continue; @@ -918,6 +1019,7 @@ static struct ast_config *config_odbc(const char *database, const char *table, c SQLFreeHandle(SQL_HANDLE_STMT, stmt); ast_odbc_release_obj(obj); + ast_free(q.var_val); return cfg; } |