diff options
-rw-r--r-- | addons/res_config_mysql.c | 364 | ||||
-rw-r--r-- | apps/app_fax.c | 14 | ||||
-rw-r--r-- | apps/app_minivm.c | 8 | ||||
-rw-r--r-- | apps/app_queue.c | 20 | ||||
-rw-r--r-- | include/asterisk/json.h | 35 | ||||
-rw-r--r-- | main/aoc.c | 50 | ||||
-rw-r--r-- | main/cel.c | 4 | ||||
-rw-r--r-- | main/json.c | 146 | ||||
-rw-r--r-- | res/res_fax.c | 12 | ||||
-rw-r--r-- | res/stasis/app.c | 2 | ||||
-rw-r--r-- | tests/test_json.c | 34 |
11 files changed, 317 insertions, 372 deletions
diff --git a/addons/res_config_mysql.c b/addons/res_config_mysql.c index be5d00284..1041c1120 100644 --- a/addons/res_config_mysql.c +++ b/addons/res_config_mysql.c @@ -126,7 +126,6 @@ static char *handle_cli_realtime_mysql_status(struct ast_cli_entry *e, int cmd, static char *handle_cli_realtime_mysql_cache(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a); static int load_mysql_config(struct ast_config *config, const char *category, struct mysql_conn *conn); static int require_mysql(const char *database, const char *tablename, va_list ap); -static int internal_require(const char *database, const char *table, ...) attribute_sentinel; static struct ast_cli_entry cli_realtime_mysql_status[] = { AST_CLI_DEFINE(handle_cli_realtime_mysql_status, "Shows connection information for the MySQL RealTime driver"), @@ -165,16 +164,6 @@ static struct mysql_conn *find_database(const char *database, int for_write) #define release_database(a) ast_mutex_unlock(&(a)->lock) -static int internal_require(const char *database, const char *table, ...) -{ - va_list ap; - int res; - va_start(ap, table); - res = require_mysql(database, table, ap); - va_end(ap); - return res; -} - static void destroy_table(struct tables *table) { struct columns *column; @@ -600,11 +589,6 @@ static int update_mysql(const char *database, const char *tablename, const char ESCAPE_STRING(buf, field->value); ast_str_set(&sql, 0, "UPDATE %s SET `%s` = '%s'", tablename, field->name, ast_str_buffer(buf)); - /* If the column length isn't long enough, give a chance to lengthen it. */ - if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); - } - while ((field = field->next)) { /* If the column is not within the table, then skip it */ if (!(column = find_column(table, field->name))) { @@ -614,11 +598,6 @@ static int update_mysql(const char *database, const char *tablename, const char ESCAPE_STRING(buf, field->value); ast_str_append(&sql, 0, ", `%s` = '%s'", field->name, ast_str_buffer(buf)); - - /* If the column length isn't long enough, give a chance to lengthen it. */ - if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); - } } ESCAPE_STRING(buf, lookup); @@ -703,11 +682,6 @@ static int update2_mysql(const char *database, const char *tablename, const stru ESCAPE_STRING(buf, field->value); ast_str_append(&where, 0, "%s `%s` = '%s'", first ? "" : " AND", field->name, ast_str_buffer(buf)); first = 0; - - /* If the column length isn't long enough, give a chance to lengthen it. */ - if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); - } } first = 1; @@ -721,11 +695,6 @@ static int update2_mysql(const char *database, const char *tablename, const stru ESCAPE_STRING(buf, field->value); ast_str_append(&sql, 0, "%s `%s` = '%s'", first ? "" : ",", field->name, ast_str_buffer(buf)); first = 0; - - /* If the column length isn't long enough, give a chance to lengthen it. */ - if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { - internal_require(database, tablename, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); - } } release_table(table); @@ -759,7 +728,6 @@ static int update2_mysql(const char *database, const char *tablename, const stru static int store_mysql(const char *database, const char *table, const struct ast_variable *rt_fields) { struct mysql_conn *dbh; - my_ulonglong insertid; struct ast_str *sql = ast_str_thread_get(&sql_buf, 16); struct ast_str *sql2 = ast_str_thread_get(&sql2_buf, 16); struct ast_str *buf = ast_str_thread_get(&scratch_buf, 16); @@ -792,15 +760,11 @@ static int store_mysql(const char *database, const char *table, const struct ast ast_str_set(&sql, 0, "INSERT INTO %s (`%s`", table, field->name); ast_str_set(&sql2, 0, ") VALUES ('%s'", ast_str_buffer(buf)); - internal_require(database, table, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL); - while ((field = field->next)) { ESCAPE_STRING(buf, field->value); - if (internal_require(database, table, field->name, RQ_CHAR, ast_str_strlen(buf), SENTINEL) == 0) { - ast_str_append(&sql, 0, ", `%s`", field->name); - ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf)); - } + ast_str_append(&sql, 0, ", `%s`", field->name); + ast_str_append(&sql2, 0, ", '%s'", ast_str_buffer(buf)); } ast_str_append(&sql, 0, "%s)", ast_str_buffer(sql2)); ast_debug(1,"MySQL RealTime: Insert SQL: %s\n", ast_str_buffer(sql)); @@ -812,18 +776,11 @@ static int store_mysql(const char *database, const char *table, const struct ast return -1; } - /*!\note The return value is non-portable and may change in future versions. */ - insertid = mysql_insert_id(&dbh->handle); release_database(dbh); - ast_debug(1, "MySQL RealTime: row inserted on table: %s, id: %llu\n", table, insertid); + ast_debug(1, "MySQL RealTime: row inserted on table: %s\n", table); - /* From http://dev.mysql.com/doc/mysql/en/mysql-affected-rows.html - * An integer greater than zero indicates the number of rows affected - * Zero indicates that no records were updated - * -1 indicates that the query returned an error (although, if the query failed, it should have been caught above.) - */ - return (int)insertid; + return 1; } static int destroy_mysql(const char *database, const char *table, const char *keyfield, const char *lookup, const struct ast_variable *rt_fields) @@ -989,105 +946,14 @@ static int unload_mysql(const char *database, const char *tablename) return cur ? 0 : -1; } -static int modify_mysql(const char *database, const char *tablename, struct columns *column, require_type type, int len) -{ - /*!\note Cannot use ANY of the same scratch space as is used in other functions, as this one is interspersed. */ - struct ast_str *sql = ast_str_thread_get(&modify_buf, 100), *escbuf = ast_str_thread_get(&modify2_buf, 100); - struct ast_str *typestr = ast_str_thread_get(&modify3_buf, 30); - int waschar = strncasecmp(column->type, "char", 4) == 0 ? 1 : 0; - int wasvarchar = strncasecmp(column->type, "varchar", 7) == 0 ? 1 : 0; - int res = 0; - struct mysql_conn *dbh; - - if (!(dbh = find_database(database, 1))) { - return -1; - } - - do { - if (type == RQ_CHAR || waschar || wasvarchar) { - if (wasvarchar) { - ast_str_set(&typestr, 0, "VARCHAR(%d)", len); - } else { - ast_str_set(&typestr, 0, "CHAR(%d)", len); - } - } else if (type == RQ_UINTEGER1) { - ast_str_set(&typestr, 0, "tinyint(3) unsigned"); - } else if (type == RQ_INTEGER1) { - ast_str_set(&typestr, 0, "tinyint(4)"); - } else if (type == RQ_UINTEGER2) { - ast_str_set(&typestr, 0, "smallint(5) unsigned"); - } else if (type == RQ_INTEGER2) { - ast_str_set(&typestr, 0, "smallint(6)"); - } else if (type == RQ_UINTEGER3) { - ast_str_set(&typestr, 0, "mediumint(8) unsigned"); - } else if (type == RQ_INTEGER3) { - ast_str_set(&typestr, 0, "mediumint(8)"); - } else if (type == RQ_UINTEGER4) { - ast_str_set(&typestr, 0, "int(10) unsigned"); - } else if (type == RQ_INTEGER4) { - ast_str_set(&typestr, 0, "int(11)"); - } else if (type == RQ_UINTEGER8) { - ast_str_set(&typestr, 0, "bigint(19) unsigned"); - } else if (type == RQ_INTEGER8) { - ast_str_set(&typestr, 0, "bigint(20)"); - } else if (type == RQ_DATETIME) { - ast_str_set(&typestr, 0, "datetime"); - } else if (type == RQ_DATE) { - ast_str_set(&typestr, 0, "date"); - } else if (type == RQ_FLOAT) { - ast_str_set(&typestr, 0, "FLOAT(%d,2)", len); - } else { - ast_log(LOG_ERROR, "Unknown type (should NEVER happen)\n"); - res = -1; - break; - } - ast_str_set(&sql, 0, "ALTER TABLE %s MODIFY `%s` %s", tablename, column->name, ast_str_buffer(typestr)); - if (!column->null) { - ast_str_append(&sql, 0, " NOT NULL"); - } - if (!ast_strlen_zero(column->dflt)) { - ESCAPE_STRING(escbuf, column->dflt); - ast_str_append(&sql, 0, " DEFAULT '%s'", ast_str_buffer(escbuf)); - } - - if (!mysql_reconnect(dbh)) { - ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql)); - res = -1; - break; - } - - /* Execution. */ - if (mysql_real_query(&dbh->handle, ast_str_buffer(sql), ast_str_strlen(sql))) { - ast_log(LOG_WARNING, "MySQL RealTime: Failed to modify database: %s\n", mysql_error(&dbh->handle)); - ast_debug(1, "MySQL RealTime: Query: %s\n", ast_str_buffer(sql)); - res = -1; - } - } while (0); - - release_database(dbh); - return res; -} - -#define PICK_WHICH_ALTER_ACTION(stringtype) \ - if (table->database->requirements == RQ_WARN) { \ - ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ - "the required data length: %d (detected stringtype)\n", \ - tablename, database, column->name, size); \ - res = -1; \ - } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { \ - table_altered = 1; \ - } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { \ - table_altered = 1; \ - } else { \ - res = -1; \ - } - static int require_mysql(const char *database, const char *tablename, va_list ap) { struct columns *column; struct tables *table = find_table(database, tablename); char *elm; - int type, size, res = 0, table_altered = 0; + int type; + int size; + int res = 0; if (!table) { ast_log(LOG_WARNING, "Table %s not found in database. This table should exist if you're using realtime.\n", tablename); @@ -1097,55 +963,54 @@ static int require_mysql(const char *database, const char *tablename, va_list ap while ((elm = va_arg(ap, char *))) { type = va_arg(ap, require_type); size = va_arg(ap, int); + AST_LIST_TRAVERSE(&table->columns, column, list) { if (strcmp(column->name, elm) == 0) { /* Char can hold anything, as long as it is large enough */ if (strncmp(column->type, "char", 4) == 0 || strncmp(column->type, "varchar", 7) == 0) { if ((size > column->len) && column->len != -1) { - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Realtime table %s@%s: Column '%s' should be at least %d long, but is only %d long.\n", database, tablename, column->name, size, column->len); - res = -1; - } else if (modify_mysql(database, tablename, column, type, size) == 0) { - table_altered = 1; - } else { - res = -1; - } + ast_log(LOG_WARNING, "Realtime table %s@%s: Column '%s' should be at least %d long, but is only %d long.\n", database, tablename, column->name, size, column->len); + res = -1; } } else if (strcasestr(column->type, "unsigned")) { if (!ast_rq_is_int(type)) { - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n", - database, tablename, column->name, column->type, - type == RQ_CHAR ? "char" : type == RQ_FLOAT ? "float" : - type == RQ_DATETIME ? "datetime" : type == RQ_DATE ? "date" : "a rather stiff drink"); - res = -1; - } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { - table_altered = 1; - } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { - table_altered = 1; - } else { - res = -1; - } + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n", + database, tablename, column->name, column->type, + type == RQ_CHAR ? "char" : type == RQ_FLOAT ? "float" : + type == RQ_DATETIME ? "datetime" : type == RQ_DATE ? "date" : "a rather stiff drink"); + res = -1; } else if (strncasecmp(column->type, "tinyint", 1) == 0) { if (type != RQ_UINTEGER1) { - PICK_WHICH_ALTER_ACTION(unsigned tinyint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "smallint", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2) { - PICK_WHICH_ALTER_ACTION(unsigned smallint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "mediumint", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2 && type != RQ_INTEGER2 && type != RQ_UINTEGER3) { - PICK_WHICH_ALTER_ACTION(unsigned mediumint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "int", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2 && type != RQ_INTEGER2 && type != RQ_UINTEGER3 && type != RQ_INTEGER3 && type != RQ_UINTEGER4) { - PICK_WHICH_ALTER_ACTION(unsigned int) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "bigint", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && @@ -1153,45 +1018,52 @@ static int require_mysql(const char *database, const char *tablename, va_list ap type != RQ_UINTEGER3 && type != RQ_INTEGER3 && type != RQ_UINTEGER4 && type != RQ_INTEGER4 && type != RQ_UINTEGER8) { - PICK_WHICH_ALTER_ACTION(unsigned bigint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } } else if (strcasestr(column->type, "int")) { if (!ast_rq_is_int(type)) { - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n", - database, tablename, column->name, column->type, - type == RQ_CHAR ? "char" : type == RQ_FLOAT ? "float" : - type == RQ_DATETIME ? "datetime" : type == RQ_DATE ? "date" : - "to get a life, rather than writing silly error messages"); - res = -1; - } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { - table_altered = 1; - } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { - table_altered = 1; - } else { - res = -1; - } + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' cannot be type '%s' (need %s)\n", + database, tablename, column->name, column->type, + type == RQ_CHAR ? "char" : type == RQ_FLOAT ? "float" : + type == RQ_DATETIME ? "datetime" : type == RQ_DATE ? "date" : + "to get a life, rather than writing silly error messages"); + res = -1; } else if (strncasecmp(column->type, "tinyint", 1) == 0) { if (type != RQ_INTEGER1) { - PICK_WHICH_ALTER_ACTION(tinyint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "smallint", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_INTEGER2) { - PICK_WHICH_ALTER_ACTION(smallint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "mediumint", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2 && type != RQ_INTEGER2 && type != RQ_INTEGER3) { - PICK_WHICH_ALTER_ACTION(mediumint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "int", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && type != RQ_UINTEGER2 && type != RQ_INTEGER2 && type != RQ_UINTEGER3 && type != RQ_INTEGER3 && type != RQ_INTEGER4) { - PICK_WHICH_ALTER_ACTION(int) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } else if (strncasecmp(column->type, "bigint", 1) == 0) { if (type != RQ_UINTEGER1 && type != RQ_INTEGER1 && @@ -1199,137 +1071,41 @@ static int require_mysql(const char *database, const char *tablename, va_list ap type != RQ_UINTEGER3 && type != RQ_INTEGER3 && type != RQ_UINTEGER4 && type != RQ_INTEGER4 && type != RQ_INTEGER8) { - PICK_WHICH_ALTER_ACTION(bigint) + ast_log(LOG_WARNING, "Realtime table %s@%s: column '%s' may not be large enough for " \ + "the required data length: %d (detected stringtype)\n", \ + tablename, database, column->name, size); \ + res = -1; \ } } } else if (strncmp(column->type, "float", 5) == 0) { if (!ast_rq_is_int(type) && type != RQ_FLOAT) { - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type); - res = -1; - } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { - table_altered = 1; - } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { - table_altered = 1; - } else { - res = -1; - } + ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type); + res = -1; } } else if (strncmp(column->type, "datetime", 8) == 0 || strncmp(column->type, "timestamp", 9) == 0) { if (type != RQ_DATETIME) { - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type); - res = -1; - } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { - table_altered = 1; - } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { - table_altered = 1; - } else { - res = -1; - } + ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type); + res = -1; } } else if (strncmp(column->type, "date", 4) == 0) { if (type != RQ_DATE) { - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type); - res = -1; - } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { - table_altered = 1; - } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { - table_altered = 1; - } else { - res = -1; - } - } - } else { /* Other, possibly unsupported types? */ - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name); + ast_log(LOG_WARNING, "Realtime table %s@%s: Column %s cannot be a %s\n", tablename, database, column->name, column->type); res = -1; - } else if (table->database->requirements == RQ_CREATECLOSE && modify_mysql(database, tablename, column, type, size) == 0) { - table_altered = 1; - } else if (table->database->requirements == RQ_CREATECHAR && modify_mysql(database, tablename, column, RQ_CHAR, size) == 0) { - table_altered = 1; - } else { } + } else { /* Other, possibly unsupported types? */ + ast_log(LOG_WARNING, "Possibly unsupported column type '%s' on column '%s'\n", column->type, column->name); + res = -1; } break; } } if (!column) { - if (table->database->requirements == RQ_WARN) { - ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size); - } else { - struct ast_str *sql = ast_str_thread_get(&modify_buf, 100), *fieldtype = ast_str_thread_get(&modify3_buf, 16); - - if (table->database->requirements == RQ_CREATECHAR || type == RQ_CHAR) { - ast_str_set(&fieldtype, 0, "CHAR(%d)", size); - } else if (type == RQ_UINTEGER1 || type == RQ_UINTEGER2 || type == RQ_UINTEGER3 || type == RQ_UINTEGER4 || type == RQ_UINTEGER8) { - if (type == RQ_UINTEGER1) { - ast_str_set(&fieldtype, 0, "TINYINT(3) UNSIGNED"); - } else if (type == RQ_UINTEGER2) { - ast_str_set(&fieldtype, 0, "SMALLINT(5) UNSIGNED"); - } else if (type == RQ_UINTEGER3) { - ast_str_set(&fieldtype, 0, "MEDIUMINT(8) UNSIGNED"); - } else if (type == RQ_UINTEGER4) { - ast_str_set(&fieldtype, 0, "INT(10) UNSIGNED"); - } else if (type == RQ_UINTEGER8) { - ast_str_set(&fieldtype, 0, "BIGINT(20) UNSIGNED"); - } else { - ast_log(LOG_WARNING, "Somebody should check this code for a rather large bug... it's about to squash Tokyo.\n"); - continue; - } - } else if (ast_rq_is_int(type)) { - if (type == RQ_INTEGER1) { - ast_str_set(&fieldtype, 0, "TINYINT(3)"); - } else if (type == RQ_INTEGER2) { - ast_str_set(&fieldtype, 0, "SMALLINT(5)"); - } else if (type == RQ_INTEGER3) { - ast_str_set(&fieldtype, 0, "MEDIUMINT(8)"); - } else if (type == RQ_INTEGER4) { - ast_str_set(&fieldtype, 0, "INT(10)"); - } else if (type == RQ_INTEGER8) { - ast_str_set(&fieldtype, 0, "BIGINT(20)"); - } else { - ast_log(LOG_WARNING, "Somebody should check this code for a rather large bug... it's about to eat Cincinnati.\n"); - continue; - } - } else if (type == RQ_FLOAT) { - ast_str_set(&fieldtype, 0, "FLOAT"); - } else if (type == RQ_DATE) { - ast_str_set(&fieldtype, 0, "DATE"); - } else if (type == RQ_DATETIME) { - ast_str_set(&fieldtype, 0, "DATETIME"); - } else { - continue; - } - ast_str_set(&sql, 0, "ALTER TABLE %s ADD COLUMN %s %s", tablename, elm, ast_str_buffer(fieldtype)); - - ast_mutex_lock(&table->database->lock); - if (!mysql_reconnect(table->database)) { - ast_mutex_unlock(&table->database->lock); - ast_log(LOG_ERROR, "Unable to add column: %s\n", ast_str_buffer(sql)); - continue; - } - - /* Execution. */ - if (mysql_real_query(&table->database->handle, ast_str_buffer(sql), ast_str_strlen(sql))) { - ast_log(LOG_WARNING, "MySQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_debug(1, "MySQL RealTime: Query: %s\n", ast_str_buffer(sql)); - ast_debug(1, "MySQL RealTime: Query Failed because: %s\n", mysql_error(&table->database->handle)); - } else { - table_altered = 1; - } - } + ast_log(LOG_WARNING, "Table %s requires a column '%s' of size '%d', but no such column exists.\n", tablename, elm, size); } } release_table(table); - /* If we altered the table, we must refresh the cache */ - if (table_altered) { - unload_mysql(database, tablename); - release_table(find_table(database, tablename)); - } return res; } diff --git a/apps/app_fax.c b/apps/app_fax.c index 88aa6ad1a..e2a7c2a4c 100644 --- a/apps/app_fax.c +++ b/apps/app_fax.c @@ -262,13 +262,13 @@ static void phase_e_handler(t30_state_t *f, void *user_data, int result) } ast_json_ref(json_filenames); json_object = ast_json_pack("{s: s, s: s, s: s, s: i, s: i, s: i, s: o}", - "type", s->direction ? "send" : "receive", - "remote_station_id", far_ident, - "local_station_id", local_ident, - "fax_pages", pages_transferred, - "fax_resolution", stat.y_resolution, - "fax_bitrate", stat.bit_rate, - "filenames", json_filenames); + "type", s->direction ? "send" : "receive", + "remote_station_id", AST_JSON_UTF8_VALIDATE(far_ident), + "local_station_id", AST_JSON_UTF8_VALIDATE(local_ident), + "fax_pages", pages_transferred, + "fax_resolution", stat.y_resolution, + "fax_bitrate", stat.bit_rate, + "filenames", json_filenames); message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(s->chan), ast_channel_fax_type(), json_object); if (!message) { return; diff --git a/apps/app_minivm.c b/apps/app_minivm.c index fb7c22aa4..789a48aab 100644 --- a/apps/app_minivm.c +++ b/apps/app_minivm.c @@ -1853,10 +1853,10 @@ static int notify_new_message(struct ast_channel *chan, const char *templatename } mwi_state->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan)); - json_object = ast_json_pack("{s: s, s: s}", - "Event", "MiniVoiceMail" - "Action", "SentNotification", - "Counter", counter); + json_object = ast_json_pack("{s: s, s: s, s: s}", + "Event", "MiniVoiceMail", + "Action", "SentNotification", + "Counter", counter ?: ""); if (!json_object) { goto notify_cleanup; } diff --git a/apps/app_queue.c b/apps/app_queue.c index 45b5683ed..0cc3b2685 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -5616,12 +5616,12 @@ static void send_agent_complete(const char *queuename, struct ast_channel_snapsh } blob = ast_json_pack("{s: s, s: s, s: s, s: i, s: i, s: s}", - "Queue", queuename, - "Interface", member->interface, - "MemberName", member->membername, - "HoldTime", (long)(callstart - holdstart), - "TalkTime", (long)(time(NULL) - callstart), - "Reason", reason); + "Queue", queuename, + "Interface", member->interface, + "MemberName", member->membername, + "HoldTime", (long)(callstart - holdstart), + "TalkTime", (long)(time(NULL) - callstart), + "Reason", reason ?: ""); queue_publish_multi_channel_snapshot_blob(ast_queue_topic(queuename), caller, peer, queue_agent_complete_type(), blob); @@ -7199,12 +7199,10 @@ static void set_queue_member_pause(struct call_queue *q, struct member *mem, con } mem->paused = paused; - if (paused) { - if (!ast_strlen_zero(reason)) { - ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused)); - } + if (paused && !ast_strlen_zero(reason)) { + ast_copy_string(mem->reason_paused, reason, sizeof(mem->reason_paused)); } else { - ast_copy_string(mem->reason_paused, "", sizeof(mem->reason_paused)); + mem->reason_paused[0] = '\0'; } ast_devstate_changed(mem->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE, diff --git a/include/asterisk/json.h b/include/asterisk/json.h index 28ebfbd51..cfd9a2997 100644 --- a/include/asterisk/json.h +++ b/include/asterisk/json.h @@ -217,6 +217,41 @@ const char *ast_json_typename(enum ast_json_type type); /*!@{*/ /*! + * \brief Check the string of the given length for UTF-8 format. + * \since 13.12.0 + * + * \param str String to check. + * \param len Length of string to check. + * + * \retval 0 if not UTF-8 encoded or str is NULL. + * \retval 1 if UTF-8 encoded. + */ +int ast_json_utf8_check_len(const char *str, size_t len); + +/*! + * \brief Check the nul terminated string for UTF-8 format. + * \since 13.12.0 + * + * \param str String to check. + * + * \retval 0 if not UTF-8 encoded or str is NULL. + * \retval 1 if UTF-8 encoded. + */ +int ast_json_utf8_check(const char *str); + +/*! + * \brief Check str for UTF-8 and replace with an empty string if fails the check. + * + * \note The convenience macro is normally used with ast_json_pack() + * or a function wrapper that calls ast_json_vpack(). + */ +#define AST_JSON_UTF8_VALIDATE(str) (ast_json_utf8_check(str) ? (str) : "") + +/*!@}*/ + +/*!@{*/ + +/*! * \brief Get the JSON true value. * \since 12.0.0 * diff --git a/main/aoc.c b/main/aoc.c index 4ab931536..ba151d760 100644 --- a/main/aoc.c +++ b/main/aoc.c @@ -1656,8 +1656,10 @@ static struct ast_json *units_to_json(const struct ast_aoc_decoded *decoded) static struct ast_json *currency_to_json(const char *name, int cost, enum ast_aoc_currency_multiplier mult) { - return ast_json_pack("{s:s, s:i, s:s}", "Name", name, - "Cost", cost, "Multiplier", aoc_multiplier_str(mult)); + return ast_json_pack("{s:s, s:i, s:s}", + "Name", AST_JSON_UTF8_VALIDATE(name), + "Cost", cost, + "Multiplier", aoc_multiplier_str(mult)); } static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded) @@ -1667,8 +1669,8 @@ static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded) if (decoded->charge_type != AST_AOC_CHARGE_CURRENCY && decoded->charge_type != AST_AOC_CHARGE_UNIT) { - return ast_json_pack("{s:s}", "Type", - aoc_charge_type_str(decoded->charge_type)); + return ast_json_pack("{s:s}", + "Type", aoc_charge_type_str(decoded->charge_type)); } if (decoded->charge_type == AST_AOC_CHARGE_CURRENCY) { @@ -1680,8 +1682,7 @@ static struct ast_json *charge_to_json(const struct ast_aoc_decoded *decoded) obj = units_to_json(decoded); } - return ast_json_pack( - "{s:s, s:s, s:s, s:o}", + return ast_json_pack("{s:s, s:s, s:s, s:o}", "Type", aoc_charge_type_str(decoded->charge_type), "BillingID", aoc_billingid_str(decoded->billing_id), "TotalType", aoc_type_of_totaling_str(decoded->total_type), @@ -1692,13 +1693,11 @@ static struct ast_json *association_to_json(const struct ast_aoc_decoded *decode { switch (decoded->charging_association.charging_type) { case AST_AOC_CHARGING_ASSOCIATION_NUMBER: - return ast_json_pack( - "{s:s, s:i}", - "Number", decoded->charging_association.charge.number.number, + return ast_json_pack("{s:s, s:i}", + "Number", AST_JSON_UTF8_VALIDATE(decoded->charging_association.charge.number.number), "Plan", decoded->charging_association.charge.number.plan); case AST_AOC_CHARGING_ASSOCIATION_ID: - return ast_json_pack( - "{s:i}", "ID", decoded->charging_association.charge.id); + return ast_json_pack("{s:i}", "ID", decoded->charging_association.charge.id); case AST_AOC_CHARGING_ASSOCIATION_NA: default: return ast_json_null(); @@ -1740,22 +1739,22 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded) decoded->aoc_s_entries[i].rate.duration.amount, decoded->aoc_s_entries[i].rate.duration.multiplier); - time = ast_json_pack( - "{s:i, s:s}", + time = ast_json_pack("{s:i, s:i}", "Length", decoded->aoc_s_entries[i].rate.duration.time, "Scale", decoded->aoc_s_entries[i].rate.duration.time_scale); if (decoded->aoc_s_entries[i].rate.duration.granularity_time) { - granularity = ast_json_pack( - "{s:i, s:s}", + granularity = ast_json_pack("{s:i, s:i}", "Length", decoded->aoc_s_entries[i].rate.duration.granularity_time, "Scale", decoded->aoc_s_entries[i].rate.duration.granularity_time_scale); } - type = ast_json_pack("{s:o, s:s, s:o, s:o}", "Currency", ast_json_ref(currency), "ChargingType", - decoded->aoc_s_entries[i].rate.duration.charging_type ? - "StepFunction" : "ContinuousCharging", "Time", ast_json_ref(time), - "Granularity", granularity ? ast_json_ref(granularity) : ast_json_ref(ast_json_null())); + type = ast_json_pack("{s:o, s:s, s:o, s:o}", + "Currency", ast_json_ref(currency), + "ChargingType", decoded->aoc_s_entries[i].rate.duration.charging_type + ? "StepFunction" : "ContinuousCharging", + "Time", ast_json_ref(time), + "Granularity", granularity ? ast_json_ref(granularity) : ast_json_null()); break; } @@ -1773,21 +1772,22 @@ static struct ast_json *s_to_json(const struct ast_aoc_decoded *decoded) decoded->aoc_s_entries[i].rate.volume.amount, decoded->aoc_s_entries[i].rate.volume.multiplier); - type = ast_json_pack( - "{s:s, s:o}", "Unit", aoc_volume_unit_str( + type = ast_json_pack("{s:s, s:o}", + "Unit", aoc_volume_unit_str( decoded->aoc_s_entries[i].rate.volume.volume_unit), "Currency", ast_json_ref(currency)); break; case AST_AOC_RATE_TYPE_SPECIAL_CODE: - type = ast_json_pack("{s:i}", "SpecialCode", - decoded->aoc_s_entries[i].rate.special_code); + type = ast_json_pack("{s:i}", + "SpecialCode", decoded->aoc_s_entries[i].rate.special_code); break; default: break; } - rate = ast_json_pack("{s:s, s:o}", "Chargeable", charge_item, - aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), ast_json_ref(type)); + rate = ast_json_pack("{s:s, s:o}", + "Chargeable", charge_item, + aoc_rate_type_str(decoded->aoc_s_entries[i].rate_type), ast_json_ref(type)); if (ast_json_array_append(rates, rate)) { break; } diff --git a/main/cel.c b/main/cel.c index 4abaac7c8..0cdf1be00 100644 --- a/main/cel.c +++ b/main/cel.c @@ -1237,10 +1237,10 @@ static void cel_parking_cb( if (parked_payload->retriever) { extra = ast_json_pack("{s: s, s: s}", - "reason", reason, + "reason", reason ?: "", "retriever", parked_payload->retriever->name); } else { - extra = ast_json_pack("{s: s}", "reason", reason); + extra = ast_json_pack("{s: s}", "reason", reason ?: ""); } if (extra) { diff --git a/main/json.c b/main/json.c index 35e6f16ce..9f42f0adb 100644 --- a/main/json.c +++ b/main/json.c @@ -269,6 +269,127 @@ const char *ast_json_typename(enum ast_json_type type) return "?"; } +/* Ported from libjansson utf.c:utf8_check_first() */ +static size_t json_utf8_check_first(char byte) +{ + unsigned char ch = (unsigned char) byte; + + if (ch < 0x80) { + return 1; + } + + if (0x80 <= ch && ch <= 0xBF) { + /* second, third or fourth byte of a multi-byte + sequence, i.e. a "continuation byte" */ + return 0; + } else if (ch == 0xC0 || ch == 0xC1) { + /* overlong encoding of an ASCII byte */ + return 0; + } else if (0xC2 <= ch && ch <= 0xDF) { + /* 2-byte sequence */ + return 2; + } else if (0xE0 <= ch && ch <= 0xEF) { + /* 3-byte sequence */ + return 3; + } else if (0xF0 <= ch && ch <= 0xF4) { + /* 4-byte sequence */ + return 4; + } else { /* ch >= 0xF5 */ + /* Restricted (start of 4-, 5- or 6-byte sequence) or invalid + UTF-8 */ + return 0; + } +} + +/* Ported from libjansson utf.c:utf8_check_full() */ +static size_t json_utf8_check_full(const char *str, size_t len) +{ + size_t pos; + int32_t value; + unsigned char ch = (unsigned char) str[0]; + + if (len == 2) { + value = ch & 0x1F; + } else if (len == 3) { + value = ch & 0xF; + } else if (len == 4) { + value = ch & 0x7; + } else { + return 0; + } + + for (pos = 1; pos < len; ++pos) { + ch = (unsigned char) str[pos]; + if (ch < 0x80 || ch > 0xBF) { + /* not a continuation byte */ + return 0; + } + + value = (value << 6) + (ch & 0x3F); + } + + if (value > 0x10FFFF) { + /* not in Unicode range */ + return 0; + } else if (0xD800 <= value && value <= 0xDFFF) { + /* invalid code point (UTF-16 surrogate halves) */ + return 0; + } else if ((len == 2 && value < 0x80) + || (len == 3 && value < 0x800) + || (len == 4 && value < 0x10000)) { + /* overlong encoding */ + return 0; + } + + return 1; +} + +int ast_json_utf8_check_len(const char *str, size_t len) +{ + size_t pos; + size_t count; + int res = 1; + + if (!str) { + return 0; + } + + /* + * Since the json library does not make the check function + * public we recreate/copy the function in our interface + * module. + * + * Loop ported from libjansson utf.c:utf8_check_string() + */ + for (pos = 0; pos < len; pos += count) { + count = json_utf8_check_first(str[pos]); + if (count == 0) { + res = 0; + break; + } else if (count > 1) { + if (count > len - pos) { + /* UTF-8 needs more than we have left in the string. */ + res = 0; + break; + } + + if (!json_utf8_check_full(&str[pos], count)) { + res = 0; + break; + } + } + } + + if (!res) { + ast_debug(1, "String '%.*s' is not UTF-8 for json conversion\n", (int) len, str); + } + return res; +} + +int ast_json_utf8_check(const char *str) +{ + return str ? ast_json_utf8_check_len(str, strlen(str)) : 0; +} struct ast_json *ast_json_true(void) { @@ -721,16 +842,16 @@ struct ast_json *ast_json_deep_copy(const struct ast_json *value) struct ast_json *ast_json_name_number(const char *name, const char *number) { return ast_json_pack("{s: s, s: s}", - "name", name, - "number", number); + "name", AST_JSON_UTF8_VALIDATE(name), + "number", AST_JSON_UTF8_VALIDATE(number)); } struct ast_json *ast_json_dialplan_cep(const char *context, const char *exten, int priority) { return ast_json_pack("{s: o, s: o, s: o}", - "context", context ? ast_json_string_create(context) : ast_json_null(), - "exten", exten ? ast_json_string_create(exten) : ast_json_null(), - "priority", priority != -1 ? ast_json_integer_create(priority) : ast_json_null()); + "context", context ? ast_json_string_create(context) : ast_json_null(), + "exten", exten ? ast_json_string_create(exten) : ast_json_null(), + "priority", priority != -1 ? ast_json_integer_create(priority) : ast_json_null()); } struct ast_json *ast_json_timeval(const struct timeval tv, const char *zone) @@ -821,7 +942,7 @@ static struct ast_json *json_party_number(struct ast_party_number *number) return NULL; } return ast_json_pack("{s: s, s: i, s: i, s: s}", - "number", number->str, + "number", AST_JSON_UTF8_VALIDATE(number->str), "plan", number->plan, "presentation", number->presentation, "presentation_txt", ast_describe_caller_presentation(number->presentation)); @@ -833,7 +954,7 @@ static struct ast_json *json_party_name(struct ast_party_name *name) return NULL; } return ast_json_pack("{s: s, s: s, s: i, s: s}", - "name", name->str, + "name", AST_JSON_UTF8_VALIDATE(name->str), "character_set", ast_party_name_charset_describe(name->char_set), "presentation", name->presentation, "presentation_txt", ast_describe_caller_presentation(name->presentation)); @@ -845,7 +966,7 @@ static struct ast_json *json_party_subaddress(struct ast_party_subaddress *subad return NULL; } return ast_json_pack("{s: s, s: i, s: b}", - "subaddress", subaddress->str, + "subaddress", AST_JSON_UTF8_VALIDATE(subaddress->str), "type", subaddress->type, "odd", subaddress->odd_even_indicator); } @@ -865,17 +986,20 @@ struct ast_json *ast_json_party_id(struct ast_party_id *party) } /* Party number */ - if (party->number.valid && ast_json_object_set(json_party_id, "number", json_party_number(&party->number))) { + if (party->number.valid + && ast_json_object_set(json_party_id, "number", json_party_number(&party->number))) { return NULL; } /* Party name */ - if (party->name.valid && ast_json_object_set(json_party_id, "name", json_party_name(&party->name))) { + if (party->name.valid + && ast_json_object_set(json_party_id, "name", json_party_name(&party->name))) { return NULL; } /* Party subaddress */ - if (party->subaddress.valid && ast_json_object_set(json_party_id, "subaddress", json_party_subaddress(&party->subaddress))) { + if (party->subaddress.valid + && ast_json_object_set(json_party_id, "subaddress", json_party_subaddress(&party->subaddress))) { return NULL; } diff --git a/res/res_fax.c b/res/res_fax.c index ab0945a89..666c2d997 100644 --- a/res/res_fax.c +++ b/res/res_fax.c @@ -1415,11 +1415,13 @@ static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_de } json_object = ast_json_pack("{s: s, s: s, s: s, s: s, s: o}", - "type", "status", - "operation", (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send", - "status", status, - "local_station_id", details->localstationid, - "filenames", json_filenames); + "type", "status", + "operation", (details->caps & AST_FAX_TECH_GATEWAY) + ? "gateway" + : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send", + "status", status, + "local_station_id", AST_JSON_UTF8_VALIDATE(details->localstationid), + "filenames", json_filenames); if (!json_object) { return -1; } diff --git a/res/stasis/app.c b/res/stasis/app.c index 4e18aa5ae..957ed7f69 100644 --- a/res/stasis/app.c +++ b/res/stasis/app.c @@ -456,7 +456,7 @@ static struct ast_json *channel_dialplan( "type", "ChannelDialplan", "timestamp", ast_json_timeval(*tv, NULL), "dialplan_app", new_snapshot->appl, - "dialplan_app_data", new_snapshot->data, + "dialplan_app_data", AST_JSON_UTF8_VALIDATE(new_snapshot->data), "channel", json_channel); } diff --git a/tests/test_json.c b/tests/test_json.c index 9d624cdc6..206bd8a39 100644 --- a/tests/test_json.c +++ b/tests/test_json.c @@ -1597,11 +1597,26 @@ AST_TEST_DEFINE(json_test_clever_circle) return AST_TEST_PASS; } -AST_TEST_DEFINE(json_test_name_number) +static int test_name_number(const char *name, const char *number) { - RAII_VAR(struct ast_json *, uut, NULL, ast_json_unref); - RAII_VAR(struct ast_json *, expected, NULL, ast_json_unref); + int res; + struct ast_json *uut; + struct ast_json *expected; + + expected = ast_json_pack("{s: s, s: s}", + "name", name ?: "", + "number", number ?: ""); + uut = ast_json_name_number(name, number); + + res = ast_json_equal(expected, uut); + ast_json_unref(expected); + ast_json_unref(uut); + return res; +} + +AST_TEST_DEFINE(json_test_name_number) +{ switch (cmd) { case TEST_INIT: info->name = "name_number"; @@ -1613,15 +1628,10 @@ AST_TEST_DEFINE(json_test_name_number) break; } - ast_test_validate(test, NULL == ast_json_name_number("name", NULL)); - ast_test_validate(test, NULL == ast_json_name_number(NULL, "1234")); - ast_test_validate(test, NULL == ast_json_name_number(NULL, NULL)); - - expected = ast_json_pack("{s: s, s: s}", - "name", "Jenny", - "number", "867-5309"); - uut = ast_json_name_number("Jenny", "867-5309"); - ast_test_validate(test, ast_json_equal(expected, uut)); + ast_test_validate(test, test_name_number("name", NULL)); + ast_test_validate(test, test_name_number(NULL, "1234")); + ast_test_validate(test, test_name_number(NULL, NULL)); + ast_test_validate(test, test_name_number("Jenny", "867-5309")); return AST_TEST_PASS; } |