Implement changing group assignments through the API
Signed-off-by: DL6ER <dl6er@dl6er.de>
This commit is contained in:
parent
7fa2dac90a
commit
5c3a2b509c
224
src/api/list.c
224
src/api/list.c
|
@ -14,18 +14,18 @@
|
|||
#include "routes.h"
|
||||
#include "../database/gravity-db.h"
|
||||
|
||||
static int get_list(struct mg_connection *conn,
|
||||
const int code,
|
||||
const enum gravity_list_type listtype,
|
||||
const char *filter)
|
||||
static int api_list_read(struct mg_connection *conn,
|
||||
const int code,
|
||||
const enum gravity_list_type listtype,
|
||||
const char *argument)
|
||||
{
|
||||
const char *sql_msg = NULL;
|
||||
if(!gravityDB_readTable(listtype, filter, &sql_msg))
|
||||
if(!gravityDB_readTable(listtype, argument, &sql_msg))
|
||||
{
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
|
||||
// Add filter (may be NULL = not available)
|
||||
JSON_OBJ_REF_STR(json, "filter", filter);
|
||||
// Add argument (may be NULL = not available)
|
||||
JSON_OBJ_REF_STR(json, "argument", argument);
|
||||
|
||||
// Add SQL message (may be NULL = not available)
|
||||
if (sql_msg != NULL) {
|
||||
|
@ -121,8 +121,8 @@ static int get_list(struct mg_connection *conn,
|
|||
JSON_DELETE(items);
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
|
||||
// Add filter (may be NULL = not available)
|
||||
JSON_OBJ_REF_STR(json, "filter", filter);
|
||||
// Add argument (may be NULL = not available)
|
||||
JSON_OBJ_REF_STR(json, "argument", argument);
|
||||
|
||||
// Add SQL message (may be NULL = not available)
|
||||
if (sql_msg != NULL) {
|
||||
|
@ -138,134 +138,85 @@ static int get_list(struct mg_connection *conn,
|
|||
}
|
||||
}
|
||||
|
||||
static int api_list_read(struct mg_connection *conn,
|
||||
const enum gravity_list_type listtype)
|
||||
{
|
||||
// Extract domain from path (option for GET)
|
||||
const struct mg_request_info *request = mg_get_request_info(conn);
|
||||
char domain_filter[1024] = { 0 };
|
||||
|
||||
// Advance one character to strip "/"
|
||||
const char *encoded_uri = strrchr(request->local_uri, '/')+1u;
|
||||
|
||||
// Decode URL (necessary for regular expressions, harmless for domains)
|
||||
if(strlen(encoded_uri) != 0 &&
|
||||
strcmp(encoded_uri, "exact") != 0 &&
|
||||
strcmp(encoded_uri, "regex") != 0 &&
|
||||
strcmp(encoded_uri, "allow") != 0 &&
|
||||
strcmp(encoded_uri, "deny") != 0 &&
|
||||
strcmp(encoded_uri, "list") != 0 &&
|
||||
strcmp(encoded_uri, "group") != 0 &&
|
||||
strcmp(encoded_uri, "adlist") != 0)
|
||||
mg_url_decode(encoded_uri, strlen(encoded_uri), domain_filter, sizeof(domain_filter), 0);
|
||||
|
||||
return get_list(conn, 200, listtype, domain_filter);
|
||||
}
|
||||
|
||||
static int api_list_write(struct mg_connection *conn,
|
||||
const enum gravity_list_type listtype,
|
||||
const enum http_method method)
|
||||
const enum http_method method,
|
||||
const char *argument)
|
||||
{
|
||||
tablerow row;
|
||||
bool need_domain = false, need_name = false, need_address = false;
|
||||
switch (listtype)
|
||||
{
|
||||
case GRAVITY_GROUPS:
|
||||
need_name = true;
|
||||
break;
|
||||
tablerow row = { 0 };
|
||||
|
||||
case GRAVITY_ADLISTS:
|
||||
need_address = true;
|
||||
break;
|
||||
|
||||
case GRAVITY_DOMAINLIST_ALLOW_EXACT:
|
||||
case GRAVITY_DOMAINLIST_DENY_EXACT:
|
||||
case GRAVITY_DOMAINLIST_ALLOW_REGEX:
|
||||
case GRAVITY_DOMAINLIST_DENY_REGEX:
|
||||
case GRAVITY_DOMAINLIST_ALLOW_ALL:
|
||||
case GRAVITY_DOMAINLIST_DENY_ALL:
|
||||
case GRAVITY_DOMAINLIST_ALL_EXACT:
|
||||
case GRAVITY_DOMAINLIST_ALL_REGEX:
|
||||
case GRAVITY_DOMAINLIST_ALL_ALL:
|
||||
need_domain = true;
|
||||
break;
|
||||
}
|
||||
// Set argument
|
||||
row.argument = argument;
|
||||
|
||||
// Extract payload
|
||||
char payload[1024] = { 0 };
|
||||
const char *argument = NULL;
|
||||
http_get_payload(conn, payload, sizeof(payload));
|
||||
|
||||
// Try to extract data from payload
|
||||
char domain[256] = { 0 };
|
||||
if(need_domain)
|
||||
{
|
||||
if(GET_VAR("domain", domain, payload) < 1)
|
||||
{
|
||||
return send_json_error(conn, 400,
|
||||
"bad_request",
|
||||
"No \"domain\" string in body data",
|
||||
NULL);
|
||||
}
|
||||
row.domain = domain;
|
||||
argument = domain;
|
||||
cJSON *obj = cJSON_Parse(payload);
|
||||
if (obj == NULL) {
|
||||
return send_json_error(conn, 400,
|
||||
"bad_request",
|
||||
"Invalid request body data",
|
||||
NULL);
|
||||
}
|
||||
|
||||
char name[256] = { 0 };
|
||||
if(need_name)
|
||||
{
|
||||
if(GET_VAR("name", name, payload) < 1)
|
||||
{
|
||||
return send_json_error(conn, 400,
|
||||
"bad_request",
|
||||
"No \"name\" string in body data",
|
||||
NULL);
|
||||
}
|
||||
row.name = name;
|
||||
argument = name;
|
||||
cJSON *json_enabled = cJSON_GetObjectItemCaseSensitive(obj, "enabled");
|
||||
if (!cJSON_IsBool(json_enabled)) {
|
||||
cJSON_Delete(obj);
|
||||
return send_json_error(conn, 400,
|
||||
"bad_request",
|
||||
"No \"enabled\" boolean in body data",
|
||||
NULL);
|
||||
}
|
||||
row.enabled = cJSON_IsTrue(json_enabled);
|
||||
|
||||
char address[256] = { 0 };
|
||||
if(need_address)
|
||||
{
|
||||
if(GET_VAR("address", address, payload) < 1)
|
||||
{
|
||||
return send_json_error(conn, 400,
|
||||
"bad_request",
|
||||
"No \"address\" string in body data",
|
||||
NULL);
|
||||
}
|
||||
row.address = address;
|
||||
argument = address;
|
||||
}
|
||||
|
||||
row.enabled = true;
|
||||
get_bool_var(payload, "enabled", &row.enabled);
|
||||
|
||||
char comment[256] = { 0 };
|
||||
if(GET_VAR("comment", comment, payload) > 0)
|
||||
row.comment = comment;
|
||||
cJSON *json_comment = cJSON_GetObjectItemCaseSensitive(obj, "comment");
|
||||
if(cJSON_IsString(json_comment))
|
||||
row.comment = json_comment->valuestring;
|
||||
else
|
||||
row.comment = NULL;
|
||||
|
||||
char description[256] = { 0 };
|
||||
if(GET_VAR("description", description, payload) > 0)
|
||||
row.description = description;
|
||||
cJSON *json_description = cJSON_GetObjectItemCaseSensitive(obj, "description");
|
||||
if(cJSON_IsString(json_description))
|
||||
row.description = json_description->valuestring;
|
||||
else
|
||||
row.description = NULL;
|
||||
|
||||
cJSON *json_name = cJSON_GetObjectItemCaseSensitive(obj, "name");
|
||||
if(cJSON_IsString(json_name))
|
||||
row.name = json_name->valuestring;
|
||||
else
|
||||
row.name = NULL;
|
||||
|
||||
cJSON *json_oldtype = cJSON_GetObjectItemCaseSensitive(obj, "oldtype");
|
||||
if(cJSON_IsString(json_oldtype))
|
||||
row.oldtype = json_oldtype->valuestring;
|
||||
else
|
||||
row.oldtype = NULL;
|
||||
|
||||
// Try to add domain to table
|
||||
const char *sql_msg = NULL;
|
||||
if(gravityDB_addToTable(listtype, row, &sql_msg, method))
|
||||
bool okay = false;
|
||||
if(gravityDB_addToTable(listtype, &row, &sql_msg, method))
|
||||
{
|
||||
// Send GET style reply with code 201 Created
|
||||
return get_list(conn, 201, listtype, argument);
|
||||
cJSON *groups = cJSON_GetObjectItemCaseSensitive(obj, "groups");
|
||||
if(groups != NULL)
|
||||
okay = gravityDB_edit_groups(listtype, groups, &row, &sql_msg);
|
||||
}
|
||||
else
|
||||
if(!okay)
|
||||
{
|
||||
// Error adding domain, prepare error object
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
JSON_OBJ_COPY_STR(json, "argument", argument);
|
||||
JSON_OBJ_REF_STR(json, "argument", argument);
|
||||
JSON_OBJ_ADD_BOOL(json, "enabled", row.enabled);
|
||||
if(row.comment != NULL)
|
||||
JSON_OBJ_REF_STR(json, "comment", row.comment);
|
||||
if(row.description != NULL)
|
||||
JSON_OBJ_REF_STR(json, "description", row.description);
|
||||
if(row.name != NULL)
|
||||
JSON_OBJ_REF_STR(json, "name", row.name);
|
||||
if(row.oldtype != NULL)
|
||||
JSON_OBJ_REF_STR(json, "oldtype", row.oldtype);
|
||||
|
||||
// Add SQL message (may be NULL = not available)
|
||||
if (sql_msg != NULL) {
|
||||
|
@ -274,25 +225,28 @@ static int api_list_write(struct mg_connection *conn,
|
|||
JSON_OBJ_ADD_NULL(json, "sql_msg");
|
||||
}
|
||||
|
||||
// Free memory not needed any longer
|
||||
cJSON_Delete(obj);
|
||||
|
||||
// Send error reply
|
||||
return send_json_error(conn, 400, // 400 Bad Request
|
||||
"database_error",
|
||||
"Could not add to gravity database",
|
||||
json);
|
||||
}
|
||||
// else: everything is okay
|
||||
|
||||
// Free memory not needed any longer
|
||||
cJSON_Delete(obj);
|
||||
|
||||
// Send GET style reply with code 201 Created
|
||||
return api_list_read(conn, 201, listtype, argument);
|
||||
}
|
||||
|
||||
static int api_list_remove(struct mg_connection *conn,
|
||||
const enum gravity_list_type listtype)
|
||||
const enum gravity_list_type listtype,
|
||||
const char *argument)
|
||||
{
|
||||
const struct mg_request_info *request = mg_get_request_info(conn);
|
||||
|
||||
char argument[1024] = { 0 };
|
||||
// Advance one character to strip "/"
|
||||
const char *encoded_uri = strrchr(request->local_uri, '/')+1u;
|
||||
// Decode URL (necessary for regular expressions, harmless for domains)
|
||||
mg_url_decode(encoded_uri, strlen(encoded_uri), argument, sizeof(argument)-1u, 0);
|
||||
|
||||
cJSON *json = JSON_NEW_OBJ();
|
||||
const char *sql_msg = NULL;
|
||||
if(gravityDB_delFromTable(listtype, argument, &sql_msg))
|
||||
|
@ -331,24 +285,25 @@ int api_list(struct mg_connection *conn)
|
|||
enum gravity_list_type listtype;
|
||||
bool can_modify = false;
|
||||
const struct mg_request_info *request = mg_get_request_info(conn);
|
||||
if(startsWith("/api/group", request->local_uri))
|
||||
const char *argument = NULL;
|
||||
if((argument = startsWith("/api/group", request->local_uri)) != NULL)
|
||||
{
|
||||
listtype = GRAVITY_GROUPS;
|
||||
can_modify = true;
|
||||
}
|
||||
else if(startsWith("/api/adlist", request->local_uri))
|
||||
else if((argument = startsWith("/api/adlist", request->local_uri)) != NULL)
|
||||
{
|
||||
listtype = GRAVITY_ADLISTS;
|
||||
can_modify = true;
|
||||
}
|
||||
else if(startsWith("/api/list/allow", request->local_uri))
|
||||
else if((argument = startsWith("/api/list/allow", request->local_uri)) != NULL)
|
||||
{
|
||||
if(startsWith("/api/list/allow/exact", request->local_uri))
|
||||
if((argument = startsWith("/api/list/allow/exact", request->local_uri)) != NULL)
|
||||
{
|
||||
listtype = GRAVITY_DOMAINLIST_ALLOW_EXACT;
|
||||
can_modify = true;
|
||||
}
|
||||
else if(startsWith("/api/list/allow/regex", request->local_uri))
|
||||
else if((argument = startsWith("/api/list/allow/regex", request->local_uri)) != NULL)
|
||||
{
|
||||
listtype = GRAVITY_DOMAINLIST_ALLOW_REGEX;
|
||||
can_modify = true;
|
||||
|
@ -356,14 +311,14 @@ int api_list(struct mg_connection *conn)
|
|||
else
|
||||
listtype = GRAVITY_DOMAINLIST_ALLOW_ALL;
|
||||
}
|
||||
else if(startsWith("/api/list/deny", request->local_uri))
|
||||
else if((argument = startsWith("/api/list/deny", request->local_uri)) != NULL)
|
||||
{
|
||||
if(startsWith("/api/list/deny/exact", request->local_uri))
|
||||
if((argument = startsWith("/api/list/deny/exact", request->local_uri)) != NULL)
|
||||
{
|
||||
listtype = GRAVITY_DOMAINLIST_DENY_EXACT;
|
||||
can_modify = true;
|
||||
}
|
||||
else if(startsWith("/api/list/deny/regex", request->local_uri))
|
||||
else if((argument = startsWith("/api/list/deny/regex", request->local_uri)) != NULL)
|
||||
{
|
||||
listtype = GRAVITY_DOMAINLIST_DENY_REGEX;
|
||||
can_modify = true;
|
||||
|
@ -373,28 +328,31 @@ int api_list(struct mg_connection *conn)
|
|||
}
|
||||
else
|
||||
{
|
||||
if(startsWith("/api/list/exact", request->local_uri))
|
||||
if((argument = startsWith("/api/list/exact", request->local_uri)) != NULL)
|
||||
listtype = GRAVITY_DOMAINLIST_ALL_EXACT;
|
||||
else if(startsWith("/api/list/regex", request->local_uri))
|
||||
else if((argument = startsWith("/api/list/regex", request->local_uri)) != NULL)
|
||||
listtype = GRAVITY_DOMAINLIST_ALL_REGEX;
|
||||
else
|
||||
{
|
||||
argument = startsWith("/api/list", request->local_uri);
|
||||
listtype = GRAVITY_DOMAINLIST_ALL_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
const enum http_method method = http_method(conn);
|
||||
if(method == HTTP_GET)
|
||||
{
|
||||
return api_list_read(conn, listtype);
|
||||
return api_list_read(conn, 200, listtype, argument);
|
||||
}
|
||||
else if(can_modify && (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH))
|
||||
{
|
||||
// Add item from list
|
||||
return api_list_write(conn, listtype, method);
|
||||
return api_list_write(conn, listtype, method, argument);
|
||||
}
|
||||
else if(can_modify && method == HTTP_DELETE)
|
||||
{
|
||||
// Delete item from list
|
||||
return api_list_remove(conn, listtype);
|
||||
return api_list_remove(conn, listtype, argument);
|
||||
}
|
||||
else if(!can_modify)
|
||||
{
|
||||
|
|
|
@ -1364,7 +1364,7 @@ bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int n
|
|||
return true;
|
||||
}
|
||||
|
||||
bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow row,
|
||||
bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
|
||||
const char **message, const enum http_method method)
|
||||
{
|
||||
if(gravity_db == NULL)
|
||||
|
@ -1391,7 +1391,8 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow
|
|||
|
||||
case GRAVITY_GROUPS:
|
||||
case GRAVITY_ADLISTS:
|
||||
// No type required for this table
|
||||
case GRAVITY_CLIENTS:
|
||||
// No type required for these tables
|
||||
break;
|
||||
|
||||
// Aggregate types cannot be handled by this routine
|
||||
|
@ -1403,6 +1404,7 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow
|
|||
default:
|
||||
return false;
|
||||
}
|
||||
row->type_int = type;
|
||||
|
||||
// Prepare SQLite statement
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
|
@ -1410,58 +1412,46 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow
|
|||
if(method == HTTP_POST) // Create NEW entry, error if existing
|
||||
{
|
||||
if(listtype == GRAVITY_GROUPS)
|
||||
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:name,:enabled,:description);";
|
||||
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:argument,:enabled,:description);";
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
querystr = "INSERT INTO adlist (address,enabled,description) VALUES (:address,:enabled,:description);";
|
||||
querystr = "INSERT INTO adlist (address,enabled,description) VALUES (:argument,:enabled,:description);";
|
||||
else // domainlist
|
||||
querystr = "INSERT INTO domainlist (domain,type,enabled,comment) VALUES (:domain,:type,:enabled,:comment);";
|
||||
querystr = "INSERT INTO domainlist (domain,type,enabled,comment) VALUES (:argument,:type,:enabled,:comment);";
|
||||
}
|
||||
else // Create new or replace existing entry, no error if existing
|
||||
// We have to use a subquery here to avoid violating FOREIGN KEY
|
||||
// contraints (REPLACE recreates (= new ID) entries instead of updating them)
|
||||
if(listtype == GRAVITY_GROUPS)
|
||||
querystr = "REPLACE INTO \"group\" (name,enabled,description,id,date_added) "
|
||||
"VALUES (:name,:enabled,:description,"
|
||||
"(SELECT id FROM \"group\" WHERE name = :name),"
|
||||
"(SELECT date_added FROM \"group\" WHERE name = :name));";
|
||||
"VALUES (:argument,:enabled,:description,"
|
||||
"(SELECT id FROM \"group\" WHERE name = :argument),"
|
||||
"(SELECT date_added FROM \"group\" WHERE name = :argument));";
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
querystr = "REPLACE INTO adlist (address,enabled,description,id,date_added) "
|
||||
"VALUES (:address,:enabled,:description,"
|
||||
"(SELECT id FROM adlist WHERE address = :address),"
|
||||
"(SELECT date_added FROM adlist WHERE address = :address));";
|
||||
"VALUES (:argument,:enabled,:description,"
|
||||
"(SELECT id FROM adlist WHERE address = :argument),"
|
||||
"(SELECT date_added FROM adlist WHERE address = :argument));";
|
||||
else // domainlist
|
||||
querystr = "REPLACE INTO domainlist (domain,type,enabled,comment,id,date_added) "
|
||||
"VALUES (:domain,:type,:enabled,:comment,"
|
||||
"(SELECT id FROM domainlist WHERE domain = :domain and type = :type),"
|
||||
"(SELECT date_added FROM domainlist WHERE domain = :domain and type = :type));";
|
||||
"VALUES (:argument,:type,:enabled,:comment,"
|
||||
"(SELECT id FROM domainlist WHERE domain = :argument and type = :oldtype),"
|
||||
"(SELECT date_added FROM domainlist WHERE domain = :argument and type = :oldtype));";
|
||||
int rc = sqlite3_prepare_v2(gravity_db, querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s) - SQL error prepare (%i): %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
logg("gravityDB_addToTable(%d, %s) - SQL error prepare (%i): %s",
|
||||
type, row->domain, rc, *message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind domain to prepared statement (if requested)
|
||||
int idx = sqlite3_bind_parameter_index(stmt, ":domain");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row.domain, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
// Bind argument to prepared statement (if requested)
|
||||
int idx = sqlite3_bind_parameter_index(stmt, ":argument");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row->argument, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s): Failed to bind domain (error %d) - %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind name to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":name");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row.name, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s): Failed to bind name (error %d) - %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
logg("gravityDB_addToTable(%d, %s): Failed to bind argument (error %d) - %s",
|
||||
type, row->argument, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
|
@ -1472,20 +1462,62 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow
|
|||
if(idx > 0 && (rc = sqlite3_bind_int(stmt, idx, type)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s): Failed to bind type (error %d) - %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
logg("gravityDB_addToTable(%d, %s): Failed to bind type (error %d) - %s",
|
||||
type, row->domain, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind oldtype to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":oldtype");
|
||||
if(idx > 0)
|
||||
{
|
||||
if(row->oldtype == NULL)
|
||||
{
|
||||
*message = "Field oldtype missing from request.";
|
||||
logg("gravityDB_addToTable(%d, %s): Oldtype missing",
|
||||
type, row->domain);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
int oldtype = -1;
|
||||
if(strcasecmp("allow/exact", row->oldtype) == 0)
|
||||
oldtype = 0;
|
||||
else if(strcasecmp("deny/exact", row->oldtype) == 0)
|
||||
oldtype = 1;
|
||||
else if(strcasecmp("allow/regex", row->oldtype) == 0)
|
||||
oldtype = 2;
|
||||
else if(strcasecmp("deny/regex", row->oldtype) == 0)
|
||||
oldtype = 3;
|
||||
else
|
||||
{
|
||||
*message = "Cannot interpret oldtype field.";
|
||||
logg("gravityDB_addToTable(%d, %s): Failed to identify oldtype \"%s\"",
|
||||
type, row->domain, row->oldtype);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
if((rc = sqlite3_bind_int(stmt, idx, oldtype)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s): Failed to bind oldtype (error %d) - %s",
|
||||
type, row->domain, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Bind enabled boolean to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":enabled");
|
||||
if(idx > 0 && (rc = sqlite3_bind_int(stmt, idx, row.enabled ? 1 : 0)) != SQLITE_OK)
|
||||
if(idx > 0 && (rc = sqlite3_bind_int(stmt, idx, row->enabled ? 1 : 0)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s): Failed to bind enabled (error %d) - %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
logg("gravityDB_addToTable(%d, %s): Failed to bind enabled (error %d) - %s",
|
||||
type, row->domain, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
|
@ -1493,11 +1525,11 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow
|
|||
|
||||
// Bind comment string to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":comment");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row.comment, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row->comment, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s): Failed to bind comment (error %d) - %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
logg("gravityDB_addToTable(%d, %s): Failed to bind comment (error %d) - %s",
|
||||
type, row->domain, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
|
@ -1505,23 +1537,11 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow
|
|||
|
||||
// Bind description string to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":description");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row.description, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row->description, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s): Failed to bind description (error %d) - %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind address string to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":address");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row.address, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_addToTable(%d, %s, %s): Failed to bind address (error %d) - %s",
|
||||
type, row.domain, row.name, rc, *message);
|
||||
logg("gravityDB_addToTable(%d, %s): Failed to bind description (error %d) - %s",
|
||||
type, row->domain, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
|
@ -1572,7 +1592,8 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const char* a
|
|||
|
||||
case GRAVITY_GROUPS:
|
||||
case GRAVITY_ADLISTS:
|
||||
// No type required for this table
|
||||
case GRAVITY_CLIENTS:
|
||||
// No type required for these tables
|
||||
break;
|
||||
|
||||
// Aggregate types cannot be handled by this routine
|
||||
|
@ -1627,6 +1648,14 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const char* a
|
|||
return false;
|
||||
}
|
||||
|
||||
// Debug output
|
||||
if(config.debug & DEBUG_API)
|
||||
{
|
||||
logg("SQL: %s", querystr);
|
||||
logg(" :argument = \"%s\"", argument);
|
||||
logg(" :type = \"%i\"", type);
|
||||
}
|
||||
|
||||
// Perform step
|
||||
bool okay = false;
|
||||
if((rc = sqlite3_step(stmt)) == SQLITE_DONE)
|
||||
|
@ -1647,7 +1676,7 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const char* a
|
|||
}
|
||||
|
||||
static sqlite3_stmt* read_stmt = NULL;
|
||||
bool gravityDB_readTable(const enum gravity_list_type listtype, const char *filter, const char **message)
|
||||
bool gravityDB_readTable(const enum gravity_list_type listtype, const char *argument, const char **message)
|
||||
{
|
||||
if(gravity_db == NULL)
|
||||
{
|
||||
|
@ -1688,7 +1717,8 @@ bool gravityDB_readTable(const enum gravity_list_type listtype, const char *filt
|
|||
break;
|
||||
case GRAVITY_GROUPS:
|
||||
case GRAVITY_ADLISTS:
|
||||
// No type required for this table
|
||||
case GRAVITY_CLIENTS:
|
||||
// No type required for these tables
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1699,20 +1729,20 @@ bool gravityDB_readTable(const enum gravity_list_type listtype, const char *filt
|
|||
const char *extra = "";
|
||||
if(listtype == GRAVITY_GROUPS)
|
||||
{
|
||||
if(filter[0] != '\0')
|
||||
extra = " WHERE name = :filter;";
|
||||
if(argument != NULL && argument[0] != '\0')
|
||||
extra = " WHERE name = :argument;";
|
||||
sprintf(querystr, "SELECT id,name,enabled,date_added,date_modified,description FROM \"group\"%s;", extra);
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
{
|
||||
if(filter[0] != '\0')
|
||||
extra = " WHERE address = :filter;";
|
||||
if(argument != NULL && argument[0] != '\0')
|
||||
extra = " WHERE address = :argument;";
|
||||
sprintf(querystr, "SELECT id,address,enabled,date_added,date_modified,comment FROM adlist%s;", extra);
|
||||
}
|
||||
else // domainlist
|
||||
{
|
||||
if(filter[0] != '\0')
|
||||
extra = " AND domain = :filter;";
|
||||
if(argument != NULL && argument[0] != '\0')
|
||||
extra = " AND domain = :argument;";
|
||||
sprintf(querystr, "SELECT id,type,domain,enabled,date_added,date_modified,comment,"
|
||||
"(SELECT GROUP_CONCAT(group_id) FROM domainlist_by_group g WHERE g.domainlist_id = d.id) AS group_ids "
|
||||
"FROM domainlist d WHERE d.type IN (%s)%s;", type, extra);
|
||||
|
@ -1727,18 +1757,25 @@ bool gravityDB_readTable(const enum gravity_list_type listtype, const char *filt
|
|||
return false;
|
||||
}
|
||||
|
||||
// Bind filter to prepared statement (if requested)
|
||||
int idx = sqlite3_bind_parameter_index(read_stmt, "filter");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(read_stmt, idx, filter, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
// Bind argument to prepared statement (if requested)
|
||||
int idx = sqlite3_bind_parameter_index(read_stmt, ":argument");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(read_stmt, idx, argument, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_readTable(%d => (%s), %s): Failed to bind filter (error %d) - %s",
|
||||
listtype, type, filter, rc, *message);
|
||||
logg("gravityDB_readTable(%d => (%s), %s): Failed to bind argument (error %d) - %s",
|
||||
listtype, type, argument, rc, *message);
|
||||
sqlite3_reset(read_stmt);
|
||||
sqlite3_finalize(read_stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Debug output
|
||||
if(config.debug & DEBUG_API)
|
||||
{
|
||||
logg("SQL: %s", querystr);
|
||||
logg(" :argument = \"%s\"", argument);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1832,3 +1869,220 @@ void gravityDB_readTableFinalize(void)
|
|||
// Finalize statement
|
||||
sqlite3_finalize(read_stmt);
|
||||
}
|
||||
|
||||
bool gravityDB_edit_groups(const enum gravity_list_type listtype, cJSON *groups,
|
||||
const tablerow *row, const char **message)
|
||||
{
|
||||
if(gravity_db == NULL)
|
||||
{
|
||||
*message = "Database not available";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare SQLite statements
|
||||
const char *get_querystr, *del_querystr, *add_querystr;
|
||||
if(listtype == GRAVITY_GROUPS)
|
||||
return false;
|
||||
else if(listtype == GRAVITY_CLIENTS)
|
||||
{
|
||||
get_querystr = "SELECT id FROM client WHERE name = :argument";
|
||||
del_querystr = "DELETE FROM client_by_group WHERE client_id = :id);";
|
||||
add_querystr = "INSERT INTO client_by_group (client_id,group_id) VALUES (:id,:gid);";
|
||||
}
|
||||
else if(listtype == GRAVITY_ADLISTS)
|
||||
{
|
||||
get_querystr = "SELECT id FROM adlist WHERE address = :argument";
|
||||
del_querystr = "DELETE FROM adlist_by_group WHERE adlist_id = :id;";
|
||||
add_querystr = "INSERT INTO adlist_by_group (adlist_id,group_id) VALUES (:id,:gid);";
|
||||
}
|
||||
else // domainlist
|
||||
{
|
||||
get_querystr = "SELECT id FROM domainlist WHERE domain = :argument AND type = :type";
|
||||
del_querystr = "DELETE FROM domainlist_by_group WHERE domainlist_id = :id;";
|
||||
add_querystr = "INSERT INTO domainlist_by_group (domainlist_id,group_id) VALUES (:id,:gid);";
|
||||
}
|
||||
|
||||
// First step: Get ID of the item to modify
|
||||
sqlite3_stmt* stmt = NULL;
|
||||
int rc = sqlite3_prepare_v2(gravity_db, get_querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d) - SQL error prepare SELECT (%i): %s",
|
||||
listtype, rc, *message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind argument string to prepared statement (if requested)
|
||||
int idx = sqlite3_bind_parameter_index(stmt, ":argument");
|
||||
if(idx > 0 && (rc = sqlite3_bind_text(stmt, idx, row->argument, -1, SQLITE_STATIC)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d): Failed to bind argument SELECT (error %d) - %s",
|
||||
listtype, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind type to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":type");
|
||||
if(idx > 0 && (rc = sqlite3_bind_int(stmt, idx, row->type_int)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d): Failed to bind type SELECT (error %d) - %s",
|
||||
listtype, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform step
|
||||
bool okay = false;
|
||||
int id = -1;
|
||||
if((rc = sqlite3_step(stmt)) == SQLITE_ROW)
|
||||
{
|
||||
// Get ID of domain
|
||||
id = sqlite3_column_int(stmt, 0);
|
||||
okay = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
}
|
||||
logg("SELECT: %i -> %i", rc, id);
|
||||
|
||||
// Debug output
|
||||
if(config.debug & DEBUG_API)
|
||||
{
|
||||
logg("SQL: %s", get_querystr);
|
||||
logg(" :argument = \"%s\"", row->argument);
|
||||
logg(" :type = \"%d\"", row->type_int);
|
||||
}
|
||||
|
||||
// Finalize statement
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Return early if getting the ID failed
|
||||
if(!okay)
|
||||
return false;
|
||||
|
||||
// Second step: Delete all existing group associations for this item
|
||||
rc = sqlite3_prepare_v2(gravity_db, del_querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d) - SQL error prepare DELETE (%i): %s",
|
||||
listtype, rc, *message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind id to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":id");
|
||||
if(idx > 0 && (rc = sqlite3_bind_int(stmt, idx, id)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d): Failed to bind id DELETE (error %d) - %s",
|
||||
listtype, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform step
|
||||
if((rc = sqlite3_step(stmt)) == SQLITE_DONE)
|
||||
{
|
||||
// All groups deleted
|
||||
}
|
||||
else
|
||||
{
|
||||
okay = false;
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
}
|
||||
logg("DELETE: %i", rc);
|
||||
|
||||
// Debug output
|
||||
if(config.debug & DEBUG_API)
|
||||
{
|
||||
logg("SQL: %s", del_querystr);
|
||||
logg(" :id = \"%d\"", id);
|
||||
}
|
||||
|
||||
// Finalize statement
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Return early if deleting the existing group associations failed
|
||||
if(!okay)
|
||||
return false;
|
||||
|
||||
// Third step: Create new group associations for this item
|
||||
rc = sqlite3_prepare_v2(gravity_db, add_querystr, -1, &stmt, NULL);
|
||||
if( rc != SQLITE_OK )
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d) - SQL error prepare INSERT (%i): %s",
|
||||
listtype, rc, *message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bind id to prepared statement (if requested)
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":id");
|
||||
if(idx > 0 && (rc = sqlite3_bind_int(stmt, idx, id)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d): Failed to bind id INSERT (error %d) - %s",
|
||||
listtype, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loop over all loops in array
|
||||
const int groupcount = cJSON_GetArraySize(groups);
|
||||
logg("groupscount = %d", groupcount);
|
||||
for(int i = 0; i < groupcount; i++)
|
||||
{
|
||||
cJSON *group = cJSON_GetArrayItem(groups, i);
|
||||
if(group == NULL || !cJSON_IsNumber(group))
|
||||
continue;
|
||||
|
||||
idx = sqlite3_bind_parameter_index(stmt, ":gid");
|
||||
if(idx > 0 && (rc = sqlite3_bind_int(stmt, idx, group->valueint)) != SQLITE_OK)
|
||||
{
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
logg("gravityDB_edit_groups(%d): Failed to bind gid INSERT (error %d) - %s",
|
||||
listtype, rc, *message);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform step
|
||||
if((rc = sqlite3_step(stmt)) != SQLITE_DONE)
|
||||
{
|
||||
okay = false;
|
||||
*message = sqlite3_errmsg(gravity_db);
|
||||
break;
|
||||
}
|
||||
logg("INSERT: %i -> (%i,%i)", rc, id, group->valueint);
|
||||
|
||||
// Debug output
|
||||
if(config.debug & DEBUG_API)
|
||||
{
|
||||
logg("SQL: %s", add_querystr);
|
||||
logg(" :id = \"%d\"", id);
|
||||
logg(" :gid = \"%d\"", group->valueint);
|
||||
}
|
||||
|
||||
// Reset before next iteration, this will not clear the id binding
|
||||
sqlite3_reset(stmt);
|
||||
}
|
||||
|
||||
|
||||
// Finalize statement
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
return okay;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ typedef struct {
|
|||
const char *comment;
|
||||
const char *group_ids;
|
||||
const char *description;
|
||||
const char *argument;
|
||||
const char *oldtype;
|
||||
long id;
|
||||
time_t date_added;
|
||||
time_t date_modified;
|
||||
|
@ -56,8 +58,10 @@ bool gravityDB_get_regex_client_groups(clientsData* client, const unsigned int n
|
|||
bool gravityDB_readTable(const enum gravity_list_type listtype, const char *filter, const char **message);
|
||||
bool gravityDB_readTableGetRow(tablerow *row, const char **message);
|
||||
void gravityDB_readTableFinalize(void);
|
||||
bool gravityDB_addToTable(const enum gravity_list_type listtype, const tablerow row,
|
||||
bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
|
||||
const char **message, const enum http_method method);
|
||||
bool gravityDB_delFromTable(const enum gravity_list_type listtype, const char* domain_name, const char **message);
|
||||
bool gravityDB_edit_groups(const enum gravity_list_type listtype, cJSON *groups,
|
||||
const tablerow *row, const char **message);
|
||||
|
||||
#endif //GRAVITY_H
|
||||
|
|
|
@ -167,7 +167,8 @@ enum gravity_list_type {
|
|||
GRAVITY_DOMAINLIST_ALL_REGEX,
|
||||
GRAVITY_DOMAINLIST_ALL_ALL,
|
||||
GRAVITY_GROUPS,
|
||||
GRAVITY_ADLISTS
|
||||
GRAVITY_ADLISTS,
|
||||
GRAVITY_CLIENTS
|
||||
} __attribute__ ((packed));
|
||||
|
||||
enum gravity_tables {
|
||||
|
|
|
@ -164,9 +164,18 @@ bool http_get_payload(struct mg_connection *conn, char *payload, const size_t si
|
|||
return false;
|
||||
}
|
||||
|
||||
bool __attribute__((pure)) startsWith(const char *path, const char *uri)
|
||||
const char* __attribute__((pure)) startsWith(const char *path, const char *uri)
|
||||
{
|
||||
return strncmp(path, uri, strlen(path)) == 0;
|
||||
if(strncmp(path, uri, strlen(path)) == 0)
|
||||
if(uri[strlen(path)] == '/')
|
||||
// Path match with argument after ".../"
|
||||
return uri + strlen(path) + 1u;
|
||||
else
|
||||
// Path match without argument
|
||||
return "";
|
||||
else
|
||||
// Path does not match
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool http_get_cookie_int(struct mg_connection *conn, const char *cookieName, int *i)
|
||||
|
|
|
@ -49,6 +49,6 @@ enum http_method { HTTP_UNKNOWN, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP
|
|||
int http_method(struct mg_connection *conn);
|
||||
|
||||
// Utils
|
||||
bool startsWith(const char *path, const char *uri) __attribute__((pure));
|
||||
const char *startsWith(const char *path, const char *uri) __attribute__((pure));
|
||||
|
||||
#endif // HTTP_H
|
||||
|
|
Loading…
Reference in New Issue