From 0e924fd91042aec53d34b2786de82e78a76bade8 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 8 Nov 2020 18:51:49 +0100 Subject: [PATCH 1/7] Add new replace_domain action in groups.php to allow adding domain(s) exclusively to a specific list. In this mode, any occurrences of said domain are first removed from the list before adding the new ones. Signed-off-by: DL6ER --- scripts/pi-hole/js/db_queries.js | 2 +- scripts/pi-hole/js/queries.js | 2 +- scripts/pi-hole/php/groups.php | 24 +++++++++++++++++++++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index 9c33bd18..0c3d2f21 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -99,7 +99,7 @@ function add(domain, list) { domain: domain, list: list, token: token, - action: "add_domain", + action: "replace_domain", comment: "Added from Long-Term-Data Query Log" }, success: function (response) { diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 092dd610..681bf0fe 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -56,7 +56,7 @@ function add(domain, list) { domain: domain, list: list, token: token, - action: "add_domain", + action: "replace_domain", comment: "Added from Query Log" }, success: function (response) { diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 4c69c456..b594f565 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -497,7 +497,7 @@ if ($_POST['action'] == 'get_groups') { } catch (\Exception $ex) { JSON_error($ex->getMessage()); } -} elseif ($_POST['action'] == 'add_domain') { +} elseif ($_POST['action'] == 'add_domain' || $_POST['action'] == 'replace_domain') { // Add new domain try { $domains = explode(' ', html_entity_decode(trim($_POST['domain']))); @@ -509,6 +509,14 @@ if ($_POST['action'] == 'get_groups') { throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); } + $delstmt = null; + if($_POST['action'] == 'replace_domain') { + $delstmt = $db->prepare('DELETE FROM domainlist WHERE domain = :domain'); + if (!$delstmt) { + throw new Exception('While preparing delete statement: ' . $db->lastErrorMsg()); + } + } + if (isset($_POST['type'])) { $type = intval($_POST['type']); } else if (isset($_POST['list']) && $_POST['list'] === "white") { @@ -571,6 +579,20 @@ if ($_POST['action'] == 'get_groups') { } } + // First try to delete any occurrences of this domain if we're in replace mode + if($_POST['action'] == 'replace_domain') { + if (!$delstmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { + throw new Exception('While binding domain: ' . $db->lastErrorMsg() . '
'. + 'Added ' . $added . " out of ". $total . " domains"); + } + + if (!$delstmt->execute()) { + throw new Exception('While executing: ' . $db->lastErrorMsg() . '
'. + 'Added ' . $added . " out of ". $total . " domains"); + } + } + + // Add domain with specific type and comment (both were already bound above) if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { throw new Exception('While binding domain: ' . $db->lastErrorMsg() . '
'. 'Added ' . $added . " out of ". $total . " domains"); From f5a39ce1e411dd43bed3c39fdb1bd97defc0ac88 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 8 Nov 2020 22:52:03 +0100 Subject: [PATCH 2/7] Check if replacing the domain messes with existing special groups settings. Don't change it but show a warning if this is the case. Signed-off-by: DL6ER --- scripts/pi-hole/php/groups.php | 81 +++++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 15 deletions(-) diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index b594f565..f0778a99 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -504,15 +504,30 @@ if ($_POST['action'] == 'get_groups') { $before = intval($db->querySingle("SELECT COUNT(*) FROM domainlist;")); $total = count($domains); $added = 0; - $stmt = $db->prepare('REPLACE INTO domainlist (domain,type,comment) VALUES (:domain,:type,:comment)'); - if (!$stmt) { + + // Prepare INSERT INTO statement + $insert_stmt = $db->prepare('REPLACE INTO domainlist (domain,type) VALUES (:domain,:type)'); + if (!$insert_stmt) { throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); } - $delstmt = null; + // Prepare UPDATE statement + $update_stmt = $db->prepare('UPDATE domainlist SET comment = :comment WHERE domain = :domain AND type = :type'); + if (!$update_stmt) { + throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); + } + + $check_stmt = null; + $delete_stmt = null; if($_POST['action'] == 'replace_domain') { - $delstmt = $db->prepare('DELETE FROM domainlist WHERE domain = :domain'); - if (!$delstmt) { + // Check statement will reveal any group associations for a given (domain,type) which do NOT belong to the default group + $check_stmt = $db->prepare('SELECT EXISTS(SELECT * FROM domainlist_by_group WHERE domainlist_id = (SELECT id FROM domainlist WHERE domain = :domain) AND group_id != 0)'); + if (!$check_stmt) { + throw new Exception('While preparing check statement: ' . $db->lastErrorMsg()); + } + // Delete statement will remove this domain from any type of list + $delete_stmt = $db->prepare('DELETE FROM domainlist WHERE domain = :domain'); + if (!$delete_stmt) { throw new Exception('While preparing delete statement: ' . $db->lastErrorMsg()); } } @@ -525,7 +540,8 @@ if ($_POST['action'] == 'get_groups') { $type = ListType::blacklist; } - if (!$stmt->bindValue(':type', $type, SQLITE3_TEXT)) { + if (!$insert_stmt->bindValue(':type', $type, SQLITE3_TEXT) || + !$update_stmt->bindValue(':type', $type, SQLITE3_TEXT)) { throw new Exception('While binding type: ' . $db->lastErrorMsg()); } @@ -534,7 +550,7 @@ if ($_POST['action'] == 'get_groups') { // Store NULL in database for empty comments $comment = null; } - if (!$stmt->bindValue(':comment', $comment, SQLITE3_TEXT)) { + if (!$update_stmt->bindValue(':comment', $comment, SQLITE3_TEXT)) { throw new Exception('While binding comment: ' . $db->lastErrorMsg()); } @@ -556,7 +572,7 @@ if ($_POST['action'] == 'get_groups') { } } - if(strlen($_POST['type']) === 2 && $_POST['type'][1] === 'W') + if(isset($_POST['type']) && strlen($_POST['type']) === 2 && $_POST['type'][1] === 'W') { // Apply wildcard-style formatting $domain = "(\\.|^)".str_replace(".","\\.",$domain)."$"; @@ -579,27 +595,62 @@ if ($_POST['action'] == 'get_groups') { } } - // First try to delete any occurrences of this domain if we're in replace mode + // First try to delete any occurrences of this domain if we're in + // replace mode. Only do this when the domain to be replaced is in + // the default group! Otherwise, we would shuffle group settings and + // just throw an error at the user to tell them to change this + // domain manually. This ensures user's will really get what they + // want from us. if($_POST['action'] == 'replace_domain') { - if (!$delstmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { + if (!$check_stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { + throw new Exception('While binding domain to check: ' . $db->lastErrorMsg() . '
'. + 'Added ' . $added . " out of ". $total . " domains"); + } + + $check_result = $check_stmt->execute(); + if (!$check_result) { + throw new Exception('While executing check: ' . $db->lastErrorMsg() . '
'. + 'Added ' . $added . " out of ". $total . " domains"); + } + + // Check return value of CHECK query (0 = only default group, 1 = special group assignments) + $only_default_group = (($check_result->fetchArray(SQLITE3_NUM)[0]) == 0) ? true : false; + if(!$only_default_group) { + throw new Exception('Domain ' . $domain . 'is configured with special group settings.
'. + 'Please modify the domain on the respective group management pages.'); + } + + if (!$delete_stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { throw new Exception('While binding domain: ' . $db->lastErrorMsg() . '
'. 'Added ' . $added . " out of ". $total . " domains"); } - if (!$delstmt->execute()) { + if (!$delete_stmt->execute()) { throw new Exception('While executing: ' . $db->lastErrorMsg() . '
'. 'Added ' . $added . " out of ". $total . " domains"); } } - // Add domain with specific type and comment (both were already bound above) - if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { + + if (!$insert_stmt->bindValue(':domain', $domain, SQLITE3_TEXT) || + !$update_stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) { throw new Exception('While binding domain: ' . $db->lastErrorMsg() . '
'. 'Added ' . $added . " out of ". $total . " domains"); } - if (!$stmt->execute()) { - throw new Exception('While executing: ' . $db->lastErrorMsg() . '
'. + // First execute INSERT OR IGNORE statement to create a record for + // this domain (ignore if already existing) + if (!$insert_stmt->execute()) { + throw new Exception('While executing INSERT OT IGNORE: ' . $db->lastErrorMsg() . '
'. + 'Added ' . $added . " out of ". $total . " domains"); + } + + // Then update the record with a new comment (and modification date + // due to the trigger event) We are not using REPLACE INTO to avoid + // the initial DELETE event (loosing group assignments in case an + // entry did already exist). + if (!$update_stmt->execute()) { + throw new Exception('While executing UPDATE: ' . $db->lastErrorMsg() . '
'. 'Added ' . $added . " out of ". $total . " domains"); } $added++; From 45578468586ac72e94fef9451ee9de5025390282 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 9 Nov 2020 21:16:50 +0100 Subject: [PATCH 3/7] Use JOIN instead of nested SELECT Signed-off-by: DL6ER Co-authored-by: Adam Warner --- scripts/pi-hole/php/groups.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index f0778a99..934580ed 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -521,7 +521,7 @@ if ($_POST['action'] == 'get_groups') { $delete_stmt = null; if($_POST['action'] == 'replace_domain') { // Check statement will reveal any group associations for a given (domain,type) which do NOT belong to the default group - $check_stmt = $db->prepare('SELECT EXISTS(SELECT * FROM domainlist_by_group WHERE domainlist_id = (SELECT id FROM domainlist WHERE domain = :domain) AND group_id != 0)'); + $check_stmt = $db->prepare('SELECT EXISTS(SELECT domain FROM domainlist_by_group dlbg JOIN domainlist dl on dlbg.domainlist_id = dl.id WHERE dl.domain = :domain AND dlbg.group_id != 0)'); if (!$check_stmt) { throw new Exception('While preparing check statement: ' . $db->lastErrorMsg()); } From ad43e5a8dc65a15a89c03711a8c676f24c845efe Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 9 Nov 2020 21:21:30 +0100 Subject: [PATCH 4/7] Add missing space and extend error hiding timeout from 4 to 10 seconds. Signed-off-by: DL6ER --- scripts/pi-hole/js/db_queries.js | 4 ++-- scripts/pi-hole/js/queries.js | 4 ++-- scripts/pi-hole/php/groups.php | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index 0c3d2f21..d5d888d8 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -106,7 +106,7 @@ function add(domain, list) { if (!response.success) { alFailure.show(); err.html(response.message); - alFailure.delay(4000).fadeOut(2000, function () { + alFailure.delay(10000).fadeOut(2000, function () { alFailure.hide(); }); } else { @@ -125,7 +125,7 @@ function add(domain, list) { error: function () { alFailure.show(); err.html(""); - alFailure.delay(1000).fadeOut(2000, function () { + alFailure.delay(10000).fadeOut(2000, function () { alFailure.hide(); }); alInfo.delay(1000).fadeOut(2000, function () { diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 681bf0fe..36e2f59a 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -68,7 +68,7 @@ function add(domain, list) { alFailure.fadeIn(1000); setTimeout(function () { alertModal.modal("hide"); - }, 3000); + }, 10000); } else { // Success alSuccess.children(alDomain).html(domain); @@ -86,7 +86,7 @@ function add(domain, list) { alFailure.fadeIn(1000); setTimeout(function () { alertModal.modal("hide"); - }, 3000); + }, 10000); } }); }); diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index 934580ed..ba9843ca 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -616,7 +616,7 @@ if ($_POST['action'] == 'get_groups') { // Check return value of CHECK query (0 = only default group, 1 = special group assignments) $only_default_group = (($check_result->fetchArray(SQLITE3_NUM)[0]) == 0) ? true : false; if(!$only_default_group) { - throw new Exception('Domain ' . $domain . 'is configured with special group settings.
'. + throw new Exception('Domain ' . $domain . ' is configured with special group settings.
'. 'Please modify the domain on the respective group management pages.'); } From a3b24e08aa35e7b27ba08f09d78dcb41ec92720f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 9 Nov 2020 21:30:40 +0100 Subject: [PATCH 5/7] Reduce code-duplication by moving the addFromQueryLog() function into utils.js. This also fixes non-visible modals on the long-term query log page. Signed-off-by: DL6ER --- db_queries.php | 32 +++++++++++++ scripts/pi-hole/js/db_queries.js | 70 ++-------------------------- scripts/pi-hole/js/queries.js | 79 +------------------------------- scripts/pi-hole/js/utils.js | 78 ++++++++++++++++++++++++++++++- 4 files changed, 114 insertions(+), 145 deletions(-) diff --git a/db_queries.php b/db_queries.php index fd3b0606..6eb103f4 100644 --- a/db_queries.php +++ b/db_queries.php @@ -123,6 +123,37 @@ + + +
@@ -162,6 +193,7 @@ + Date: Mon, 9 Nov 2020 22:55:16 +0100 Subject: [PATCH 6/7] Add modal close button Signed-off-by: DL6ER --- db_queries.php | 3 +++ queries.php | 3 +++ 2 files changed, 6 insertions(+) diff --git a/db_queries.php b/db_queries.php index 6eb103f4..e8962604 100644 --- a/db_queries.php +++ b/db_queries.php @@ -149,6 +149,9 @@
+
diff --git a/queries.php b/queries.php index 08350397..e2ae6924 100644 --- a/queries.php +++ b/queries.php @@ -123,6 +123,9 @@ if(strlen($showing) > 0) + From c8b85b85960025e2ba7710e1a365884db311773b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 10 Nov 2020 21:26:01 +0100 Subject: [PATCH 7/7] Use INSERT OR IGNORE instead of REPLACE Signed-off-by: DL6ER --- scripts/pi-hole/php/groups.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/groups.php b/scripts/pi-hole/php/groups.php index ba9843ca..ab6abc82 100644 --- a/scripts/pi-hole/php/groups.php +++ b/scripts/pi-hole/php/groups.php @@ -506,7 +506,7 @@ if ($_POST['action'] == 'get_groups') { $added = 0; // Prepare INSERT INTO statement - $insert_stmt = $db->prepare('REPLACE INTO domainlist (domain,type) VALUES (:domain,:type)'); + $insert_stmt = $db->prepare('INSERT OR IGNORE INTO domainlist (domain,type) VALUES (:domain,:type)'); if (!$insert_stmt) { throw new Exception('While preparing statement: ' . $db->lastErrorMsg()); }