diff options
-rw-r--r-- | addons/res_config_mysql.c | 54 | ||||
-rw-r--r-- | apps/app_voicemail.c | 12 | ||||
-rw-r--r-- | main/Makefile | 8 | ||||
-rw-r--r-- | main/tcptls.c | 20 | ||||
-rw-r--r-- | res/res_config_pgsql.c | 50 | ||||
-rw-r--r-- | res/res_config_sqlite3.c | 219 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_cli.c | 2 |
7 files changed, 296 insertions, 69 deletions
diff --git a/addons/res_config_mysql.c b/addons/res_config_mysql.c index 1041c1120..b9ca81f36 100644 --- a/addons/res_config_mysql.c +++ b/addons/res_config_mysql.c @@ -305,6 +305,11 @@ static char *decode_chunk(char *chunk) return orig; } +#define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE")) + +/* MySQL requires us to escape the escape... yo dawg */ +static char *ESCAPE_CLAUSE = " ESCAPE '\\\\'"; + static struct ast_variable *realtime_mysql(const char *database, const char *table, const struct ast_variable *rt_fields) { struct mysql_conn *dbh; @@ -317,6 +322,7 @@ static struct ast_variable *realtime_mysql(const char *database, const char *tab char *stringp; char *chunk; char *op; + char *escape = ""; const struct ast_variable *field = rt_fields; struct ast_variable *var=NULL, *prev=NULL; @@ -347,20 +353,29 @@ static struct ast_variable *realtime_mysql(const char *database, const char *tab /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - if (!strchr(field->name, ' ')) - op = " ="; - else + if (!strchr(field->name, ' ')) { + op = " ="; + } else { op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(buf, field->value); - ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, field->name, op, ast_str_buffer(buf)); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(buf), escape); while ((field = field->next)) { - if (!strchr(field->name, ' ')) - op = " ="; - else + escape = ""; + if (!strchr(field->name, ' ')) { + op = " ="; + } else { op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(buf, field->value); - ast_str_append(&sql, 0, " AND %s%s '%s'", field->name, op, ast_str_buffer(buf)); + ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(buf), escape); } ast_debug(1, "MySQL RealTime: Retrieve SQL: %s\n", ast_str_buffer(sql)); @@ -418,6 +433,7 @@ static struct ast_config *realtime_multi_mysql(const char *database, const char char *stringp; char *chunk; char *op; + char *escape = ""; const struct ast_variable *field = rt_fields; struct ast_variable *var = NULL; struct ast_config *cfg = NULL; @@ -464,17 +480,29 @@ static struct ast_config *realtime_multi_mysql(const char *database, const char /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - if (!strchr(field->name, ' ')) + if (!strchr(field->name, ' ')) { op = " ="; - else + } else { op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(buf, field->value); - ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, field->name, op, ast_str_buffer(buf)); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(buf), escape); while ((field = field->next)) { - if (!strchr(field->name, ' ')) op = " ="; else op = ""; + escape = ""; + if (!strchr(field->name, ' ')) { + op = " ="; + } else { + op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(buf, field->value); - ast_str_append(&sql, 0, " AND %s%s '%s'", field->name, op, ast_str_buffer(buf)); + ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(buf), escape); } if (initfield) { diff --git a/apps/app_voicemail.c b/apps/app_voicemail.c index ef210c2ef..608977952 100644 --- a/apps/app_voicemail.c +++ b/apps/app_voicemail.c @@ -11113,7 +11113,7 @@ static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_ return -1; } if (vmu && !skipuser) { - memcpy(res_vmu, vmu, sizeof(struct ast_vm_user)); + *res_vmu = *vmu; } return 0; } @@ -11275,8 +11275,8 @@ static int vm_execmain(struct ast_channel *chan, const char *data) int box; int useadsi = 0; int skipuser = 0; - struct vm_state vms; - struct ast_vm_user *vmu = NULL, vmus; + struct vm_state vms = {{0}}; + struct ast_vm_user *vmu = NULL, vmus = {{0}}; char *context = NULL; int silentexit = 0; struct ast_flags flags = { 0 }; @@ -11289,12 +11289,8 @@ static int vm_execmain(struct ast_channel *chan, const char *data) #endif /* Add the vm_state to the active list and keep it active */ - memset(&vms, 0, sizeof(vms)); - vms.lastmsg = -1; - memset(&vmus, 0, sizeof(vmus)); - ast_test_suite_event_notify("START", "Message: vm_execmain started"); if (ast_channel_state(chan) != AST_STATE_UP) { ast_debug(1, "Before ast_answer\n"); @@ -12587,7 +12583,7 @@ static struct ast_custom_function vm_info_acf = { static int vmauthenticate(struct ast_channel *chan, const char *data) { char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = ""; - struct ast_vm_user vmus; + struct ast_vm_user vmus = {{0}}; char *options = NULL; int silent = 0, skipuser = 0; int res = -1; diff --git a/main/Makefile b/main/Makefile index da38a3e3e..b3e84e16c 100644 --- a/main/Makefile +++ b/main/Makefile @@ -355,8 +355,12 @@ else # Darwin endif endif ifneq ($(LDCONFIG),) +ifneq ($(DESTDIR),) + $(LDCONFIG) $(LDCONFIG_FLAGS) "$(DESTDIR)$(ASTLIBDIR)/" +else $(LDCONFIG) endif +endif $(LN) -sf asterisk "$(DESTDIR)$(ASTSBINDIR)/rasterisk" binuninstall: @@ -373,8 +377,12 @@ ifneq ($(ASTPJ_LIB).$(ASTPJ_SO_VERSION),.) rm -f "$(DESTDIR)$(ASTLIBDIR)/$(ASTPJ_LIB)" endif ifneq ($(LDCONFIG),) +ifneq ($(DESTDIR),) + $(LDCONFIG) $(LDCONFIG_FLAGS) "$(DESTDIR)$(ASTLIBDIR)/" +else $(LDCONFIG) endif +endif clean:: rm -f asterisk libasteriskssl.o diff --git a/main/tcptls.c b/main/tcptls.c index 4d110cda9..c46b8df43 100644 --- a/main/tcptls.c +++ b/main/tcptls.c @@ -773,14 +773,16 @@ void *ast_tcptls_server_root(void *data) } tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); if (!tcptls_session) { - ast_log(LOG_WARNING, "No memory for new session: %s\n", strerror(errno)); - if (close(fd)) { - ast_log(LOG_ERROR, "close() failed: %s\n", strerror(errno)); - } + close(fd); continue; } tcptls_session->overflow_buf = ast_str_create(128); + if (!tcptls_session->overflow_buf) { + ao2_ref(tcptls_session, -1); + close(fd); + continue; + } flags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); tcptls_session->fd = fd; @@ -1059,11 +1061,15 @@ struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_s } } - if (!(tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor))) { + tcptls_session = ao2_alloc(sizeof(*tcptls_session), session_instance_destructor); + if (!tcptls_session) { goto error; } tcptls_session->overflow_buf = ast_str_create(128); + if (!tcptls_session->overflow_buf) { + goto error; + } tcptls_session->client = 1; tcptls_session->fd = desc->accept_fd; tcptls_session->parent = desc; @@ -1078,9 +1084,7 @@ struct ast_tcptls_session_instance *ast_tcptls_client_create(struct ast_tcptls_s error: close(desc->accept_fd); desc->accept_fd = -1; - if (tcptls_session) { - ao2_ref(tcptls_session, -1); - } + ao2_cleanup(tcptls_session); return NULL; } diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c index 52c8ede74..58c34d082 100644 --- a/res/res_config_pgsql.c +++ b/res/res_config_pgsql.c @@ -417,6 +417,9 @@ static struct columns *find_column(struct tables *t, const char *colname) return NULL; } +#define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE")) +static char *ESCAPE_CLAUSE = " ESCAPE '\\'"; + static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, const struct ast_variable *fields) { RAII_VAR(PGresult *, result, NULL, PQclear); @@ -426,6 +429,7 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab char *stringp; char *chunk; char *op; + char *escape = ""; const struct ast_variable *field = fields; struct ast_variable *var = NULL, *prev = NULL; @@ -453,7 +457,14 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - op = strchr(field->name, ' ') ? "" : " ="; + if (!strchr(field->name, ' ')) { + op = " ="; + } else { + op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(escapebuf, field->value); if (pgresult) { @@ -461,12 +472,17 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab return NULL; } - ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, field->name, op, ast_str_buffer(escapebuf)); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", tablename, field->name, op, ast_str_buffer(escapebuf), escape); while ((field = field->next)) { - if (!strchr(field->name, ' ')) + escape = ""; + if (!strchr(field->name, ' ')) { op = " ="; - else + } else { op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(escapebuf, field->value); if (pgresult) { @@ -474,7 +490,7 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab return NULL; } - ast_str_append(&sql, 0, " AND %s%s '%s'", field->name, op, ast_str_buffer(escapebuf)); + ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape); } /* We now have our complete statement; Lets connect to the server and execute it. */ @@ -540,6 +556,7 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char char *stringp; char *chunk; char *op; + char *escape = ""; struct ast_variable *var = NULL; struct ast_config *cfg = NULL; struct ast_category *cat = NULL; @@ -578,10 +595,15 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char /* Create the first part of the query using the first parameter/value pairs we just extracted If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ - if (!strchr(field->name, ' ')) + if (!strchr(field->name, ' ')) { op = " ="; - else + escape = ""; + } else { op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(escapebuf, field->value); if (pgresult) { @@ -590,12 +612,18 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char return NULL; } - ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, field->name, op, ast_str_buffer(escapebuf)); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'%s", table, field->name, op, ast_str_buffer(escapebuf), escape); while ((field = field->next)) { - if (!strchr(field->name, ' ')) + escape = ""; + if (!strchr(field->name, ' ')) { op = " ="; - else + escape = ""; + } else { op = ""; + if (IS_SQL_LIKE_CLAUSE(field->name)) { + escape = ESCAPE_CLAUSE; + } + } ESCAPE_STRING(escapebuf, field->value); if (pgresult) { @@ -604,7 +632,7 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char return NULL; } - ast_str_append(&sql, 0, " AND %s%s '%s'", field->name, op, ast_str_buffer(escapebuf)); + ast_str_append(&sql, 0, " AND %s%s '%s'%s", field->name, op, ast_str_buffer(escapebuf), escape); } if (initfield) { diff --git a/res/res_config_sqlite3.c b/res/res_config_sqlite3.c index 39d4f08ad..b9a106215 100644 --- a/res/res_config_sqlite3.c +++ b/res/res_config_sqlite3.c @@ -60,6 +60,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") /*** DOCUMENTATION ***/ +static int has_explicit_like_escaping; + static struct ast_config *realtime_sqlite3_load(const char *database, const char *table, const char *configfile, struct ast_config *config, struct ast_flags flags, const char *suggested_include_file, const char *who_asked); static struct ast_variable *realtime_sqlite3(const char *database, const char *table, const struct ast_variable *fields); static struct ast_config *realtime_sqlite3_multi(const char *database, const char *table, const struct ast_variable *fields); @@ -115,7 +117,13 @@ AST_THREADSTORAGE(escape_table_buf); AST_THREADSTORAGE(escape_column_buf); AST_THREADSTORAGE(escape_value_buf); -static int realtime_sqlite3_execute_handle(struct realtime_sqlite3_db *db, const char *sql, int (*callback)(void*, int, char **, char **), void *arg, int sync); +typedef int (*callback_t)(void*, int, char **, char **); + +static int realtime_sqlite3_exec_query_with_handle(struct realtime_sqlite3_db *, const char *, callback_t, void *); +static int realtime_sqlite3_exec_query(const char *, const char *, callback_t, void *); +static int realtime_sqlite3_exec_update_with_handle(struct realtime_sqlite3_db *, const char *); +static int realtime_sqlite3_exec_update(const char *, const char *); + void db_start_batch(struct realtime_sqlite3_db *db); void db_stop_batch(struct realtime_sqlite3_db *db); @@ -303,20 +311,20 @@ static void *db_sync_thread(void *data) { struct realtime_sqlite3_db *db = data; ao2_lock(db); - realtime_sqlite3_execute_handle(db, "BEGIN TRANSACTION", NULL, NULL, 0); + realtime_sqlite3_exec_query_with_handle(db, "BEGIN TRANSACTION", NULL, NULL); for (;;) { if (!db->wakeup) { ast_cond_wait(&db->cond, ao2_object_get_lockaddr(db)); } db->wakeup = 0; - if (realtime_sqlite3_execute_handle(db, "COMMIT", NULL, NULL, 0) < 0) { - realtime_sqlite3_execute_handle(db, "ROLLBACK", NULL, NULL, 0); + if (realtime_sqlite3_exec_query_with_handle(db, "COMMIT", NULL, NULL) < 0) { + realtime_sqlite3_exec_query_with_handle(db, "ROLLBACK", NULL, NULL); } if (db->exiting) { ao2_unlock(db); break; } - realtime_sqlite3_execute_handle(db, "BEGIN TRANSACTION", NULL, NULL, 0); + realtime_sqlite3_exec_query_with_handle(db, "BEGIN TRANSACTION", NULL, NULL); ao2_unlock(db); usleep(1000 * db->batch); ao2_lock(db); @@ -527,18 +535,125 @@ struct cfg_entry_args { const char *who_asked; }; -/*! Exeute an SQL statement given the database object +/*! + * Structure passed to row counting SQLite callback. + */ +struct row_counter_args { + callback_t wrapped_callback; + void *wrapped_arg; + int row_count; +}; + +/*! + * \internal + * \brief SQLite3 callback that counts rows of a result set. + * + * \details + * This is used to decorate existing callbacks so that we can count the number + * of rows returned from a SELECT statement and still process each row + * independently. + * + * \param data user data pointer passed in via sqlite3_exec() + * \param num_columns number of columns in the result + * \param values array of pointers to column values + * \param columns array of pointers of to column names + * + * \return the return value of the wrapped callback, or 0 if no wrapped callback + * is provided. + */ +static int row_counter_wrapper(void *arg, int num_columns, char **values, char **columns) +{ + struct row_counter_args *wrapped = arg; + wrapped->row_count++; + if (wrapped->wrapped_callback) { + return wrapped->wrapped_callback(wrapped->wrapped_arg, num_columns, values, columns); + } + return 0; +} + +/*! + * \internal + * \brief Execute a SQL SELECT statement using a database handle + * + * \param db the database handle to use for the query + * \param sql the SQL statement to execute + * \param callback a user defined callback that will be called for each row of + * the result set + * \param arg data to be passed to the user defined callback + * + * \return if successful, the number of rows returned from the provided SELECT + * statement. -1 on failure. + */ +static int realtime_sqlite3_exec_query_with_handle(struct realtime_sqlite3_db *db, const char *sql, callback_t callback, void *arg) +{ + int res = 0; + char *errmsg; + struct row_counter_args wrapper = { + .wrapped_callback = callback, + .wrapped_arg = arg, + .row_count = 0, + }; + + ao2_lock(db); + if (sqlite3_exec(db->handle, sql, row_counter_wrapper, &wrapper, &errmsg) != SQLITE_OK) { + ast_log(LOG_WARNING, "Could not execute '%s': %s\n", sql, errmsg); + sqlite3_free(errmsg); + res = -1; + } + ao2_unlock(db); + + return res == 0 ? wrapper.row_count : res; +} + +/*! + * \internal + * \brief Execute a SQL SELECT statement on the specified database + * + * \param database the name of the database to query + * \param sql the SQL statement to execute + * \param callback a user defined callback that will be called for each row of + * the result set + * \param arg data to be passed to the user defined callback + * + * \return if successful, the number of rows returned from the provided SELECT + * statement. -1 on failure. + */ +static int realtime_sqlite3_exec_query(const char *database, const char *sql, callback_t callback, void *arg) +{ + struct realtime_sqlite3_db *db; + int res; + + if (!(db = find_database(database))) { + ast_log(LOG_WARNING, "Could not find database: %s\n", database); + return -1; + } + + res = realtime_sqlite3_exec_query_with_handle(db, sql, callback, arg); + ao2_ref(db, -1); + + return res; +} + +/*! + * \internal + * \brief Execute a SQL INSERT/UPDATE/DELETE statement using a database handle + * + * \note A database sync operation is always performed after a statement + * is executed. + * + * \param db the database handle to use for the query + * \param sql the SQL statement to execute * - * \retval -1 ERROR - * \retval > -1 Number of rows changed + * \return if successful, the number of rows modified by the provided SQL + * statement. -1 on failure. */ -static int realtime_sqlite3_execute_handle(struct realtime_sqlite3_db *db, const char *sql, int (*callback)(void*, int, char **, char **), void *arg, int sync) +static int realtime_sqlite3_exec_update_with_handle(struct realtime_sqlite3_db *db, const char *sql) { int res = 0; char *errmsg; ao2_lock(db); - if (sqlite3_exec(db->handle, sql, callback, arg, &errmsg) != SQLITE_OK) { + if (sqlite3_exec(db->handle, sql, NULL, NULL, &errmsg) != SQLITE_OK) { ast_log(LOG_WARNING, "Could not execute '%s': %s\n", sql, errmsg); sqlite3_free(errmsg); res = -1; @@ -547,19 +662,25 @@ static int realtime_sqlite3_execute_handle(struct realtime_sqlite3_db *db, const } ao2_unlock(db); - if (sync) { - db_sync(db); - } + db_sync(db); return res; } -/*! Exeute an SQL statement give the database name +/*! + * \internal + * \brief Execute a SQL INSERT/UPDATE/DELETE statement using a database handle + * + * \note A database sync operation is always performed after a statement + * is executed. * - * \retval -1 ERROR - * \retval > -1 Number of rows changed + * \param database the name of the database to query + * \param sql the SQL statement to execute + * + * \return if successful, the number of rows modified by the provided SQL + * statement. -1 on failure. */ -static int realtime_sqlite3_execute(const char *database, const char *sql, int (*callback)(void*, int, char **, char **), void *arg, int sync) +static int realtime_sqlite3_exec_update(const char *database, const char *sql) { struct realtime_sqlite3_db *db; int res; @@ -569,7 +690,7 @@ static int realtime_sqlite3_execute(const char *database, const char *sql, int ( return -1; } - res = realtime_sqlite3_execute_handle(db, sql, callback, arg, sync); + res = realtime_sqlite3_exec_update_with_handle(db, sql); ao2_ref(db, -1); return res; @@ -653,13 +774,15 @@ static struct ast_config *realtime_sqlite3_load(const char *database, const char args.flags = flags; args.who_asked = who_asked; - realtime_sqlite3_execute(database, sql, static_realtime_cb, &args, 0); + realtime_sqlite3_exec_query(database, sql, static_realtime_cb, &args); sqlite3_free(sql); return config; } +#define IS_SQL_LIKE_CLAUSE(x) ((x) && ast_ends_with(x, " LIKE")) + /*! \brief Helper function for single and multi-row realtime load functions */ static int realtime_sqlite3_helper(const char *database, const char *table, const struct ast_variable *fields, int is_multi, void *arg) { @@ -685,13 +808,22 @@ static int realtime_sqlite3_helper(const char *database, const char *table, cons ast_str_append(&sql, 0, " AND %s %s", sqlite3_escape_column_op(field->name), sqlite3_escape_value(field->value)); } + + if (has_explicit_like_escaping && IS_SQL_LIKE_CLAUSE(field->name)) { + /* + * The realtime framework is going to pre-escape these + * for us with a backslash. We just need to make sure + * to tell SQLite about it + */ + ast_str_append(&sql, 0, " ESCAPE '\\'"); + } } if (!is_multi) { ast_str_append(&sql, 0, "%s", " LIMIT 1"); } - if (realtime_sqlite3_execute(database, ast_str_buffer(sql), is_multi ? append_row_to_cfg : row_to_varlist, arg, 0) < 0) { + if (realtime_sqlite3_exec_query(database, ast_str_buffer(sql), is_multi ? append_row_to_cfg : row_to_varlist, arg) < 0) { ast_free(sql); return -1; } @@ -762,7 +894,7 @@ static int realtime_sqlite3_update(const char *database, const char *table, cons ast_str_append(&sql, 0, " WHERE %s %s", sqlite3_escape_column_op(keyfield), sqlite3_escape_value(entity)); - res = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL, 1); + res = realtime_sqlite3_exec_update(database, ast_str_buffer(sql)); ast_free(sql); return res; @@ -813,7 +945,7 @@ static int realtime_sqlite3_update2(const char *database, const char *table, con ast_str_append(&sql, 0, "%s", ast_str_buffer(where_clause)); - res = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL, 1); + res = realtime_sqlite3_exec_update(database, ast_str_buffer(sql)); ast_free(sql); ast_free(where_clause); @@ -857,7 +989,7 @@ static int realtime_sqlite3_store(const char *database, const char *table, const ast_str_append(&sql, 0, "%s)", ast_str_buffer(values)); - res = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL, 1); + res = realtime_sqlite3_exec_update(database, ast_str_buffer(sql)); ast_free(sql); ast_free(values); @@ -893,7 +1025,7 @@ static int realtime_sqlite3_destroy(const char *database, const char *table, con } } - res = realtime_sqlite3_execute(database, ast_str_buffer(sql), NULL, NULL, 1); + res = realtime_sqlite3_exec_update(database, ast_str_buffer(sql)); ast_free(sql); @@ -946,7 +1078,9 @@ static int handle_missing_table(struct realtime_sqlite3_db *db, const char *tabl return -1; } - while ((column = va_arg(ap, typeof(column))) && (type = va_arg(ap, typeof(type))) && (sz = va_arg(ap, typeof(sz)))) { + while ((column = va_arg(ap, typeof(column)))) { + type = va_arg(ap, typeof(type)); + sz = va_arg(ap, typeof(sz)); if (first) { ast_str_set(&sql, 0, "CREATE TABLE IF NOT EXISTS %s (%s %s", sqlite3_escape_table(table), sqlite3_escape_column(column), get_sqlite_column_type(type)); @@ -958,7 +1092,7 @@ static int handle_missing_table(struct realtime_sqlite3_db *db, const char *tabl ast_str_append(&sql, 0, ")"); - res = realtime_sqlite3_execute_handle(db, ast_str_buffer(sql), NULL, NULL, 1) < 0 ? -1 : 0; + res = realtime_sqlite3_exec_update_with_handle(db, ast_str_buffer(sql)) < 0 ? -1 : 0; ast_free(sql); return res; @@ -983,7 +1117,7 @@ static int handle_missing_column(struct realtime_sqlite3_db *db, const char *tab return -1; } - if (!(res = (realtime_sqlite3_execute_handle(db, sql, NULL, NULL, 1) < 0 ? -1 : 0))) { + if (!(res = (realtime_sqlite3_exec_update_with_handle(db, sql) < 0 ? -1 : 0))) { ast_log(LOG_NOTICE, "Creating column '%s' type %s for table %s\n", column, sqltype, table); } @@ -1061,7 +1195,7 @@ static int realtime_sqlite3_require(const char *database, const char *table, va_ return -1; } - if ((res = realtime_sqlite3_execute_handle(db, sql, add_column_name, columns, 0)) < 0) { + if ((res = realtime_sqlite3_exec_query_with_handle(db, sql, add_column_name, columns)) < 0) { unref_db(&db); ao2_ref(columns, -1); sqlite3_free(sql); @@ -1077,8 +1211,10 @@ static int realtime_sqlite3_require(const char *database, const char *table, va_ sqlite3_free(sql); - while ((column = va_arg(ap, typeof(column))) && (type = va_arg(ap, typeof(type))) && (sz = va_arg(ap, typeof(sz)))) { + while ((column = va_arg(ap, typeof(column)))) { char *found; + type = va_arg(ap, typeof(type)); + sz = va_arg(ap, typeof(sz)); if (!(found = ao2_find(columns, column, OBJ_POINTER | OBJ_UNLINK))) { if (handle_missing_column(db, table, column, type, sz)) { unref_db(&db); @@ -1186,6 +1322,29 @@ static int unload_module(void) return 0; } +static void discover_sqlite3_caps(void) +{ + /* + * So we cheat a little bit here. SQLite3 added support for the + * 'ESCAPE' keyword in 3.1.0. They added SQLITE_VERSION_NUMBER + * in 3.1.2. So if we run into 3.1.0 or 3.1.1 in the wild, we + * just treat it like < 3.1.0. + * + * For reference: 3.1.0, 3.1.1, and 3.1.2 were all released + * within 30 days of each other in Jan/Feb 2005, so I don't + * imagine we'll be finding something pre-3.1.2 that often in + * practice. + */ +#if defined(SQLITE_VERSION_NUMBER) + has_explicit_like_escaping = 1; +#else + has_explicit_like_escaping = 0; +#endif + + ast_debug(3, "SQLite3 has 'LIKE ... ESCAPE ...' support? %s\n", + has_explicit_like_escaping ? "Yes" : "No"); +} + /*! * \brief Load the module * @@ -1198,6 +1357,8 @@ static int unload_module(void) */ static int load_module(void) { + discover_sqlite3_caps(); + if (!((databases = ao2_container_alloc(DB_BUCKETS, db_hash_fn, db_cmp_fn)))) { return AST_MODULE_LOAD_FAILURE; } diff --git a/res/res_pjsip/pjsip_cli.c b/res/res_pjsip/pjsip_cli.c index e6433f435..56ec191ed 100644 --- a/res/res_pjsip/pjsip_cli.c +++ b/res/res_pjsip/pjsip_cli.c @@ -221,6 +221,8 @@ char *ast_sip_cli_traverse_objects(struct ast_cli_entry *e, int cmd, struct ast_ return CLI_SUCCESS; } ao2_callback(container, OBJ_NODATA, formatter_entry->print_body, &context); + ast_str_append(&context.output_buffer, 0, "\nObjects found: %d\n", ao2_container_count(container)); + } else { if (ast_strlen_zero(object_id)) { ast_free(context.output_buffer); |