diff options
Diffstat (limited to 'res')
-rw-r--r-- | res/res_config_curl.c | 64 | ||||
-rw-r--r-- | res/res_config_ldap.c | 190 | ||||
-rw-r--r-- | res/res_config_odbc.c | 144 | ||||
-rw-r--r-- | res/res_config_pgsql.c | 365 | ||||
-rw-r--r-- | res/res_config_sqlite.c | 113 | ||||
-rw-r--r-- | res/res_realtime.c | 143 |
6 files changed, 873 insertions, 146 deletions
diff --git a/res/res_config_curl.c b/res/res_config_curl.c index 37079adea..b159c768d 100644 --- a/res/res_config_curl.c +++ b/res/res_config_curl.c @@ -275,6 +275,69 @@ static int update_curl(const char *url, const char *unused, const char *keyfield return -1; } +static int update2_curl(const char *url, const char *unused, va_list ap) +{ + struct ast_str *query; + char buf1[200], buf2[200]; + const char *newparam, *newval; + char *stringp; + int rowcount = -1, lookup = 1, first = 1; + const int EncodeSpecialChars = 1, bufsize = 100; + char *buffer; + + if (!ast_custom_function_find("CURL")) { + ast_log(LOG_ERROR, "func_curl.so must be loaded in order to use res_config_curl.so!!\n"); + return -1; + } + + if (!(query = ast_str_create(1000))) + return -1; + + if (!(buffer = ast_malloc(bufsize))) { + ast_free(query); + return -1; + } + + ast_str_set(&query, 0, "${CURL(%s/update?", url); + + for (;;) { + if ((newparam = va_arg(ap, const char *)) == SENTINEL) { + if (lookup) { + lookup = 0; + ast_str_append(&query, 0, ","); + /* Back to the first parameter; we don't need a starting '&' */ + first = 1; + continue; + } else { + break; + } + } + newval = va_arg(ap, const char *); + ast_uri_encode(newparam, buf1, sizeof(buf1), EncodeSpecialChars); + ast_uri_encode(newval, buf2, sizeof(buf2), EncodeSpecialChars); + ast_str_append(&query, 0, "%s%s=%s", first ? "" : "&", buf1, buf2); + } + va_end(ap); + + ast_str_append(&query, 0, ")}"); + /* TODO: Make proxies work */ + pbx_substitute_variables_helper(NULL, query->str, buffer, bufsize); + + /* Line oriented output */ + stringp = buffer; + while (*stringp <= ' ') + stringp++; + sscanf(stringp, "%d", &rowcount); + + ast_free(buffer); + ast_free(query); + + if (rowcount >= 0) + return (int)rowcount; + + return -1; +} + /*! * \brief Execute an INSERT query * \param url @@ -535,6 +598,7 @@ static struct ast_config_engine curl_engine = { .store_func = store_curl, .destroy_func = destroy_curl, .update_func = update_curl, + .update2_func = update2_curl, .require_func = require_curl, }; diff --git a/res/res_config_ldap.c b/res/res_config_ldap.c index 966da9eea..b5690b7b4 100644 --- a/res/res_config_ldap.c +++ b/res/res_config_ldap.c @@ -89,6 +89,7 @@ struct ldap_table_config { struct ast_variable *attributes; /*!< attribute names conversion */ struct ast_variable *delimiters; /*!< the current delimiter is semicolon, so we are not using this variable */ AST_LIST_ENTRY(ldap_table_config) entry; + /* TODO: Make proxies work */ }; /*! \brief Should be locked before using it */ @@ -1305,12 +1306,199 @@ static int update_ldap(const char *basedn, const char *table_name, const char *a return num_entries; } +static int update2_ldap(const char *basedn, const char *table_name, va_list ap) +{ + int error = 0; + LDAPMessage *ldap_entry = NULL; + LDAPMod **ldap_mods; + const char *newparam = NULL; + const char *newval = NULL; + char *dn; + int num_entries = 0; + int i = 0; + int mods_size = 0; + int mod_exists = 0; + struct ldap_table_config *table_config = NULL; + char *clean_basedn = NULL; + struct ast_str *filter = NULL; + int tries = 0; + int result = 0; + LDAPMessage *ldap_result_msg = NULL; + + if (!table_name) { + ast_log(LOG_WARNING, "No table_name specified.\n"); + return -1; + } + + if (!(filter = ast_str_create(80))) + return -1; + + ast_mutex_lock(&ldap_lock); + + /* We now have our complete statement; Lets connect to the server and execute it. */ + if (!ldap_reconnect()) { + ast_mutex_unlock(&ldap_lock); + ast_free(filter); + return -1; + } + + table_config = table_config_for_table_name(table_name); + if (!table_config) { + ast_log(LOG_WARNING, "No table named '%s'.\n", table_name); + ast_mutex_unlock(&ldap_lock); + ast_free(filter); + return -1; + } + + clean_basedn = cleaned_basedn(NULL, basedn); + + /* Create the filter with the table additional filter and the parameter/value pairs we were given */ + ast_str_append(&filter, 0, "(&"); + if (table_config && table_config->additional_filter) { + ast_str_append(&filter, 0, "%s", table_config->additional_filter); + } + if (table_config != base_table_config && base_table_config + && base_table_config->additional_filter) { + ast_str_append(&filter, 0, "%s", base_table_config->additional_filter); + } + + /* Get multiple lookup keyfields and values */ + while ((newparam = va_arg(ap, const char *))) { + newval = va_arg(ap, const char *); + append_var_and_value_to_filter(&filter, table_config, newparam, newval); + } + ast_str_append(&filter, 0, ")"); + + /* Create the modification array with the parameter/value pairs we were given, + * if there are several parameters with the same name, we collect them into + * one parameter/value pair and delimit them with a semicolon */ + newparam = va_arg(ap, const char *); + newparam = convert_attribute_name_to_ldap(table_config, newparam); + newval = va_arg(ap, const char *); + if (!newparam || !newval) { + ast_log(LOG_WARNING, + "LINE(%d): need at least one parameter to modify.\n", __LINE__); + ast_free(filter); + ast_free(clean_basedn); + return -1; + } + + mods_size = 2; /* one for the first param/value pair and one for the the terminating NULL */ + ldap_mods = ast_calloc(sizeof(LDAPMod *), mods_size); + ldap_mods[0] = ast_calloc(1, sizeof(LDAPMod)); + + ldap_mods[0]->mod_op = LDAP_MOD_REPLACE; + ldap_mods[0]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1); + strcpy(ldap_mods[0]->mod_type, newparam); + + ldap_mods[0]->mod_values = ast_calloc(sizeof(char), 2); + ldap_mods[0]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1); + strcpy(ldap_mods[0]->mod_values[0], newval); + + while ((newparam = va_arg(ap, const char *))) { + newparam = convert_attribute_name_to_ldap(table_config, newparam); + newval = va_arg(ap, const char *); + mod_exists = 0; + + for (i = 0; i < mods_size - 1; i++) { + if (ldap_mods[i]&& !strcmp(ldap_mods[i]->mod_type, newparam)) { + /* We have the parameter allready, adding the value as a semicolon delimited value */ + ldap_mods[i]->mod_values[0] = ast_realloc(ldap_mods[i]->mod_values[0], sizeof(char) * (strlen(ldap_mods[i]->mod_values[0]) + strlen(newval) + 2)); + strcat(ldap_mods[i]->mod_values[0], ";"); + strcat(ldap_mods[i]->mod_values[0], newval); + mod_exists = 1; + break; + } + } + + /* create new mod */ + if (!mod_exists) { + mods_size++; + ldap_mods = ast_realloc(ldap_mods, sizeof(LDAPMod *) * mods_size); + ldap_mods[mods_size - 1] = NULL; + ldap_mods[mods_size - 2] = ast_calloc(1, sizeof(LDAPMod)); + + ldap_mods[mods_size - 2]->mod_op = LDAP_MOD_REPLACE; + + ldap_mods[mods_size - 2]->mod_type = ast_calloc(sizeof(char), strlen(newparam) + 1); + strcpy(ldap_mods[mods_size - 2]->mod_type, newparam); + + ldap_mods[mods_size - 2]->mod_values = ast_calloc(sizeof(char *), 2); + ldap_mods[mods_size - 2]->mod_values[0] = ast_calloc(sizeof(char), strlen(newval) + 1); + strcpy(ldap_mods[mods_size - 2]->mod_values[0], newval); + } + } + /* freeing ldap_mods further down */ + + do { + /* freeing ldap_result further down */ + result = ldap_search_ext_s(ldapConn, clean_basedn, + LDAP_SCOPE_SUBTREE, filter->str, NULL, 0, NULL, NULL, NULL, LDAP_NO_LIMIT, + &ldap_result_msg); + if (result != LDAP_SUCCESS && is_ldap_connect_error(result)) { + ast_log(LOG_WARNING, "Failed to query database. Try %d/3\n", + tries + 1); + tries++; + if (tries < 3) { + usleep(500000L * tries); + if (ldapConn) { + ldap_unbind_ext_s(ldapConn, NULL, NULL); + ldapConn = NULL; + } + if (!ldap_reconnect()) + break; + } + } + } while (result != LDAP_SUCCESS && tries < 3 && is_ldap_connect_error(result)); + + if (result != LDAP_SUCCESS) { + ast_log(LOG_WARNING, "Failed to query directory. Check debug for more info.\n"); + ast_log(LOG_WARNING, "Query: %s\n", filter->str); + ast_log(LOG_WARNING, "Query Failed because: %s\n", + ldap_err2string(result)); + + ast_mutex_unlock(&ldap_lock); + if (filter) + free(filter); + if (clean_basedn) + free(clean_basedn); + ldap_msgfree(ldap_result_msg); + ldap_mods_free(ldap_mods, 0); + return -1; + } + /* Ready to update */ + if ((num_entries = ldap_count_entries(ldapConn, ldap_result_msg)) > 0) { + for (i = 0; option_debug > 2 && i < mods_size - 1; i++) + ast_debug(3, "LINE(%d) %s=%s \n", __LINE__, ldap_mods[i]->mod_type, ldap_mods[i]->mod_values[0]); + + ldap_entry = ldap_first_entry(ldapConn, ldap_result_msg); + + for (i = 0; ldap_entry; i++) { + dn = ldap_get_dn(ldapConn, ldap_entry); + if ((error = ldap_modify_ext_s(ldapConn, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) + ast_log(LOG_ERROR, "Couldn't modify dn:%s because %s", dn, ldap_err2string(error)); + + ldap_entry = ldap_next_entry(ldapConn, ldap_entry); + } + } + + ast_mutex_unlock(&ldap_lock); + if (filter) + free(filter); + if (clean_basedn) + free(clean_basedn); + ldap_msgfree(ldap_result_msg); + ldap_mods_free(ldap_mods, 0); + return num_entries; +} + static struct ast_config_engine ldap_engine = { .name = "ldap", .load_func = config_ldap, .realtime_func = realtime_ldap, .realtime_multi_func = realtime_multi_ldap, - .update_func = update_ldap + .update_func = update_ldap, + .update2_func = update2_ldap, }; static int load_module(void) diff --git a/res/res_config_odbc.c b/res/res_config_odbc.c index 82d6c989d..ea83652b1 100644 --- a/res/res_config_odbc.c +++ b/res/res_config_odbc.c @@ -49,6 +49,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/res_odbc.h" #include "asterisk/utils.h" +AST_THREADSTORAGE(sql_buf); + struct custom_prepare_struct { const char *sql; const char *extra; @@ -474,6 +476,147 @@ static int update_odbc(const char *database, const char *table, const char *keyf return -1; } +struct update2_prepare_struct { + const char *database; + const char *table; + va_list ap; +}; + +static SQLHSTMT update2_prepare(struct odbc_obj *obj, void *data) +{ + int res, x = 1, first = 1; + struct update2_prepare_struct *ups = data; + const char *newparam, *newval; + struct ast_str *sql = ast_str_thread_get(&sql_buf, 16); + SQLHSTMT stmt; + va_list ap; + struct odbc_cache_tables *tableptr = ast_odbc_find_table(ups->database, ups->table); + struct odbc_cache_columns *column; + + if (!sql) { + if (tableptr) { + ast_odbc_release_table(tableptr); + } + return NULL; + } + + if (!tableptr) { + ast_log(LOG_ERROR, "Could not retrieve metadata for table '%s@%s'. Update will fail!\n", ups->table, ups->database); + return NULL; + } + + res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n"); + ast_odbc_release_table(tableptr); + return NULL; + } + + ast_str_set(&sql, 0, "UPDATE %s SET ", ups->table); + + /* Start by finding the second set of parameters */ + va_copy(ap, ups->ap); + + while ((newparam = va_arg(ap, const char *))) { + newval = va_arg(ap, const char *); + } + + while ((newparam = va_arg(ap, const char *))) { + newval = va_arg(ap, const char *); + if ((column = ast_odbc_find_column(tableptr, newparam))) { + ast_str_append(&sql, 0, "%s%s=? ", first ? "" : ", ", newparam); + SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL); + first = 0; + } else { + ast_log(LOG_NOTICE, "Not updating column '%s' in '%s@%s' because that column does not exist!\n", newparam, ups->table, ups->database); + } + } + va_end(ap); + + /* Restart search, because we need to add the search parameters */ + va_copy(ap, ups->ap); + ast_str_append(&sql, 0, "WHERE"); + first = 1; + + while ((newparam = va_arg(ap, const char *))) { + newval = va_arg(ap, const char *); + if (!(column = ast_odbc_find_column(tableptr, newparam))) { + ast_log(LOG_ERROR, "One or more of the criteria columns '%s' on '%s@%s' for this update does not exist!\n", newparam, ups->table, ups->database); + ast_odbc_release_table(tableptr); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return NULL; + } + ast_str_append(&sql, 0, "%s %s=?", first ? "" : " AND", newparam); + SQLBindParameter(stmt, x++, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(newval), 0, (void *)newval, 0, NULL); + first = 0; + } + va_end(ap); + + /* Done with the table metadata */ + ast_odbc_release_table(tableptr); + + res = SQLPrepare(stmt, (unsigned char *)sql->str, SQL_NTS); + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql->str); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + return NULL; + } + + return stmt; +} + +/*! + * \brief Execute an UPDATE query + * \param database + * \param table + * \param ap list containing one or more field/value set(s). + * + * Update a database table, preparing the sql statement from a list of + * key/value pairs specified in ap. The lookup pairs are specified first + * and are separated from the update pairs by a sentinel value. + * Sub-in the values to the prepared statement and execute it. + * + * \retval number of rows affected + * \retval -1 on failure +*/ +static int update2_odbc(const char *database, const char *table, va_list ap) +{ + struct odbc_obj *obj; + SQLHSTMT stmt; + struct update2_prepare_struct ups = { .database = database, .table = table, }; + struct ast_str *sql; + int res; + SQLLEN rowcount = 0; + + va_copy(ups.ap, ap); + + if (!(obj = ast_odbc_request_obj(database, 0))) { + return -1; + } + + if (!(stmt = ast_odbc_prepare_and_execute(obj, update2_prepare, &ups))) { + ast_odbc_release_obj(obj); + return -1; + } + + res = SQLRowCount(stmt, &rowcount); + SQLFreeHandle(SQL_HANDLE_STMT, stmt); + ast_odbc_release_obj(obj); + + if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) { + /* Since only a single thread can access this memory, we can retrieve what would otherwise be lost. */ + sql = ast_str_thread_get(&sql_buf, 16); + ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n", sql->str); + return -1; + } + + if (rowcount >= 0) { + return (int)rowcount; + } + + return -1; +} + /*! * \brief Excute an INSERT query * \param database @@ -899,6 +1042,7 @@ static struct ast_config_engine odbc_engine = { .store_func = store_odbc, .destroy_func = destroy_odbc, .update_func = update_odbc, + .update2_func = update2_odbc, .require_func = require_odbc, .unload_func = ast_odbc_clear_cache, }; diff --git a/res/res_config_pgsql.c b/res/res_config_pgsql.c index f7c1f3a6a..fe672bf62 100644 --- a/res/res_config_pgsql.c +++ b/res/res_config_pgsql.c @@ -42,6 +42,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" AST_MUTEX_DEFINE_STATIC(pgsql_lock); +AST_THREADSTORAGE(sql_buf); +AST_THREADSTORAGE(findtable_buf); +AST_THREADSTORAGE(where_buf); +AST_THREADSTORAGE(escapebuf_buf); #define RES_CONFIG_PGSQL_CONF "res_pgsql.conf" @@ -59,7 +63,7 @@ struct columns { }; struct tables { - ast_mutex_t lock; + ast_rwlock_t lock; AST_LIST_HEAD_NOLOCK(psql_columns, columns) columns; AST_LIST_ENTRY(tables) list; char name[0]; @@ -87,15 +91,24 @@ static struct ast_cli_entry cli_realtime[] = { AST_CLI_DEFINE(handle_cli_realtime_pgsql_cache, "Shows cached tables within the PostgreSQL realtime driver"), }; +#define ESCAPE_STRING(buffer, stringname) \ + do { \ + int len; \ + if ((len = strlen(stringname)) > (buffer->len - 1) / 2) { \ + ast_str_make_space(&buffer, len * 2 + 1); \ + } \ + PQescapeStringConn(pgsqlConn, buffer->str, stringname, len, &pgresult); \ + } while (0) + static void destroy_table(struct tables *table) { struct columns *column; - ast_mutex_lock(&table->lock); + ast_rwlock_wrlock(&table->lock); while ((column = AST_LIST_REMOVE_HEAD(&table->columns, list))) { ast_free(column); } - ast_mutex_unlock(&table->lock); - ast_mutex_destroy(&table->lock); + ast_rwlock_unlock(&table->lock); + ast_rwlock_destroy(&table->lock); ast_free(table); } @@ -103,7 +116,7 @@ static struct tables *find_table(const char *tablename) { struct columns *column; struct tables *table; - struct ast_str *sql = ast_str_create(330); + struct ast_str *sql = ast_str_thread_get(&findtable_buf, 330); char *pgerror; PGresult *result; char *fname, *ftype, *flen, *fnotnull, *fdef; @@ -113,7 +126,7 @@ static struct tables *find_table(const char *tablename) AST_LIST_TRAVERSE(&psql_tables, table, list) { if (!strcasecmp(table->name, tablename)) { ast_debug(1, "Found table in cache; now locking\n"); - ast_mutex_lock(&table->lock); + ast_rwlock_rdlock(&table->lock); ast_debug(1, "Lock cached table; now returning\n"); AST_LIST_UNLOCK(&psql_tables); return table; @@ -140,9 +153,9 @@ static struct tables *find_table(const char *tablename) return NULL; } strcpy(table->name, tablename); /* SAFE */ - ast_mutex_init(&table->lock); + ast_rwlock_init(&table->lock); AST_LIST_HEAD_INIT_NOLOCK(&table->columns); - + rows = PQntuples(result); for (i = 0; i < rows; i++) { fname = PQgetvalue(result, i, 0); @@ -186,23 +199,39 @@ static struct tables *find_table(const char *tablename) PQclear(result); AST_LIST_INSERT_TAIL(&psql_tables, table, list); - ast_mutex_lock(&table->lock); + ast_rwlock_rdlock(&table->lock); AST_LIST_UNLOCK(&psql_tables); return table; } -static struct ast_variable *realtime_pgsql(const char *database, const char *table, va_list ap) +#define release_table(table) ast_rwlock_unlock(&(table)->lock); + +static struct columns *find_column(struct tables *t, const char *colname) +{ + struct columns *column; + + /* Check that the column exists in the table */ + AST_LIST_TRAVERSE(&t->columns, column, list) { + if (strcmp(column->name, colname) == 0) { + return column; + } + } + return NULL; +} + +static struct ast_variable *realtime_pgsql(const char *database, const char *tablename, va_list ap) { PGresult *result = NULL; - int num_rows = 0, pgerror; - char sql[256], escapebuf[513]; + int num_rows = 0, pgresult; + struct ast_str *sql = ast_str_thread_get(&sql_buf, 100); + struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100); char *stringp; char *chunk; char *op; const char *newparam, *newval; struct ast_variable *var = NULL, *prev = NULL; - if (!table) { + if (!tablename) { ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n"); return NULL; } @@ -216,7 +245,7 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; - }; + } return NULL; } @@ -224,15 +253,14 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab If there is only 1 set, then we have our query. Otherwise, loop thru the list and concat */ op = strchr(newparam, ' ') ? "" : " ="; - PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); - if (pgerror) { + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } - snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, - escapebuf); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", tablename, newparam, op, escapebuf->str); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if (!strchr(newparam, ' ')) @@ -240,15 +268,14 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab else op = ""; - PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); - if (pgerror) { + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } - snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, - op, escapebuf); + ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, escapebuf->str); } va_end(ap); @@ -259,10 +286,10 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab return NULL; } - if (!(result = PQexec(pgsqlConn, sql))) { + if (!(result = PQexec(pgsqlConn, sql->str))) { ast_log(LOG_WARNING, - "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql); + "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; @@ -272,8 +299,8 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab && result_status != PGRES_TUPLES_OK && result_status != PGRES_NONFATAL_ERROR) { ast_log(LOG_WARNING, - "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql); + "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", tablename, database); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); @@ -281,7 +308,7 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab } } - ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql); + ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql->str); if ((num_rows = PQntuples(result)) > 0) { int i = 0; @@ -318,7 +345,7 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab } ast_free(fieldnames); } else { - ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s.\n", table); + ast_debug(1, "Postgresql RealTime: Could not find any rows in table %s@%s.\n", tablename, database); } ast_mutex_unlock(&pgsql_lock); @@ -330,8 +357,9 @@ static struct ast_variable *realtime_pgsql(const char *database, const char *tab static struct ast_config *realtime_multi_pgsql(const char *database, const char *table, va_list ap) { PGresult *result = NULL; - int num_rows = 0, pgerror; - char sql[256], escapebuf[513]; + int num_rows = 0, pgresult; + struct ast_str *sql = ast_str_thread_get(&sql_buf, 100); + struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100); const char *initfield = NULL; char *stringp; char *chunk; @@ -358,7 +386,7 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; - }; + } return NULL; } @@ -375,15 +403,14 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char else op = ""; - PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); - if (pgerror) { + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } - snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, - escapebuf); + ast_str_set(&sql, 0, "SELECT * FROM %s WHERE %s%s '%s'", table, newparam, op, escapebuf->str); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); if (!strchr(newparam, ' ')) @@ -391,19 +418,18 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char else op = ""; - PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); - if (pgerror) { + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); return NULL; } - snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " AND %s%s '%s'", newparam, - op, escapebuf); + ast_str_append(&sql, 0, " AND %s%s '%s'", newparam, op, escapebuf->str); } if (initfield) { - snprintf(sql + strlen(sql), sizeof(sql) - strlen(sql), " ORDER BY %s", initfield); + ast_str_append(&sql, 0, " ORDER BY %s", initfield); } va_end(ap); @@ -415,10 +441,10 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char return NULL; } - if (!(result = PQexec(pgsqlConn, sql))) { + if (!(result = PQexec(pgsqlConn, sql->str))) { ast_log(LOG_WARNING, - "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql); + "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; @@ -428,8 +454,8 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char && result_status != PGRES_TUPLES_OK && result_status != PGRES_NONFATAL_ERROR) { ast_log(LOG_WARNING, - "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql); + "PostgreSQL RealTime: Failed to query %s@%s. Check debug for more info.\n", table, database); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); @@ -437,7 +463,7 @@ static struct ast_config *realtime_multi_pgsql(const char *database, const char } } - ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql); + ast_debug(1, "PostgreSQL RealTime: Result=%p Query: %s\n", result, sql->str); if ((num_rows = PQntuples(result)) > 0) { int numFields = PQnfields(result); @@ -490,22 +516,20 @@ static int update_pgsql(const char *database, const char *tablename, const char const char *lookup, va_list ap) { PGresult *result = NULL; - int numrows = 0, pgerror; - char escapebuf[513]; + int numrows = 0, pgresult; const char *newparam, *newval; - struct ast_str *sql = ast_str_create(100); + struct ast_str *sql = ast_str_thread_get(&sql_buf, 100); + struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 100); struct tables *table; struct columns *column = NULL; if (!tablename) { ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n"); - ast_free(sql); return -1; } if (!(table = find_table(tablename))) { ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename); - ast_free(sql); return -1; } @@ -518,9 +542,8 @@ static int update_pgsql(const char *database, const char *tablename, const char if (pgsqlConn) { PQfinish(pgsqlConn); pgsqlConn = NULL; - }; - ast_mutex_unlock(&table->lock); - ast_free(sql); + } + release_table(table); return -1; } @@ -533,62 +556,51 @@ static int update_pgsql(const char *database, const char *tablename, const char if (!column) { ast_log(LOG_ERROR, "PostgreSQL RealTime: Updating on column '%s', but that column does not exist within the table '%s'!\n", newparam, tablename); - ast_mutex_unlock(&table->lock); - ast_free(sql); + release_table(table); return -1; } /* 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 */ - PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); - if (pgerror) { + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); - ast_mutex_unlock(&table->lock); - ast_free(sql); + release_table(table); return -1; } - ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, escapebuf); + ast_str_set(&sql, 0, "UPDATE %s SET %s = '%s'", tablename, newparam, escapebuf->str); while ((newparam = va_arg(ap, const char *))) { newval = va_arg(ap, const char *); - /* If the column is not within the table, then skip it */ - AST_LIST_TRAVERSE(&table->columns, column, list) { - if (strcmp(column->name, newparam) == 0) { - break; - } - } - - if (!column) { + if (!find_column(table, newparam)) { ast_log(LOG_WARNING, "Attempted to update column '%s' in table '%s', but column does not exist!\n", newparam, tablename); continue; } - PQescapeStringConn(pgsqlConn, escapebuf, newval, (sizeof(escapebuf) - 1) / 2, &pgerror); - if (pgerror) { + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); va_end(ap); - ast_mutex_unlock(&table->lock); - ast_free(sql); + release_table(table); return -1; } - ast_str_append(&sql, 0, ", %s = '%s'", newparam, escapebuf); + ast_str_append(&sql, 0, ", %s = '%s'", newparam, escapebuf->str); } va_end(ap); - ast_mutex_unlock(&table->lock); + release_table(table); - PQescapeStringConn(pgsqlConn, escapebuf, lookup, (sizeof(escapebuf) - 1) / 2, &pgerror); - if (pgerror) { + ESCAPE_STRING(escapebuf, lookup); + if (pgresult) { ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", lookup); va_end(ap); - ast_free(sql); return -1; } - ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, escapebuf); + ast_str_append(&sql, 0, " WHERE %s = '%s'", keyfield, escapebuf->str); ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", sql->str); @@ -596,7 +608,6 @@ static int update_pgsql(const char *database, const char *tablename, const char ast_mutex_lock(&pgsql_lock); if (!pgsql_reconnect(database)) { ast_mutex_unlock(&pgsql_lock); - ast_free(sql); return -1; } @@ -642,22 +653,145 @@ static int update_pgsql(const char *database, const char *tablename, const char return -1; } -#define ESCAPE_STRING(buffer, stringname) \ - do { \ - int len; \ - if ((len = strlen(stringname)) > (buffer->len - 1) / 2) { \ - ast_str_make_space(&buffer, len * 2 + 1); \ - } \ - PQescapeStringConn(pgsqlConn, buffer->str, stringname, len, &pgresult); \ - } while (0) +static int update2_pgsql(const char *database, const char *tablename, va_list ap) +{ + PGresult *result = NULL; + int numrows = 0, pgresult, first = 1; + struct ast_str *escapebuf = ast_str_thread_get(&escapebuf_buf, 16); + const char *newparam, *newval; + struct ast_str *sql = ast_str_thread_get(&sql_buf, 100); + struct ast_str *where = ast_str_thread_get(&where_buf, 100); + struct tables *table; + + if (!tablename) { + ast_log(LOG_WARNING, "PostgreSQL RealTime: No table specified.\n"); + return -1; + } + + if (!escapebuf || !sql || !where) { + /* Memory error, already handled */ + return -1; + } + + if (!(table = find_table(tablename))) { + ast_log(LOG_ERROR, "Table '%s' does not exist!!\n", tablename); + return -1; + } + + ast_str_set(&sql, 0, "UPDATE %s SET ", tablename); + ast_str_set(&where, 0, "WHERE"); + + while ((newparam = va_arg(ap, const char *))) { + if (!find_column(table, newparam)) { + ast_log(LOG_ERROR, "Attempted to update based on criteria column '%s' (%s@%s), but that column does not exist!\n", newparam, tablename, database); + release_table(table); + return -1; + } + + newval = va_arg(ap, const char *); + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { + ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); + release_table(table); + ast_free(sql); + return -1; + } + ast_str_append(&where, 0, "%s %s='%s'", first ? "" : " AND", newparam, escapebuf->str); + first = 0; + } + + if (first) { + ast_log(LOG_WARNING, + "PostgreSQL RealTime: Realtime update requires at least 1 parameter and 1 value to search on.\n"); + if (pgsqlConn) { + PQfinish(pgsqlConn); + pgsqlConn = NULL; + } + release_table(table); + return -1; + } + + /* Now retrieve the columns to update */ + first = 1; + while ((newparam = va_arg(ap, const char *))) { + newval = va_arg(ap, const char *); + + /* If the column is not within the table, then skip it */ + if (!find_column(table, newparam)) { + ast_log(LOG_NOTICE, "Attempted to update column '%s' in table '%s@%s', but column does not exist!\n", newparam, tablename, database); + continue; + } + + ESCAPE_STRING(escapebuf, newval); + if (pgresult) { + ast_log(LOG_ERROR, "Postgres detected invalid input: '%s'\n", newval); + release_table(table); + ast_free(sql); + return -1; + } + + ast_str_append(&sql, 0, "%s %s='%s'", first ? "" : ",", newparam, escapebuf->str); + } + release_table(table); + + ast_str_append(&sql, 0, " %s", where->str); + + ast_debug(1, "PostgreSQL RealTime: Update SQL: %s\n", sql->str); + + /* We now have our complete statement; connect to the server and execute it. */ + ast_mutex_lock(&pgsql_lock); + if (!pgsql_reconnect(database)) { + ast_mutex_unlock(&pgsql_lock); + return -1; + } + + if (!(result = PQexec(pgsqlConn, sql->str))) { + ast_log(LOG_WARNING, + "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); + ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); + ast_mutex_unlock(&pgsql_lock); + return -1; + } else { + ExecStatusType result_status = PQresultStatus(result); + if (result_status != PGRES_COMMAND_OK + && result_status != PGRES_TUPLES_OK + && result_status != PGRES_NONFATAL_ERROR) { + ast_log(LOG_WARNING, + "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); + ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", + PQresultErrorMessage(result), PQresStatus(result_status)); + ast_mutex_unlock(&pgsql_lock); + return -1; + } + } + + numrows = atoi(PQcmdTuples(result)); + ast_mutex_unlock(&pgsql_lock); + + ast_debug(1, "PostgreSQL RealTime: Updated %d rows on table: %s\n", numrows, tablename); + + /* From http://dev.pgsql.com/doc/pgsql/en/pgsql-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.) + */ + + if (numrows >= 0) { + return (int) numrows; + } + + return -1; +} static int store_pgsql(const char *database, const char *table, va_list ap) { PGresult *result = NULL; Oid insertid; - struct ast_str *buf = ast_str_create(256); - struct ast_str *sql1 = ast_str_create(256); - struct ast_str *sql2 = ast_str_create(256); + struct ast_str *buf = ast_str_thread_get(&escapebuf_buf, 256); + struct ast_str *sql1 = ast_str_thread_get(&sql_buf, 256); + struct ast_str *sql2 = ast_str_thread_get(&where_buf, 256); int pgresult; const char *newparam, *newval; @@ -710,9 +844,6 @@ static int store_pgsql(const char *database, const char *table, va_list ap) ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql1->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); - ast_free(sql1); - ast_free(sql2); - ast_free(buf); return -1; } else { ExecStatusType result_status = PQresultStatus(result); @@ -725,18 +856,12 @@ static int store_pgsql(const char *database, const char *table, va_list ap) ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); - ast_free(sql1); - ast_free(sql2); - ast_free(buf); return -1; } } insertid = PQoidValue(result); ast_mutex_unlock(&pgsql_lock); - ast_free(sql1); - ast_free(sql2); - ast_free(buf); ast_debug(1, "PostgreSQL RealTime: row inserted on table: %s, id: %u\n", table, insertid); @@ -757,8 +882,8 @@ static int destroy_pgsql(const char *database, const char *table, const char *ke PGresult *result = NULL; int numrows = 0; int pgresult; - struct ast_str *sql = ast_str_create(256); - struct ast_str *buf1 = ast_str_create(60), *buf2 = ast_str_create(60); + struct ast_str *sql = ast_str_thread_get(&sql_buf, 256); + struct ast_str *buf1 = ast_str_thread_get(&where_buf, 60), *buf2 = ast_str_thread_get(&escapebuf_buf, 60); const char *newparam, *newval; if (!table) { @@ -810,9 +935,6 @@ static int destroy_pgsql(const char *database, const char *table, const char *ke ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); - ast_free(buf1); - ast_free(buf2); - ast_free(sql); return -1; } else { ExecStatusType result_status = PQresultStatus(result); @@ -825,18 +947,12 @@ static int destroy_pgsql(const char *database, const char *table, const char *ke ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); - ast_free(buf1); - ast_free(buf2); - ast_free(sql); return -1; } } numrows = atoi(PQcmdTuples(result)); ast_mutex_unlock(&pgsql_lock); - ast_free(buf1); - ast_free(buf2); - ast_free(sql); ast_debug(1, "PostgreSQL RealTime: Deleted %d rows on table: %s\n", numrows, table); @@ -861,9 +977,7 @@ static struct ast_config *config_pgsql(const char *database, const char *table, long num_rows; struct ast_variable *new_v; struct ast_category *cur_cat = NULL; - char sqlbuf[1024] = ""; - char *sql = sqlbuf; - size_t sqlleft = sizeof(sqlbuf); + struct ast_str *sql = ast_str_thread_get(&sql_buf, 100); char last[80] = ""; int last_cat_metric = 0; @@ -874,11 +988,11 @@ static struct ast_config *config_pgsql(const char *database, const char *table, return NULL; } - ast_build_string(&sql, &sqlleft, "SELECT category, var_name, var_val, cat_metric FROM %s ", table); - ast_build_string(&sql, &sqlleft, "WHERE filename='%s' and commented=0", file); - ast_build_string(&sql, &sqlleft, "ORDER BY cat_metric DESC, var_metric ASC, category, var_name "); + ast_str_set(&sql, 0, "SELECT category, var_name, var_val, cat_metric FROM %s " + "WHERE filename='%s' and commented=0" + "ORDER BY cat_metric DESC, var_metric ASC, category, var_name ", table, file); - ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", sqlbuf); + ast_debug(1, "PostgreSQL RealTime: Static SQL: %s\n", sql->str); /* We now have our complete statement; Lets connect to the server and execute it. */ ast_mutex_lock(&pgsql_lock); @@ -887,10 +1001,10 @@ static struct ast_config *config_pgsql(const char *database, const char *table, return NULL; } - if (!(result = PQexec(pgsqlConn, sqlbuf))) { + if (!(result = PQexec(pgsqlConn, sql->str))) { ast_log(LOG_WARNING, - "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql); + "PostgreSQL RealTime: Failed to query '%s@%s'. Check debug for more info.\n", table, database); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s\n", PQerrorMessage(pgsqlConn)); ast_mutex_unlock(&pgsql_lock); return NULL; @@ -901,7 +1015,7 @@ static struct ast_config *config_pgsql(const char *database, const char *table, && result_status != PGRES_NONFATAL_ERROR) { ast_log(LOG_WARNING, "PostgreSQL RealTime: Failed to query database. Check debug for more info.\n"); - ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql); + ast_debug(1, "PostgreSQL RealTime: Query: %s\n", sql->str); ast_debug(1, "PostgreSQL RealTime: Query Failed because: %s (%s)\n", PQresultErrorMessage(result), PQresStatus(result_status)); ast_mutex_unlock(&pgsql_lock); @@ -1067,7 +1181,7 @@ static int require_pgsql(const char *database, const char *tablename, va_list ap } } } - ast_mutex_unlock(&table->lock); + release_table(table); return res; } @@ -1101,6 +1215,7 @@ static struct ast_config_engine pgsql_engine = { .store_func = store_pgsql, .destroy_func = destroy_pgsql, .update_func = update_pgsql, + .update2_func = update2_pgsql, .require_func = require_pgsql, .unload_func = unload_pgsql, }; @@ -1353,7 +1468,7 @@ static char *handle_cli_realtime_pgsql_cache(struct ast_cli_entry *e, int cmd, s AST_LIST_TRAVERSE(&cur->columns, col, list) { ast_cli(a->fd, "%-20.20s %-20.20s %3d %-8.8s\n", col->name, col->type, col->len, col->notnull ? "NOT NULL" : ""); } - ast_mutex_unlock(&cur->lock); + release_table(cur); } else { ast_cli(a->fd, "No such table '%s'\n", a->argv[4]); } diff --git a/res/res_config_sqlite.c b/res/res_config_sqlite.c index 7a18d001c..c3ef1dd1a 100644 --- a/res/res_config_sqlite.c +++ b/res/res_config_sqlite.c @@ -123,6 +123,9 @@ MACRO_BEGIN \ } \ MACRO_END +AST_THREADSTORAGE(sql_buf); +AST_THREADSTORAGE(where_buf); + /*! * Maximum number of loops before giving up executing a query. Calls to * sqlite_xxx() functions which can return SQLITE_BUSY @@ -299,7 +302,7 @@ static struct ast_config * config_handler(const char *database, const char *tabl * \retval 0 if an error occurred. */ static size_t get_params(va_list ap, const char ***params_ptr, - const char ***vals_ptr); + const char ***vals_ptr, int warn); /*! * \brief SQLite callback function for RealTime configuration. @@ -396,6 +399,8 @@ static struct ast_config * realtime_multi_handler(const char *database, */ static int realtime_update_handler(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap); +static int realtime_update2_handler(const char *database, const char *table, + va_list ap); /*! * \brief Asterisk callback function for RealTime configuration (variable @@ -484,6 +489,7 @@ static struct ast_config_engine sqlite_engine = .store_func = realtime_store_handler, .destroy_func = realtime_destroy_handler, .update_func = realtime_update_handler, + .update2_func = realtime_update2_handler, .require_func = realtime_require_handler, .unload_func = realtime_unload_handler, }; @@ -949,7 +955,7 @@ static struct ast_config *config_handler(const char *database, const char *table return cfg; } -static size_t get_params(va_list ap, const char ***params_ptr, const char ***vals_ptr) +static size_t get_params(va_list ap, const char ***params_ptr, const char ***vals_ptr, int warn) { const char **tmp, *param, *val, **params, **vals; size_t params_count; @@ -981,8 +987,9 @@ static size_t get_params(va_list ap, const char ***params_ptr, const char ***val if (params_count > 0) { *params_ptr = params; *vals_ptr = vals; - } else + } else if (warn) { ast_log(LOG_WARNING, "1 parameter and 1 value at least required\n"); + } return params_count; } @@ -1029,7 +1036,7 @@ static struct ast_variable * realtime_handler(const char *database, const char * return NULL; } - params_count = get_params(ap, ¶ms, &vals); + params_count = get_params(ap, ¶ms, &vals, 1); if (params_count == 0) return NULL; @@ -1038,10 +1045,10 @@ static struct ast_variable * realtime_handler(const char *database, const char * /* \cond DOXYGEN_CAN_PARSE_THIS */ #undef QUERY -#define QUERY "SELECT * FROM '%q' WHERE commented = 0 AND %q%s '%q'" +#define QUERY "SELECT * FROM '%q' WHERE%s %q%s '%q'" /* \endcond */ - query = sqlite_mprintf(QUERY, table, params[0], op, vals[0]); + query = sqlite_mprintf(QUERY, table, !strcmp(config_table, table) ? " commented = 0 AND" : "", params[0], op, vals[0]); if (!query) { ast_log(LOG_WARNING, "Unable to allocate SQL query\n"); @@ -1174,7 +1181,7 @@ static struct ast_config *realtime_multi_handler(const char *database, return NULL; } - if (!(params_count = get_params(ap, ¶ms, &vals))) { + if (!(params_count = get_params(ap, ¶ms, &vals, 1))) { ast_config_destroy(cfg); return NULL; } @@ -1286,7 +1293,7 @@ static int realtime_update_handler(const char *database, const char *table, return -1; } - if (!(params_count = get_params(ap, ¶ms, &vals))) + if (!(params_count = get_params(ap, ¶ms, &vals, 1))) return -1; /* \cond DOXYGEN_CAN_PARSE_THIS */ @@ -1355,6 +1362,80 @@ static int realtime_update_handler(const char *database, const char *table, return rows_num; } +static int realtime_update2_handler(const char *database, const char *table, + va_list ap) +{ + char *errormsg = NULL, *tmp1, *tmp2; + int error, rows_num, first = 1; + struct ast_str *sql = ast_str_thread_get(&sql_buf, 100); + struct ast_str *where = ast_str_thread_get(&where_buf, 100); + const char *param, *value; + + if (!table) { + ast_log(LOG_WARNING, "Table name unspecified\n"); + return -1; + } + + if (!sql) { + return -1; + } + + ast_str_set(&sql, 0, "UPDATE %s SET", table); + ast_str_set(&where, 0, " WHERE"); + + while ((param = va_arg(ap, const char *))) { + value = va_arg(ap, const char *); + ast_str_append(&where, 0, "%s %s = %s", + first ? "" : " AND", + tmp1 = sqlite_mprintf("%q", param), + tmp2 = sqlite_mprintf("%Q", value)); + sqlite_freemem(tmp1); + sqlite_freemem(tmp2); + first = 0; + } + + if (first) { + ast_log(LOG_ERROR, "No criteria specified on update to '%s@%s'!\n", table, database); + return -1; + } + + first = 1; + while ((param = va_arg(ap, const char *))) { + value = va_arg(ap, const char *); + ast_str_append(&sql, 0, "%s %s = %s", + first ? "" : ",", + tmp1 = sqlite_mprintf("%q", param), + tmp2 = sqlite_mprintf("%Q", value)); + sqlite_freemem(tmp1); + sqlite_freemem(tmp2); + first = 0; + } + + ast_str_append(&sql, 0, " %s", where->str); + ast_debug(1, "SQL query: %s\n", sql->str); + + ast_mutex_lock(&mutex); + + RES_CONFIG_SQLITE_BEGIN + error = sqlite_exec(db, sql->str, NULL, NULL, &errormsg); + RES_CONFIG_SQLITE_END(error) + + if (!error) { + rows_num = sqlite_changes(db); + } else { + rows_num = -1; + } + + ast_mutex_unlock(&mutex); + + if (error) { + ast_log(LOG_WARNING, "%s\n", S_OR(errormsg, sqlite_error_string(error))); + } + sqlite_freemem(errormsg); + + return rows_num; +} + static int realtime_store_handler(const char *database, const char *table, va_list ap) { char *errormsg = NULL, *tmp_str, *tmp_keys = NULL, *tmp_keys2 = NULL, *tmp_vals = NULL, *tmp_vals2 = NULL; @@ -1368,7 +1449,7 @@ static int realtime_store_handler(const char *database, const char *table, va_li return -1; } - if (!(params_count = get_params(ap, ¶ms, &vals))) + if (!(params_count = get_params(ap, ¶ms, &vals, 1))) return -1; /* \cond DOXYGEN_CAN_PARSE_THIS */ @@ -1392,10 +1473,10 @@ static int realtime_store_handler(const char *database, const char *table, va_li } if ( tmp_vals2 ) { - tmp_vals = sqlite_mprintf("%s, '%q'", tmp_vals2, params[i]); + tmp_vals = sqlite_mprintf("%s, '%q'", tmp_vals2, vals[i]); sqlite_freemem(tmp_vals2); } else { - tmp_vals = sqlite_mprintf("'%q'", params[i]); + tmp_vals = sqlite_mprintf("'%q'", vals[i]); } if (!tmp_vals) { ast_log(LOG_WARNING, "Unable to reallocate SQL query\n"); @@ -1453,7 +1534,7 @@ static int realtime_destroy_handler(const char *database, const char *table, const char *keyfield, const char *entity, va_list ap) { char *query, *errormsg = NULL, *tmp_str; - const char **params, **vals; + const char **params = NULL, **vals = NULL; size_t params_count; int error, rows_num; size_t i; @@ -1463,8 +1544,7 @@ static int realtime_destroy_handler(const char *database, const char *table, return -1; } - if (!(params_count = get_params(ap, ¶ms, &vals))) - return -1; + params_count = get_params(ap, ¶ms, &vals, 0); /* \cond DOXYGEN_CAN_PARSE_THIS */ #undef QUERY @@ -1509,10 +1589,11 @@ static int realtime_destroy_handler(const char *database, const char *table, error = sqlite_exec(db, query, NULL, NULL, &errormsg); RES_CONFIG_SQLITE_END(error) - if (!error) + if (!error) { rows_num = sqlite_changes(db); - else + } else { rows_num = -1; + } ast_mutex_unlock(&mutex); diff --git a/res/res_realtime.c b/res/res_realtime.c index 5ecff8b94..15e25beec 100644 --- a/res/res_realtime.c +++ b/res/res_realtime.c @@ -76,7 +76,8 @@ static char *cli_realtime_load(struct ast_cli_entry *e, int cmd, struct ast_cli_ return CLI_SUCCESS; } -static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) { +static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ int res = 0; switch (cmd) { @@ -93,18 +94,149 @@ static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cl return NULL; } - if (a->argc < 7) return CLI_SHOWUSAGE; res = ast_update_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], SENTINEL); - if(res < 0) { + if (res < 0) { + ast_cli(a->fd, "Failed to update. Check the debug log for possible SQL related entries.\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "Updated %d RealTime record%s.\n", res, ESS(res)); + + return CLI_SUCCESS; +} + +static char *cli_realtime_update2(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int res = -1; + + switch (cmd) { + case CLI_INIT: + e->command = "realtime update2"; + e->usage = + "Usage: realtime update2 <family> <colupdate> <newvalue> <colmatch> <valuematch> [... <colmatch5> <valuematch5>]\n" + " Update a single variable using the RealTime driver.\n" + " You must supply a family name, a column to update on, a new value, column to match, and value to match.\n" + " Ex: realtime update sipfriends name bobsphone port 4343\n" + " will execute SQL as UPDATE sipfriends SET port = 4343 WHERE name = bobsphone\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc < 7) + return CLI_SHOWUSAGE; + + if (a->argc == 7) { + res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], SENTINEL, a->argv[3], a->argv[4], SENTINEL); + } else if (a->argc == 9) { + res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], SENTINEL, a->argv[3], a->argv[4], SENTINEL); + } else if (a->argc == 11) { + res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], SENTINEL, a->argv[3], a->argv[4], SENTINEL); + } else if (a->argc == 13) { + res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], SENTINEL, a->argv[3], a->argv[4], SENTINEL); + } else if (a->argc == 15) { + res = ast_update2_realtime(a->argv[2], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], a->argv[13], a->argv[14], SENTINEL, a->argv[3], a->argv[4], SENTINEL); + } else { + return CLI_SHOWUSAGE; + } + + if (res < 0) { ast_cli(a->fd, "Failed to update. Check the debug log for possible SQL related entries.\n"); return CLI_FAILURE; } - ast_cli(a->fd, "Updated %d RealTime record%s.\n", res, ESS(res)); + ast_cli(a->fd, "Updated %d RealTime record%s.\n", res, ESS(res)); + + return CLI_SUCCESS; +} + +static char *cli_realtime_store(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int res = -1; + + switch (cmd) { + case CLI_INIT: + e->command = "realtime store"; + e->usage = + "Usage: realtime store <family> <colname1> <value1> [<colname2> <value2> [... <colmatch5> <valuematch5>]]\n" + " Create a stored row using the RealTime driver.\n" + " You must supply a family name and name/value pairs (up to 5). If\n" + " you need to store more than 5 key/value pairs, start with the first\n" + " five, then use 'realtime update' or 'realtime update2' to add\n" + " additional columns.\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc < 5) { + return CLI_SHOWUSAGE; + } else if (a->argc == 5) { + res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], SENTINEL); + } else if (a->argc == 7) { + res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], SENTINEL); + } else if (a->argc == 9) { + res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], SENTINEL); + } else if (a->argc == 11) { + res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], SENTINEL); + } else if (a->argc == 13) { + res = ast_store_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], SENTINEL); + } else { + return CLI_SHOWUSAGE; + } + + if (res < 0) { + ast_cli(a->fd, "Failed to store record. Check the debug log for possible SQL related entries.\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "Stored RealTime record.\n"); + + return CLI_SUCCESS; +} + +static char *cli_realtime_destroy(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + int res = -1; + + switch (cmd) { + case CLI_INIT: + e->command = "realtime destroy"; + e->usage = + "Usage: realtime destroy <family> <colname1> <value1> [<colname2> <value2> [... <colmatch5> <valuematch5>]]\n" + " Remove a stored row using the RealTime driver.\n" + " You must supply a family name and name/value pairs (up to 5).\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc < 5) { + return CLI_SHOWUSAGE; + } else if (a->argc == 5) { + res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], SENTINEL); + } else if (a->argc == 7) { + res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], SENTINEL); + } else if (a->argc == 9) { + res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], SENTINEL); + } else if (a->argc == 11) { + res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], SENTINEL); + } else if (a->argc == 13) { + res = ast_destroy_realtime(a->argv[2], a->argv[3], a->argv[4], a->argv[5], a->argv[6], a->argv[7], a->argv[8], a->argv[9], a->argv[10], a->argv[11], a->argv[12], SENTINEL); + } else { + return CLI_SHOWUSAGE; + } + + if (res < 0) { + ast_cli(a->fd, "Failed to remove record. Check the debug log for possible SQL related entries.\n"); + return CLI_FAILURE; + } + + ast_cli(a->fd, "Removed %d RealTime record%s.\n", res, ESS(res)); return CLI_SUCCESS; } @@ -112,6 +244,9 @@ static char *cli_realtime_update(struct ast_cli_entry *e, int cmd, struct ast_cl static struct ast_cli_entry cli_realtime[] = { AST_CLI_DEFINE(cli_realtime_load, "Used to print out RealTime variables."), AST_CLI_DEFINE(cli_realtime_update, "Used to update RealTime variables."), + AST_CLI_DEFINE(cli_realtime_update2, "Used to test the RealTime update2 method"), + AST_CLI_DEFINE(cli_realtime_store, "Store a new row into a RealTime database"), + AST_CLI_DEFINE(cli_realtime_destroy, "Delete a row from a RealTime database"), }; static int unload_module(void) |