Skip to content

Commit

Permalink
Allow narrowing down the (ad)list type using the (optional) query par…
Browse files Browse the repository at this point in the history
…ameter ?type={allow,block}

Signed-off-by: DL6ER <[email protected]>
  • Loading branch information
DL6ER committed Feb 13, 2024
1 parent 9e3ccd9 commit 7a919cb
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 36 deletions.
12 changes: 12 additions & 0 deletions src/api/docs/content/specs/lists.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ components:
summary: Modify list
parameters:
- $ref: 'lists.yaml#/components/parameters/list'
- $ref: 'lists.yaml#/components/parameters/listtype'
get:
summary: Get lists
tags:
Expand Down Expand Up @@ -449,3 +450,14 @@ components:
required: true
description: Address of the list
example: https://hosts-file.net/ad_servers.txt
listtype:
in: query
name: type
schema:
type: string
enum:
- "allow"
- "block"
required: false
description: Type of list, optional
example: block
61 changes: 52 additions & 9 deletions src/api/list.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ static int api_list_read(struct ftl_conn *api,
sql_msg);
}

tablerow table;
tablerow table = { 0 };
cJSON *rows = JSON_NEW_ARRAY();
while(gravityDB_readTableGetRow(listtype, &table, &sql_msg))
{
Expand All @@ -48,7 +48,9 @@ static int api_list_read(struct ftl_conn *api,
JSON_COPY_STR_TO_OBJECT(row, "name", table.name);
JSON_COPY_STR_TO_OBJECT(row, "comment", table.comment);
}
else if(listtype == GRAVITY_ADLISTS)
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
{
JSON_COPY_STR_TO_OBJECT(row, "address", table.address);
JSON_COPY_STR_TO_OBJECT(row, "comment", table.comment);
Expand Down Expand Up @@ -126,7 +128,9 @@ static int api_list_read(struct ftl_conn *api,
JSON_ADD_NUMBER_TO_OBJECT(row, "date_modified", table.date_modified);

// Properties added in https://github.com/pi-hole/pi-hole/pull/3951
if(listtype == GRAVITY_ADLISTS)
if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
{
JSON_REF_STR_IN_OBJECT(row, "type", table.type);
JSON_ADD_NUMBER_TO_OBJECT(row, "date_updated", table.date_updated);
Expand All @@ -147,7 +151,9 @@ static int api_list_read(struct ftl_conn *api,
cJSON *json = JSON_NEW_OBJECT();
if(listtype == GRAVITY_GROUPS)
objname = "groups";
else if(listtype == GRAVITY_ADLISTS)
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
objname = "lists";
else if(listtype == GRAVITY_CLIENTS)
objname = "clients";
Expand Down Expand Up @@ -268,6 +274,8 @@ static int api_list_write(struct ftl_conn *api,
}

case GRAVITY_ADLISTS:
case GRAVITY_ADLISTS_BLOCK:
case GRAVITY_ADLISTS_ALLOW:
{
cJSON *json_address = cJSON_GetObjectItemCaseSensitive(api->payload.json, "address");
if(cJSON_IsString(json_address) && strlen(json_address->valuestring) > 0)
Expand Down Expand Up @@ -331,6 +339,10 @@ static int api_list_write(struct ftl_conn *api,
NULL);
}
}
else if(listtype == GRAVITY_ADLISTS_BLOCK)
row.type_int = ADLIST_BLOCK;
else if(listtype == GRAVITY_ADLISTS_ALLOW)
row.type_int = ADLIST_ALLOW;
else
{
cJSON *json_type = cJSON_GetObjectItemCaseSensitive(api->payload.json, "type");
Expand Down Expand Up @@ -552,7 +564,9 @@ static int api_list_remove(struct ftl_conn *api,
if(listtype == GRAVITY_DOMAINLIST_ALLOW_EXACT ||
listtype == GRAVITY_DOMAINLIST_DENY_EXACT ||
listtype == GRAVITY_DOMAINLIST_ALLOW_REGEX ||
listtype == GRAVITY_DOMAINLIST_DENY_REGEX)
listtype == GRAVITY_DOMAINLIST_DENY_REGEX ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
{
int type = -1;
switch (listtype)
Expand All @@ -568,12 +582,17 @@ static int api_list_remove(struct ftl_conn *api,
break;
case GRAVITY_DOMAINLIST_DENY_REGEX:
type = 3;
break;
case GRAVITY_ADLISTS_BLOCK:
type = ADLIST_BLOCK;
break;
case GRAVITY_ADLISTS_ALLOW:
type = ADLIST_ALLOW;
break;
// Not handled herein
case GRAVITY_GROUPS:
case GRAVITY_ADLISTS:
case GRAVITY_CLIENTS:
// No type required for these tables
break;
// Aggregate types cannot be handled by this routine
case GRAVITY_GRAVITY:
case GRAVITY_ANTIGRAVITY:
case GRAVITY_DOMAINLIST_ALLOW_ALL:
Expand All @@ -582,7 +601,7 @@ static int api_list_remove(struct ftl_conn *api,
case GRAVITY_DOMAINLIST_ALL_REGEX:
case GRAVITY_DOMAINLIST_ALL_ALL:
default:
return false;
break;
}

// Create new JSON array with the item and type:
Expand Down Expand Up @@ -827,6 +846,30 @@ int api_list(struct ftl_conn *api)
api->request->local_uri_raw);
}

// If this is a request for a list, we check if there is a request
// parameter narrowing down which kind of list. If so, we modify the
// list type accordingly
if(listtype == GRAVITY_ADLISTS && api->request->query_string != NULL)
{
// Check if there is a type parameter
char typestr[16] = { 0 };
if(get_string_var(api->request->query_string, "type", typestr, sizeof(typestr)) > 0)
{
if(strcasecmp(typestr, "allow") == 0)
listtype = GRAVITY_ADLISTS_ALLOW;
else if(strcasecmp(typestr, "block") == 0)
listtype = GRAVITY_ADLISTS_BLOCK;
else
{
// Invalid type parameter
return send_json_error(api, 400,
"bad_request",
"Invalid request: Invalid type parameter (should be either \"allow\" or \"block\")",
api->request->query_string);
}
}
}

if(api->method == HTTP_GET)
{
// Read list item identified by URI (or read them all)
Expand Down
101 changes: 75 additions & 26 deletions src/database/gravity-db.c
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,8 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
// Nothing to be done for these tables
case GRAVITY_GROUPS:
case GRAVITY_ADLISTS:
case GRAVITY_ADLISTS_BLOCK:
case GRAVITY_ADLISTS_ALLOW:
case GRAVITY_CLIENTS:
break;

Expand All @@ -1597,15 +1599,17 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
{
querystr = "INSERT INTO \"group\" (name,enabled,description) VALUES (:item,:enabled,:comment);";
}
else if(listtype == GRAVITY_ADLISTS)
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
{
querystr = "INSERT INTO adlist (address,enabled,comment,type) VALUES (:item,:enabled,:comment,:type);";
}
else if(listtype == GRAVITY_CLIENTS)
{
querystr = "INSERT INTO client (ip,comment) VALUES (:item,:comment);";
}
else // domainlis
else // domainlist
{
querystr = "INSERT INTO domainlist (domain,type,enabled,comment) VALUES (:item,:type,:enabled,:comment);";
}
Expand All @@ -1625,9 +1629,11 @@ bool gravityDB_addToTable(const enum gravity_list_type listtype, tablerow *row,
querystr = "UPDATE \"group\" SET name = :name, enabled = :enabled, description = :comment "
"WHERE name = :item";
}
else if(listtype == GRAVITY_ADLISTS)
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
querystr = "INSERT INTO adlist (address,enabled,comment,type) VALUES (:item,:enabled,:comment,:type) "\
"ON CONFLICT(address) DO UPDATE SET enabled = :enabled, comment = :comment, type = :type;";
"ON CONFLICT(address,type) DO UPDATE SET enabled = :enabled, comment = :comment, type = :type;";
else if(listtype == GRAVITY_CLIENTS)
querystr = "INSERT INTO client (ip,comment) VALUES (:item,:comment) "\
"ON CONFLICT(ip) DO UPDATE SET comment = :comment;";
Expand Down Expand Up @@ -1825,11 +1831,14 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
return false;
}

const bool isDomain = listtype == GRAVITY_DOMAINLIST_ALLOW_EXACT ||
listtype == GRAVITY_DOMAINLIST_DENY_EXACT ||
listtype == GRAVITY_DOMAINLIST_ALLOW_REGEX ||
listtype == GRAVITY_DOMAINLIST_DENY_REGEX ||
listtype == GRAVITY_DOMAINLIST_ALL_ALL; // batch delete
const bool hasType = listtype == GRAVITY_DOMAINLIST_ALLOW_EXACT ||
listtype == GRAVITY_DOMAINLIST_DENY_EXACT ||
listtype == GRAVITY_DOMAINLIST_ALLOW_REGEX ||
listtype == GRAVITY_DOMAINLIST_DENY_REGEX ||
listtype == GRAVITY_DOMAINLIST_ALL_ALL ||
listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW;

// Begin transaction
const char *querystr = "BEGIN TRANSACTION;";
Expand All @@ -1843,7 +1852,7 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
}

// Create temporary table for JSON argument
if(isDomain)
if(hasType)
// Create temporary table for domains to be deleted
querystr = "CREATE TEMPORARY TABLE deltable (type INT, item TEXT);";
else
Expand Down Expand Up @@ -1885,7 +1894,7 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
sqlite3_finalize(stmt);

// Prepare statement for inserting items into virtual table
if(isDomain)
if(hasType)
querystr = "INSERT INTO deltable (type, item) VALUES (:type, :item);";
else
querystr = "INSERT INTO deltable (item) VALUES (:item);";
Expand All @@ -1910,12 +1919,24 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
{
// Bind type to prepared statement
cJSON *type = cJSON_GetObjectItemCaseSensitive(it, "type");
int type_int = cJSON_IsNumber(type) ? type->valueint : -1;
if(listtype == GRAVITY_ADLISTS_BLOCK)
type_int = ADLIST_BLOCK;
else if(listtype == GRAVITY_ADLISTS_ALLOW)
type_int = ADLIST_ALLOW;
else if(listtype == GRAVITY_ADLISTS && cJSON_IsString(type))
{
if(strcasecmp(type->valuestring, "block") == 0)
type_int = ADLIST_BLOCK;
else if(strcasecmp(type->valuestring, "allow") == 0)
type_int = ADLIST_ALLOW;
}
const int type_idx = sqlite3_bind_parameter_index(stmt, ":type");
if(type_idx > 0 && (!cJSON_IsNumber(type) || (rc = sqlite3_bind_int(stmt, type_idx, type->valueint)) != SQLITE_OK))
if(type_idx > 0 && (rc = sqlite3_bind_int(stmt, type_idx, type_int)) != SQLITE_OK)
{
*message = sqlite3_errmsg(gravity_db);
log_err("gravityDB_delFromTable(%d): Failed to bind type (error %d) - %s",
type->valueint, rc, *message);
type_int, rc, *message);
sqlite3_reset(stmt);
sqlite3_finalize(stmt);

Expand Down Expand Up @@ -1981,12 +2002,15 @@ bool gravityDB_delFromTable(const enum gravity_list_type listtype, const cJSON*
const char *querystrs[4] = {NULL, NULL, NULL, NULL};
if(listtype == GRAVITY_GROUPS)
querystrs[0] = "DELETE FROM \"group\" WHERE name IN (SELECT item FROM deltable);";
else if(listtype == GRAVITY_ADLISTS)
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
{
// This is actually a three-step deletion to satisfy foreign-key constraints
querystrs[0] = "DELETE FROM gravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
querystrs[1] = "DELETE FROM antigravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable));";
querystrs[2] = "DELETE FROM adlist WHERE address IN (SELECT item FROM deltable);";
// This is actually a four-step deletion to satisfy foreign-key constraints
querystrs[0] = "DELETE FROM gravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 0));";
querystrs[1] = "DELETE FROM antigravity WHERE adlist_id IN (SELECT id FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 1));";
querystrs[2] = "DELETE FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 0) AND type = 0;";
querystrs[3] = "DELETE FROM adlist WHERE address IN (SELECT item FROM deltable WHERE type = 1) AND type = 1;";
}
else if(listtype == GRAVITY_CLIENTS)
querystrs[0] = "DELETE FROM client WHERE ip IN (SELECT item FROM deltable);";
Expand Down Expand Up @@ -2096,12 +2120,16 @@ bool gravityDB_readTable(const enum gravity_list_type listtype,
case GRAVITY_DOMAINLIST_ALL_ALL:
type = "0,1,2,3";
break;

// No type required for these tables
case GRAVITY_GRAVITY:
case GRAVITY_ANTIGRAVITY:
case GRAVITY_GROUPS:
case GRAVITY_ADLISTS:
case GRAVITY_CLIENTS:
// No type required for these tables
case GRAVITY_ADLISTS:
// Type is set in the SQL query directly
case GRAVITY_ADLISTS_BLOCK:
case GRAVITY_ADLISTS_ALLOW:
break;
}

Expand Down Expand Up @@ -2133,19 +2161,29 @@ bool gravityDB_readTable(const enum gravity_list_type listtype,
}
snprintf(querystr, buflen, "SELECT id,name,enabled,date_added,date_modified,description AS comment FROM \"group\"%s;", filter);
}
else if(listtype == GRAVITY_ADLISTS)
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
{
if(listtype == GRAVITY_ADLISTS_BLOCK)
filter = "type = 0";
else if(listtype == GRAVITY_ADLISTS_ALLOW)
filter = "type = 1";
else
filter = "TRUE";

const char *filter2 = "";
if(item != NULL && item[0] != '\0')
{
if(exact)
filter = " WHERE address = :item";
filter2 = " AND address = :item";
else
filter = " WHERE address LIKE :item";
filter2 = " AND address LIKE :item";
}
snprintf(querystr, buflen, "SELECT id,type,address,enabled,date_added,date_modified,comment,"
"(SELECT GROUP_CONCAT(group_id) FROM adlist_by_group g WHERE g.adlist_id = a.id) AS group_ids,"
"date_updated,number,invalid_domains,status,abp_entries "
"FROM adlist a%s;", filter);
"FROM adlist a WHERE %s%s;", filter, filter2);
}
else if(listtype == GRAVITY_CLIENTS)
{
Expand Down Expand Up @@ -2312,6 +2350,8 @@ bool gravityDB_readTableGetRow(const enum gravity_list_type listtype, tablerow *
}
}
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_ALLOW ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_GRAVITY ||
listtype == GRAVITY_ANTIGRAVITY)
{
Expand All @@ -2328,6 +2368,10 @@ bool gravityDB_readTableGetRow(const enum gravity_list_type listtype, tablerow *
break;
}
}
else
{
row->type = "unknown";
}
}

else if(strcasecmp(cname, "domain") == 0)
Expand Down Expand Up @@ -2425,9 +2469,14 @@ bool gravityDB_edit_groups(const enum gravity_list_type listtype, cJSON *groups,
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)
else if(listtype == GRAVITY_ADLISTS ||
listtype == GRAVITY_ADLISTS_BLOCK ||
listtype == GRAVITY_ADLISTS_ALLOW)
{
get_querystr = "SELECT id FROM adlist WHERE address = :item";
if(listtype == GRAVITY_ADLISTS)
get_querystr = "SELECT id FROM adlist WHERE address = :item";
else
get_querystr = "SELECT id FROM adlist WHERE address = :item AND type = :type";
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);";
}
Expand Down
4 changes: 3 additions & 1 deletion src/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,9 @@ enum gravity_list_type {
GRAVITY_ADLISTS,
GRAVITY_CLIENTS,
GRAVITY_GRAVITY,
GRAVITY_ANTIGRAVITY
GRAVITY_ANTIGRAVITY,
GRAVITY_ADLISTS_BLOCK,
GRAVITY_ADLISTS_ALLOW
} __attribute__ ((packed));

enum gravity_tables {
Expand Down
Loading

0 comments on commit 7a919cb

Please sign in to comment.