summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/res_config_mysql.c54
-rw-r--r--apps/app_voicemail.c12
-rw-r--r--main/Makefile8
-rw-r--r--main/tcptls.c20
-rw-r--r--res/res_config_pgsql.c50
-rw-r--r--res/res_config_sqlite3.c219
-rw-r--r--res/res_pjsip/pjsip_cli.c2
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);