From a873e9d335303ce78c8a710beda81bccd367d6cb Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Fri, 3 May 2019 07:41:08 +0100 Subject: [PATCH 01/80] Update cryptocoin addresses As per https://github.com/pi-hole/pi-hole/pull/2732 --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 7de3b57b..1caf7ef6 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,7 @@ Make no mistake: **your support is absolutely vital to help keep us innovating!* Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses: - PP Donate via PayPal
-- BTC [Bitcoin](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): 1GKnevUnVaQM2pQieMyeHkpr8DXfkpfAtL
-- BTC [Bitcoin Cash](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): qqh25hlmqaj99xraw00e47xmf8sysnyxhyww2d7dnh
-- BTC [Ethereum](https://commerce.coinbase.com/checkout/fb7facaf-bebd-46be-bb77-b358f4546763): 0xF00aF43d2431BAD585056492b310e48eC40D87e8 +- BTC [Bitcoin, Bitcoin Cash, Ethereum, Litecoin](https://commerce.coinbase.com/checkout/dd304d04-f324-4a77-931b-0db61c77a41b) ### Alternative support If you'd rather not [donate](https://pi-hole.net/donate/) (_which is okay!_), there are other ways you can help support us: From dfcfafd6d9ca69ea8393eb8aadec15f82ad951f1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 22 Jul 2019 21:15:50 +0200 Subject: [PATCH 02/80] Implement whitelist regex support to web interface. Signed-off-by: DL6ER --- list.php | 8 -------- scripts/pi-hole/js/list.js | 27 +++++++++++---------------- scripts/pi-hole/php/add.php | 10 ++++++++-- scripts/pi-hole/php/auth.php | 1 - scripts/pi-hole/php/get.php | 10 ++++++---- scripts/pi-hole/php/sub.php | 13 ++++++++----- 6 files changed, 33 insertions(+), 36 deletions(-) diff --git a/list.php b/list.php index 5cbff9c7..8d4fc1ba 100644 --- a/list.php +++ b/list.php @@ -35,13 +35,9 @@ function getFullName() {
- - - -
@@ -66,14 +62,10 @@ function getFullName() { -

Exact blocking

-
    -

    Regex & Wildcard blocking

      - diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 53d6b7e5..9a6644ca 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -43,12 +43,8 @@ function addListEntry(entry, index, list, button, type) } function refresh(fade) { - var listw; var list = $("#list"); - if(listType === "black") - { - listw = $("#list-regex"); - } + var listw = $("#list-regex"); if(fade) { list.fadeOut(100); if(listw) @@ -69,9 +65,10 @@ function refresh(fade) { if((listType === "black" && response.blacklist.length === 0 && - response.regex.length === 0) || + response.regex_blacklist.length === 0) || (listType === "white" && - response.whitelist.length === 0)) + response.whitelist.length === 0 && + response.regex_whitelist.length === 0)) { $("h3").hide(); list.html("
      Your " + fullName + " is empty!
      "); @@ -82,12 +79,12 @@ function refresh(fade) { if(listType === "white") { data = response.whitelist.sort(); - data2 = []; // No regex data, use empty array + data2 = response.regex_whitelist.sort(); } else if(listType === "black") { data = response.blacklist.sort(); - data2 = response.regex.sort(); + data2 = response.regex_blacklist.sort(); } data.forEach(function (entry, index) { @@ -97,7 +94,7 @@ function refresh(fade) { // Add regex domains if present in returned list data data2.forEach(function (entry, index) { - addListEntry(entry, index, listw, "#list-regex", "regex"); + addListEntry(entry, index, listw, "#list-regex", listType+"_regex"); }); } list.fadeIn(100); @@ -117,9 +114,9 @@ window.onload = refresh(false); function sub(index, entry, arg) { var domain = $("#list #"+index); var locallistType = listType; - if(arg === "regex") + if(arg === "black_regex" || arg === "white_regex") { - locallistType = "regex"; + locallistType = arg; domain = $("#list-regex #"+index); } domain.hide("highlight"); @@ -143,14 +140,12 @@ function sub(index, entry, arg) { function add(arg) { var locallistType = listType; var domain = $("#domain"); - var wild = false; if(domain.val().length === 0){ return; } - if(arg === "wild" || arg === "regex") + if(arg === "wild") { - locallistType = arg; - wild = true; + locallistType = listType+"_wild"; } var alInfo = $("#alInfo"); diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php index 68a7496d..9f3c1b0a 100644 --- a/scripts/pi-hole/php/add.php +++ b/scripts/pi-hole/php/add.php @@ -44,12 +44,18 @@ switch($type) { echo shell_exec("sudo pihole -a audit ".$domains); } break; - case "regex": + case "black_regex": echo shell_exec("sudo pihole --regex --web ".$domains); break; - case "wild": + case "white_regex": + echo shell_exec("sudo pihole --whiteregex --web ".$domains); + break; + case "black_wild": echo shell_exec("sudo pihole --wild --web ".$domains); break; + case "white_wild": + echo shell_exec("sudo pihole --whitewild --web ".$domains); + break; case "audit": echo shell_exec("sudo pihole -a audit ".$domains); break; diff --git a/scripts/pi-hole/php/auth.php b/scripts/pi-hole/php/auth.php index 69fe6b28..1ea6014d 100644 --- a/scripts/pi-hole/php/auth.php +++ b/scripts/pi-hole/php/auth.php @@ -11,7 +11,6 @@ $ERRORLOG = getenv('PHP_ERROR_LOG'); if (empty($ERRORLOG)) { $ERRORLOG = '/var/log/lighttpd/error.log'; } -$regexfile = "/etc/pihole/regex.list"; function pi_log($message) { error_log(date('Y-m-d H:i:s') . ': ' . $message . "\n", 3, $GLOBALS['ERRORLOG']); diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php index c84b991a..b38446bc 100644 --- a/scripts/pi-hole/php/get.php +++ b/scripts/pi-hole/php/get.php @@ -21,7 +21,7 @@ function getTableContent($listname) { global $db; $entries = array(); $querystr = implode(" ",array("SELECT ${listname}.*,\"group\".enabled as group_enabled", - "FROM $listname", + "FROM ${listname}", "LEFT JOIN ${listname}_by_group ON ${listname}_by_group.${listname}_id = ${listname}.id", "LEFT JOIN \"group\" ON \"group\".id = ${listname}_by_group.group_id", "GROUP BY domain;")); @@ -54,13 +54,15 @@ function filterArray(&$inArray) { switch ($listtype) { case "white": - $list = getTableContent("whitelist"); + $exact = getTableContent("whitelist"); + $regex = getTableContent("regex_whitelist"); + $list = array_merge($exact, $regex); break; case "black": $exact = getTableContent("blacklist"); - $regex = getTableContent("regex"); - $list = array_merge($exact, $regex); + $regex = getTableContent("regex_blacklist"); + $list = array_merge($exact, $regex); break; default: diff --git a/scripts/pi-hole/php/sub.php b/scripts/pi-hole/php/sub.php index 97c8f84b..4eae79c7 100644 --- a/scripts/pi-hole/php/sub.php +++ b/scripts/pi-hole/php/sub.php @@ -18,7 +18,7 @@ if (empty($api)) { // Don't check if the added item is a valid domain for regex expressions. // Regex filters are validated by FTL on import and skipped if invalid -if($type !== "regex") { +if($type !== "black_regex" && $type !== "white_regex") { check_domain(); } @@ -27,13 +27,16 @@ $domain = escapeshellcmd($_POST['domain']); switch($type) { case "white": - exec("sudo pihole -w -q -d ".$domain); + echo shell_exec("sudo pihole -w -q -d ".$domain); break; case "black": - exec("sudo pihole -b -q -d ".$domain); + echo shell_exec("sudo pihole -b -q -d ".$domain); break; - case "regex": - exec("sudo pihole --regex -q -d ".$domain); + case "black_regex": + echo shell_exec("sudo pihole --regex -q -d ".$domain); + break; + case "white_regex": + echo shell_exec("sudo pihole --whiteregex -q -d ".$domain); break; } From 31d0b32103df6b2445a2ce8cf75c7c672b5036e9 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 22 Jul 2019 21:35:42 +0200 Subject: [PATCH 03/80] Only show headings when we have domains for the respective list (regex / exact). Signed-off-by: DL6ER --- list.php | 4 ++-- scripts/pi-hole/js/list.js | 13 ++++++++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/list.php b/list.php index 8d4fc1ba..d6ea04b9 100644 --- a/list.php +++ b/list.php @@ -62,9 +62,9 @@ function getFullName() { -

      Exact blocking

      +
        -

        Regex & Wildcard blocking

        +
          diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 9a6644ca..09e67736 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -75,7 +75,6 @@ function refresh(fade) { } else { - $("h3").show(); if(listType === "white") { data = response.whitelist.sort(); @@ -86,12 +85,20 @@ function refresh(fade) { data = response.blacklist.sort(); data2 = response.regex_blacklist.sort(); } + + if(data.length > 0) + { + $("#h3-exact").show(); + } + if(data2.length > 0) + { + $("#h3-regex").show(); + } + data.forEach(function (entry, index) { addListEntry(entry, index, list, "#list", "exact"); }); - - // Add regex domains if present in returned list data data2.forEach(function (entry, index) { addListEntry(entry, index, listw, "#list-regex", listType+"_regex"); From b7fe51815983f2e93d3ce912ab9d1ce206bd4372 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 22 Jul 2019 21:38:58 +0200 Subject: [PATCH 04/80] Remove heading when list goes empty. Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 09e67736..8545de5b 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -119,13 +119,16 @@ function refresh(fade) { window.onload = refresh(false); function sub(index, entry, arg) { - var domain = $("#list #"+index); + var list = "#list"; + var heading = "#h3-exact"; var locallistType = listType; if(arg === "black_regex" || arg === "white_regex") { + list = "#list-regex"; + heading = "#h3-regex"; locallistType = arg; - domain = $("#list-regex #"+index); } + var domain = $(list+" #"+index); domain.hide("highlight"); $.ajax({ url: "scripts/pi-hole/php/sub.php", @@ -136,6 +139,10 @@ function sub(index, entry, arg) { return; } domain.remove(); + if($(list+" li").length < 1) + { + $(heading).hide(); + } }, error: function(jqXHR, exception) { alert("Failed to remove the domain!"); From 3ebf766240f91ef2a44298504449bf8c1d22a76a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 22 Jul 2019 21:43:12 +0200 Subject: [PATCH 05/80] Simplify add() in list.js. Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 8545de5b..3feb24fc 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -151,16 +151,11 @@ function sub(index, entry, arg) { }); } -function add(arg) { - var locallistType = listType; +function add(type) { var domain = $("#domain"); if(domain.val().length === 0){ return; } - if(arg === "wild") - { - locallistType = listType+"_wild"; - } var alInfo = $("#alInfo"); var alSuccess = $("#alSuccess"); @@ -175,7 +170,7 @@ function add(arg) { $.ajax({ url: "scripts/pi-hole/php/add.php", method: "post", - data: {"domain":domain.val().trim(), "list":locallistType, "token":token}, + data: {"domain":domain.val().trim(), "list":type, "token":token}, success: function(response) { if (response.indexOf(" already exists in ") !== -1) { alWarning.show(); @@ -232,15 +227,15 @@ $(document).keypress(function(e) { // Handle buttons $("#btnAdd").on("click", function() { - add("exact"); + add(listType); }); $("#btnAddWildcard").on("click", function() { - add("wild"); + add(listType+"_wild"); }); $("#btnAddRegex").on("click", function() { - add("regex"); + add(listType+"_regex"); }); $("#btnRefresh").on("click", function() { From cd2daa323990a49029a2fcd4608b9763a8cb6655 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 24 Jul 2019 19:41:04 +0200 Subject: [PATCH 06/80] Review comments Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 15 +++------------ scripts/pi-hole/php/func.php | 17 ----------------- 2 files changed, 3 insertions(+), 29 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 3feb24fc..a5bcafd9 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -47,10 +47,7 @@ function refresh(fade) { var listw = $("#list-regex"); if(fade) { list.fadeOut(100); - if(listw) - { - listw.fadeOut(100); - } + listw.fadeOut(100); } $.ajax({ url: "scripts/pi-hole/php/get.php", @@ -58,10 +55,7 @@ function refresh(fade) { data: {"list":listType}, success: function(response) { list.html(""); - if(listw) - { - listw.html(""); - } + listw.html(""); if((listType === "black" && response.blacklist.length === 0 && @@ -105,10 +99,7 @@ function refresh(fade) { }); } list.fadeIn(100); - if(listw) - { - listw.fadeIn(100); - } + listw.fadeIn(100); }, error: function(jqXHR, exception) { $("#alFailure").show(); diff --git a/scripts/pi-hole/php/func.php b/scripts/pi-hole/php/func.php index 7c1d3d65..bc353218 100644 --- a/scripts/pi-hole/php/func.php +++ b/scripts/pi-hole/php/func.php @@ -45,21 +45,4 @@ if(!function_exists('hash_equals')) { } } -function add_regex($regex, $mode=FILE_APPEND, $append="\n") -{ - global $regexfile; - if(file_put_contents($regexfile, $regex.$append, $mode) === FALSE) - { - $err = error_get_last()["message"]; - echo "Unable to add regex \"".htmlspecialchars($regex)."\" to ${regexfile}
          Error message: $err"; - } - else - { - // Send SIGHUP to pihole-FTL using a frontend command - // to force reloading of the regex domains - // This will also wipe the resolver's cache - echo exec("sudo pihole restartdns reload"); - } -} - ?> From 1ddf36a752260d06614d10c5807455d7477ce13c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 24 Jul 2019 21:10:19 +0200 Subject: [PATCH 07/80] Archive and restore full tables instead of only domains. Use JSON as backup file format. Signed-off-by: DL6ER --- scripts/pi-hole/php/teleporter.php | 161 ++++++++++++++++++++++++++--- settings.php | 14 ++- 2 files changed, 160 insertions(+), 15 deletions(-) diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index 9b03a788..54254466 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -15,7 +15,7 @@ if (php_sapi_name() !== "cli") { check_csrf(isset($_POST["token"]) ? $_POST["token"] : ""); } -$db = SQLite3_connect(getGravityDBFilename()); +$db = SQLite3_connect(getGravityDBFilename(), SQLITE3_OPEN_READWRITE); function archive_add_file($path,$name,$subdir="") { @@ -29,20 +29,112 @@ function archive_add_file($path,$name,$subdir="") * * @param $name string The name of the file in the archive to save the table to * @param $table string The table to export - * @param $column string The column on the table to export */ -function archive_add_table($name, $table, $column) +function archive_add_table($name, $table) { global $archive, $db; - $results = $db->query("SELECT $column FROM $table"); - $content = ""; + $results = $db->query("SELECT * FROM $table"); - while($row = $results->fetchArray()) { - $content .= $row[0]."\n"; + // Return early without creating a file if th + // requested table cannot be accessed + if(is_null($results)) + return; + + $content = array(); + while ($row = $results->fetchArray(SQLITE3_ASSOC)) + { + array_push($content, $row); } - $archive[$name] = $content; + $archive[$name] = json_encode($content); +} + +/** + * Restore the contents of a table from an uploaded archive + * + * @param $file object The file of the file in the archive to restore the table from + * @param $table string The table to import + * @param $flush boolean Whether to flush the table before importing the archived data + */ +function archive_restore_table($file, $table, $flush=false) +{ + global $archive, $db; + + $json_string = file_get_contents($file); + // Return early if we cannot extract the JSON string + if(is_null($json_string)) + return 0; + + $contents = json_decode($json_string, true); + // Return early if we cannot decode the JSON string + if(is_null($contents)) + return 0; + + // Flush table if requested + if($flush) + $db->exec("DELETE FROM ".$table); + + // Prepare field name for domain/address depending on the table we restore to + if($table === "adlist") + { + $sql = "INSERT OR IGNORE INTO adlist"; + $sql .= " (id,address,enabled,date_added,comment)"; + $sql .= " VALUES (:id,:address,:enabled,:date_added,:comment);"; + } + elseif($table === "domain_audit") + { + $sql = "INSERT OR IGNORE INTO domain_audit"; + $sql .= " (id,domain,date_added)"; + $sql .= " VALUES (:id,:domain,:date_added);"; + } + else + { + $sql = "INSERT OR IGNORE INTO ".$table; + $sql .= " (id,domain,enabled,date_added,comment)"; + $sql .= " VALUES (:id,:domain,:enabled,:date_added,:comment);"; + } + + // Prepare SQLite statememt + $stmt = $db->prepare($sql); + + // Return early if we prepare the SQLite statement + if(!$stmt) + { + echo "Failed to prepare statement for ".$table." table."; + echo $sql; + return 0; + } + + // Loop over rows and inject the lines into the database + $num = 0; + foreach($contents as $row) + { + $stmt->bindValue(":id", $row["id"], SQLITE3_INTEGER); + $stmt->bindValue(":date_added", $row["date_added"], SQLITE3_INTEGER); + + if($table === "adlist") + $stmt->bindValue(":address", $row["address"], SQLITE3_TEXT); + else + $stmt->bindValue(":domain", $row["domain"], SQLITE3_TEXT); + + if($table !== "domain_audit") + { + $stmt->bindValue(":enabled", $row["enabled"], SQLITE3_INTEGER); + if(is_null($row["comment"])) + $type = SQLITE3_NULL; + else + $type = SQLITE3_TEXT; + $stmt->bindValue(":comment", $row["comment"], $type); + } + + $stmt->execute(); + $stmt->reset(); + $stmt->clear(); + $num++; + } + + return $num; } function archive_add_directory($path,$subdir="") @@ -176,6 +268,48 @@ if(isset($_POST["action"])) exec("sudo pihole -a audit ".implode(" ",$auditlog)); } + if(isset($_POST["blacklist"]) && $file->getFilename() === "blacklist.exact.json") + { + $num = archive_restore_table($file, "blacklist"); + echo "Processed blacklist (exact) (".$num." entries)
          \n"; + $importedsomething = true; + } + + if(isset($_POST["regexlist"]) && $file->getFilename() === "blacklist.regex.json") + { + $num = archive_restore_table($file, "regex_blacklist"); + echo "Processed blacklist (regex) (".$num." entries)
          \n"; + $importedsomething = true; + } + + if(isset($_POST["whitelist"]) && $file->getFilename() === "whitelist.exact.json") + { + $num = archive_restore_table($file, "whitelist"); + echo "Processed whitelist (exact) (".$num." entries)
          \n"; + $importedsomething = true; + } + + if(isset($_POST["regex_whitelist"]) && $file->getFilename() === "whitelist.regex.json") + { + $num = archive_restore_table($file, "regex_whitelist"); + echo "Processed whitelist (regex) (".$num." entries)
          \n"; + $importedsomething = true; + } + + if(isset($_POST["adlist"]) && $file->getFilename() === "adlist.json") + { + $num = archive_restore_table($file, "adlist"); + echo "Processed adlist (".$num." entries)
          \n"; + $importedsomething = true; + } + + if(isset($_POST["auditlog"]) && $file->getFilename() === "domain_audit.json") + { + $num = archive_restore_table($file, "domain_audit"); + echo "Processed domain_audit (".$num." entries)
          \n"; + $importedsomething = true; + } + if($importedsomething) { exec("sudo pihole restartdns"); @@ -201,12 +335,13 @@ else exit("cannot open/create ".htmlentities($archive_file_name)."
          \nPHP user: ".exec('whoami')."\n"); } - archive_add_table("whitelist.txt", "whitelist", "domain"); - archive_add_table("blacklist.txt", "blacklist", "domain"); - archive_add_table("regex.list", "regex", "domain"); - archive_add_table("adlists.list", "adlist", "address"); + archive_add_table("whitelist.exact.json", "whitelist"); + archive_add_table("whitelist.regex.json", "regex_whitelist"); + archive_add_table("blacklist.exact.json", "blacklist"); + archive_add_table("blacklist.regex.json", "regex_blacklist"); + archive_add_table("adlists.json", "adlist"); + archive_add_table("domain_audit.json", "domain_audit"); archive_add_file("/etc/pihole/","setupVars.conf"); - archive_add_file("/etc/pihole/","auditlog.list"); archive_add_directory("/etc/dnsmasq.d/","dnsmasq.d/"); $archive->compress(Phar::GZ); // Creates a gziped copy diff --git a/settings.php b/settings.php index e9423ecb..f0c5a187 100644 --- a/settings.php +++ b/settings.php @@ -1145,7 +1145,12 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
          + Whitelist (exact) +
          +
          +
          +
          +
          +
          From 72057cfa26353f2260e4318fe5edb51b9c13ee7b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 24 Jul 2019 22:14:07 +0200 Subject: [PATCH 09/80] Close database connection at the end of a table recovery. Signed-off-by: DL6ER --- scripts/pi-hole/php/teleporter.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index 65e7f490..2d377c83 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -59,7 +59,7 @@ function archive_add_table($name, $table) */ function archive_restore_table($file, $table, $flush=false) { - global $archive, $db; + global $db; $json_string = file_get_contents($file); // Return early if we cannot extract the JSON string @@ -128,12 +128,17 @@ function archive_restore_table($file, $table, $flush=false) $stmt->bindValue(":comment", $row["comment"], $type); } - $stmt->execute(); - $stmt->reset(); - $stmt->clear(); - $num++; + if($stmt->execute() && $stmt->reset() && $stmt->clear()) + $num++; + else + { + $stmt->close(); + return $num; + } } + // Close database connection and return number or processed rows + $stmt->close(); return $num; } From 3cfb2267571f83f9f0b8525673e29cb36d628a40 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 24 Jul 2019 22:14:48 +0200 Subject: [PATCH 10/80] Reload only ONCE not after EACH imported file. Signed-off-by: DL6ER --- scripts/pi-hole/php/teleporter.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index 2d377c83..ddc4e7c4 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -316,11 +316,11 @@ if(isset($_POST["action"])) echo "Processed domain_audit (".$num." entries)
          \n"; $importedsomething = true; } + } - if($importedsomething) - { - exec("sudo pihole restartdns"); - } + if($importedsomething) + { + exec("sudo pihole restartdns"); } unlink($fullfilename); From eac87f139f480641c2e96755f64bea7e007f52de Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 24 Jul 2019 22:19:15 +0200 Subject: [PATCH 11/80] pihole-FTL only needs to be reloaded not restarted when the lists have been updated. This is much faster than a hard restart. Signed-off-by: DL6ER --- scripts/pi-hole/php/teleporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index ddc4e7c4..db5688df 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -320,7 +320,7 @@ if(isset($_POST["action"])) if($importedsomething) { - exec("sudo pihole restartdns"); + exec("sudo pihole restartdns reload"); } unlink($fullfilename); From 13f07f9d87c681eccd9ee884a11ffba795cd2a89 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 24 Jul 2019 23:15:19 +0200 Subject: [PATCH 12/80] Switch the teleporter to only interact with gravity.db directly. This avoids any need for escaping regexes and just seems to be the right thing to do. Ensure that we flush a table only once (in case multiple files add to the same table). Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 6 +- scripts/pi-hole/php/teleporter.php | 152 ++++++++++++++++++----------- 2 files changed, 96 insertions(+), 62 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index a5bcafd9..adb7c642 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -82,11 +82,11 @@ function refresh(fade) { if(data.length > 0) { - $("#h3-exact").show(); + $("#h3-exact").fadeIn(100); } if(data2.length > 0) { - $("#h3-regex").show(); + $("#h3-regex").fadeIn(100); } data.forEach(function (entry, index) @@ -132,7 +132,7 @@ function sub(index, entry, arg) { domain.remove(); if($(list+" li").length < 1) { - $(heading).hide(); + $(heading).fadeOut(100); } }, error: function(jqXHR, exception) { diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index db5688df..6d4083f8 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -17,6 +17,8 @@ if (php_sapi_name() !== "cli") { $db = SQLite3_connect(getGravityDBFilename(), SQLITE3_OPEN_READWRITE); +$flushed_tables = array(); + function archive_add_file($path,$name,$subdir="") { global $archive; @@ -59,7 +61,7 @@ function archive_add_table($name, $table) */ function archive_restore_table($file, $table, $flush=false) { - global $db; + global $db, $flushed_tables; $json_string = file_get_contents($file); // Return early if we cannot extract the JSON string @@ -71,9 +73,12 @@ function archive_restore_table($file, $table, $flush=false) if(is_null($contents)) return 0; - // Flush table if requested - if($flush) + // Flush table if requested, only flush each table once + if($flush && !in_array($table, $flushed_tables)) + { $db->exec("DELETE FROM ".$table); + array_push($flushed_tables, $table); + } // Prepare field name for domain/address depending on the table we restore to if($table === "adlist") @@ -142,6 +147,80 @@ function archive_restore_table($file, $table, $flush=false) return $num; } +/** + * Create table rows from an uploaded archive file + * + * @param $file object The file of the file in the archive to import + * @param $table string The target table + * @param $flush boolean Whether to flush the table before importing the archived data + */ +function archive_insert_into_table($file, $table, $flush=false, $wildcardstyle=false) +{ + global $db, $flushed_tables; + + $rows = array_filter(explode("\n",file_get_contents($file))); + // Return early if we cannot extract the lines in the file + if(is_null($rows)) + return 0; + + // Flush table if requested, only flush each table once + if($flush && !in_array($table, $flushed_tables)) + { + $db->exec("DELETE FROM ".$table); + array_push($flushed_tables, $table); + } + + // Prepare field name for domain/address depending on the table we restore to + if($table === "adlist") + { + $sql = "INSERT OR IGNORE INTO adlist"; + $sql .= " (address) VALUES (:address);"; + } + else + { + $sql = "INSERT OR IGNORE INTO ".$table; + $sql .= " (domain) VALUES (:domain);"; + } + + // Prepare SQLite statememt + $stmt = $db->prepare($sql); + + // Return early if we prepare the SQLite statement + if(!$stmt) + { + echo "Failed to prepare statement for ".$table." table."; + echo $sql; + return 0; + } + + // Loop over rows and inject the lines into the database + $num = 0; + foreach($rows as $row) + { + if($wildcardstyle) + $line = "(\\.|^)".str_replace(".","\\.",$row)."$"; + else + $line = $row; + + if($table === "adlist") + $stmt->bindValue(":address", $line, SQLITE3_TEXT); + else + $stmt->bindValue(":domain", $line, SQLITE3_TEXT); + + if($stmt->execute() && $stmt->reset() && $stmt->clear()) + $num++; + else + { + $stmt->close(); + return $num; + } + } + + // Close database connection and return number or processed rows + $stmt->close(); + return $num; +} + function archive_add_directory($path,$subdir="") { if($dir = opendir($path)) @@ -157,40 +236,6 @@ function archive_add_directory($path,$subdir="") } } -function limit_length(&$item, $key) -{ - // limit max length for a domain entry to 253 chars - // return only a part of the string if it is longer - $item = substr($item, 0, 253); -} - -function process_file($contents,$check=True) -{ - $domains = array_filter(explode("\n",$contents)); - - // Walk array and apply a max string length - // function to every member of the array of domains - array_walk($domains, "limit_length"); - - // Check validity of domains (don't do it for regex filters) - if($check) - { - check_domains($domains); - } - - return $domains; -} - -function check_domains($domains) -{ - foreach($domains as $domain) - { - if(!is_valid_domain_name($domain)){ - die(htmlspecialchars($domain).' is not a valid domain'); - } - } -} - if(isset($_POST["action"])) { if($_FILES["zip_file"]["name"] && $_POST["action"] == "in") @@ -230,49 +275,38 @@ if(isset($_POST["action"])) { if(isset($_POST["blacklist"]) && $file->getFilename() === "blacklist.txt") { - $blacklist = process_file(file_get_contents($file)); - echo "Processing blacklist.txt (".count($blacklist)." entries)
          \n"; - exec("sudo pihole -b -nr --nuke"); - exec("sudo pihole -b -q -nr ".implode(" ", $blacklist)); + $num = archive_insert_into_table($file, "blacklist", $flushtables); + echo "Processed blacklist (exact) (".$num." entries)
          \n"; $importedsomething = true; } if(isset($_POST["whitelist"]) && $file->getFilename() === "whitelist.txt") { - $whitelist = process_file(file_get_contents($file)); - echo "Processing whitelist.txt (".count($whitelist)." entries)
          \n"; - exec("sudo pihole -w -nr --nuke"); - exec("sudo pihole -w -q -nr ".implode(" ", $whitelist)); + $num = archive_insert_into_table($file, "whitelist", $flushtables); + echo "Processed whitelist (exact) (".$num." entries)
          \n"; $importedsomething = true; } if(isset($_POST["regexlist"]) && $file->getFilename() === "regex.list") { - $regexlist = process_file(file_get_contents($file),false); - echo "Processing regex.list (".count($regexlist)." entries)
          \n"; - - $escapedRegexlist = array_map("escapeshellcmd", $regexlist); - exec("sudo pihole --regex -nr --nuke"); - exec("sudo pihole --regex -q -nr ".implode(" ", $escapedRegexlist)); + $num = archive_insert_into_table($file, "regex_blacklist", $flushtables); + echo "Processed blacklist (regex) (".$num." entries)
          \n"; $importedsomething = true; } // Also try to import legacy wildcard list if found if(isset($_POST["regexlist"]) && $file->getFilename() === "wildcardblocking.txt") { - $wildlist = process_file(file_get_contents($file)); - echo "Processing wildcardblocking.txt (".count($wildlist)." entries)
          \n"; - exec("sudo pihole --wild -nr --nuke"); - exec("sudo pihole --wild -q -nr ".implode(" ", $wildlist)); + $num = archive_insert_into_table($file, "regex_blacklist", $flushtables, true); + echo "Processed blacklist (regex, wildcard style) (".$num." entries)
          \n"; $importedsomething = true; } if(isset($_POST["auditlog"]) && $file->getFilename() === "auditlog.list") { - $auditlog = process_file(file_get_contents($file)); - echo "Processing auditlog.list (".count($auditlog)." entries)
          \n"; - exec("sudo pihole -a clearaudit"); - exec("sudo pihole -a audit ".implode(" ",$auditlog)); + $num = archive_insert_into_table($file, "domain_audit", $flushtables); + echo "Processed blacklist (regex) (".$num." entries)
          \n"; + $importedsomething = true; } if(isset($_POST["blacklist"]) && $file->getFilename() === "blacklist.exact.json") From 8991605f0468157a22dac722c82a1e500250cea3 Mon Sep 17 00:00:00 2001 From: denny0754 Date: Mon, 15 Jul 2019 16:33:05 +0100 Subject: [PATCH 13/80] Fixed Issue#983 --- scripts/pi-hole/js/queries.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 95276cad..2b6ca3db 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -183,7 +183,7 @@ $(document).ready(function() { blocked = true; color = "red"; fieldtext = "Blocked (gravity)"; - buttontext = ""; + buttontext = ""; break; case "2": blocked = false; @@ -201,13 +201,13 @@ $(document).ready(function() { blocked = true; color = "red"; fieldtext = "Blocked (regex/wildcard)"; - buttontext = "" ; + buttontext = "" ; break; case "5": blocked = true; color = "red"; fieldtext = "Blocked (blacklist)"; - buttontext = "" ; + buttontext = "" ; break; case "6": blocked = true; From bc7aef744cdd4f557c8b6bc65dbb06ac31d1c417 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Aug 2019 21:13:53 +0200 Subject: [PATCH 14/80] "pihole --whiteregex" has been renamed to "pihole --white-regex". Similar for "--whitewild" -> "--white-wild". Signed-off-by: DL6ER --- scripts/pi-hole/php/add.php | 4 ++-- scripts/pi-hole/php/sub.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php index 9f3c1b0a..663320f2 100644 --- a/scripts/pi-hole/php/add.php +++ b/scripts/pi-hole/php/add.php @@ -48,13 +48,13 @@ switch($type) { echo shell_exec("sudo pihole --regex --web ".$domains); break; case "white_regex": - echo shell_exec("sudo pihole --whiteregex --web ".$domains); + echo shell_exec("sudo pihole --white-regex --web ".$domains); break; case "black_wild": echo shell_exec("sudo pihole --wild --web ".$domains); break; case "white_wild": - echo shell_exec("sudo pihole --whitewild --web ".$domains); + echo shell_exec("sudo pihole --white-wild --web ".$domains); break; case "audit": echo shell_exec("sudo pihole -a audit ".$domains); diff --git a/scripts/pi-hole/php/sub.php b/scripts/pi-hole/php/sub.php index 4eae79c7..570520a8 100644 --- a/scripts/pi-hole/php/sub.php +++ b/scripts/pi-hole/php/sub.php @@ -36,7 +36,7 @@ switch($type) { echo shell_exec("sudo pihole --regex -q -d ".$domain); break; case "white_regex": - echo shell_exec("sudo pihole --whiteregex -q -d ".$domain); + echo shell_exec("sudo pihole --white-regex -q -d ".$domain); break; } From c9f5c9500a30e7cc6cd6faef4c4550338600a8ee Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 5 Aug 2019 21:48:31 +0200 Subject: [PATCH 15/80] Directly modify gravity tables from the lists pages. Signed-off-by: DL6ER --- list.php | 2 +- scripts/pi-hole/js/list.js | 37 ++++++++---- scripts/pi-hole/php/add.php | 115 +++++++++++++++++++++++++----------- scripts/pi-hole/php/sub.php | 76 +++++++++++++++++------- 4 files changed, 160 insertions(+), 70 deletions(-) diff --git a/list.php b/list.php index d6ea04b9..21db22cc 100644 --- a/list.php +++ b/list.php @@ -49,7 +49,7 @@ function getFullName() { - - -
          - -
          -
          -
          -

          Query Types (over time)

          -
          -
          -
          - -
          -
          -
          - -
          - -
          -
          - -
          -
          -
          -

          Upstreams (over time)

          -
          -
          -
          - -
          -
          -
          - -
          - -
          -
          - -
          - Date: Tue, 27 Aug 2019 16:56:42 +0200 Subject: [PATCH 35/80] Use correct IDs for spinners in index.js Signed-off-by: DL6ER --- scripts/pi-hole/js/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 21e00bda..8058b16e 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -637,8 +637,8 @@ function updateSummaryData(runOnce) { $("#temperature").html(" FTL offline"); // Show spinner $("#queries-over-time .overlay").show(); - $("#forward-destinations .overlay").show(); - $("#query-types .overlay").show(); + $("#forward-destinations-pie .overlay").show(); + $("#query-types-pie .overlay").show(); $("#client-frequency .overlay").show(); $("#domain-frequency .overlay").show(); $("#ad-frequency .overlay").show(); From 4743228a863c1ec50a82a48efebd65edb9603581 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 27 Aug 2019 17:19:05 +0200 Subject: [PATCH 36/80] Allow domains to be added by hitting return (add them as exact domains). Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 2 +- scripts/pi-hole/php/database.php | 3 +-- settings.php | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 7b9e54a8..4de285de 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -223,7 +223,7 @@ function add(type) { $(document).keypress(function(e) { if(e.which === 13 && $("#domain").is(":focus")) { // Enter was pressed, and the input has focus - add("exact"); + add(listType); } }); diff --git a/scripts/pi-hole/php/database.php b/scripts/pi-hole/php/database.php index d3ac258b..b2a7bf82 100644 --- a/scripts/pi-hole/php/database.php +++ b/scripts/pi-hole/php/database.php @@ -78,11 +78,10 @@ function add_to_table($db, $table, $domains, $wildcardstyle=false, $returnnum=fa // Return early if we failed to prepare the SQLite statement if(!$stmt) { - echo "Failed to prepare statement for ".$table." table."; if($returnnum) return 0; else - return "Error, added: 0"; + return "Error: Failed to prepare statement for ".$table." table."; } // Loop over domains and inject the lines into the database diff --git a/settings.php b/settings.php index 4b76daf7..59c79b5c 100644 --- a/settings.php +++ b/settings.php @@ -1165,7 +1165,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
          + Blocklists
          From d101465c62eb18d49b952e61607fce0183f203ac Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 27 Aug 2019 18:53:10 +0200 Subject: [PATCH 37/80] Pass through errors to user interface Signed-off-by: DL6ER --- scripts/pi-hole/php/database.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/pi-hole/php/database.php b/scripts/pi-hole/php/database.php index b2a7bf82..bbdbec72 100644 --- a/scripts/pi-hole/php/database.php +++ b/scripts/pi-hole/php/database.php @@ -105,7 +105,7 @@ function add_to_table($db, $table, $domains, $wildcardstyle=false, $returnnum=fa if($returnnum) return $num; else - return "Error, added: ".$num; + return "Error: ".$db->lastErrorMsg().", added: ".$num; } } @@ -134,11 +134,10 @@ function remove_from_table($db, $table, $domains, $returnnum=false) // Return early if we failed to prepare the SQLite statement if(!$stmt) { - echo "Failed to prepare statement for ".$table." table."; if($returnnum) return 0; else - return "Error, added: 0"; + return "Error: Failed to prepare statement for ".$table." table."; } // Loop over domains and remove the lines from the database @@ -155,7 +154,7 @@ function remove_from_table($db, $table, $domains, $returnnum=false) if($returnnum) return $num; else - return "Error, removed: ".$num; + return "Error: ".$db->lastErrorMsg().", removed: ".$num; } } From fcc49e392870b270cd258e9ebd1cc4f238c51a81 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 27 Aug 2019 19:26:51 +0200 Subject: [PATCH 38/80] Wrap addition and removal of domains in a transaction + deliver a more detailed success message to the user specifying how many domains are actually added (in case some already existed). Signed-off-by: DL6ER --- list.php | 2 +- scripts/pi-hole/js/list.js | 7 ++++ scripts/pi-hole/php/database.php | 58 +++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 5 deletions(-) diff --git a/list.php b/list.php index 21db22cc..7e9d5f62 100644 --- a/list.php +++ b/list.php @@ -49,7 +49,7 @@ function getFullName() {
          - +
          @@ -62,7 +62,7 @@
          - +
          diff --git a/db_graph.php b/db_graph.php index 79bb51e1..a4ce5b47 100644 --- a/db_graph.php +++ b/db_graph.php @@ -55,7 +55,7 @@ $token = $_SESSION['token']; diff --git a/db_lists.php b/db_lists.php index 0010edb5..fdeb62c7 100644 --- a/db_lists.php +++ b/db_lists.php @@ -75,7 +75,7 @@ else @@ -102,7 +102,7 @@ else @@ -129,7 +129,7 @@ else diff --git a/index.php b/index.php index e090a445..ab7ca967 100644 --- a/index.php +++ b/index.php @@ -90,7 +90,7 @@
          - +
          @@ -115,7 +115,7 @@
          - +
          @@ -137,7 +137,7 @@
          - +
          @@ -156,7 +156,7 @@
          - +
          @@ -197,7 +197,7 @@
          - +
          @@ -214,7 +214,7 @@
          - +
          @@ -254,7 +254,7 @@ else
          - +
          @@ -281,7 +281,7 @@ else
          - +
          @@ -310,7 +310,7 @@ else
          - +
          @@ -338,7 +338,7 @@ else
          - +
          diff --git a/list.php b/list.php index 75b14a92..5cbff9c7 100644 --- a/list.php +++ b/list.php @@ -42,7 +42,7 @@ function getFullName() { - + From 695730920aab5419e1a5eb50da6b5c773792a882 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 5 May 2019 13:49:22 +0200 Subject: [PATCH 46/80] Ensure correct sorting of queries even if they happened withing the same second. Fixes #934 Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 12112717..52982576 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -116,6 +116,7 @@ function autofilter(){ $(document).ready(function() { var status; + var dataIndex = 0; // Do we want to filter queries? var GETDict = {}; @@ -152,7 +153,6 @@ $(document).ready(function() { tableApi = $("#all-queries").DataTable( { "rowCallback": function( row, data, index ){ - // DNSSEC status var dnssec_status; switch (data[5]) @@ -293,24 +293,39 @@ $(document).ready(function() { var content = $("td:eq(5)", row).html(); $("td:eq(5)", row).html(content + " (" + (0.1*data[7]).toFixed(1)+"ms)"); } - + var timestamp = Math.floor(parseFloat(data[0])/1e6); + $("td:eq(0)", row).html(moment.unix(timestamp).format("Y-MM-DD []HH:mm:ss z")); }, dom: "<'row'<'col-sm-12'f>>" + "<'row'<'col-sm-4'l><'col-sm-8'p>>" + "<'row'<'col-sm-12'<'table-responsive'tr>>>" + "<'row'<'col-sm-5'i><'col-sm-7'p>>", - "ajax": {"url": APIstring, "error": handleAjaxError }, + "ajax": { + "url": APIstring, + "error": handleAjaxError, + "dataSrc": function(data){ + var new_data = new Array(); + for(obj of data.data) + { + obj[0] *= parseInt(1e6); + obj[0] += dataIndex++; + new_data.push(obj); + } + return new_data; + } + }, "autoWidth" : false, "processing": true, "order" : [[0, "desc"]], "columns": [ - { "width" : "15%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD []HH:mm:ss z");}else{return data;} }}, + { "width" : "15%"}, { "width" : "4%" }, { "width" : "36%", "render": $.fn.dataTable.render.text() }, { "width" : "8%", "render": $.fn.dataTable.render.text() }, { "width" : "14%", "orderData": 4 }, { "width" : "8%", "orderData": 6 }, - { "width" : "10%", "orderData": 4 } + { "width" : "10%", "orderData": 4 }, + { "width" : "0%", "searchable": false } ], "lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]], "stateSave": true, @@ -359,6 +374,9 @@ $(document).ready(function() { function () { this.style.color=""; } ); api.$("td:eq(3)").css("cursor","pointer"); + }, + "createdRow": function(row, data, dataIndex){ + $("td:eq(7)", row).html(dataIndex); } }); @@ -376,5 +394,3 @@ $(document).ready(function() { $("#resetButton").click( function () { tableApi.search("").draw(); $("#resetButton").hide(); } ); } ); - - From 77931b499a236ddea662b56bfaa51cf9423c3860 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 5 May 2019 13:51:14 +0200 Subject: [PATCH 47/80] Remove extra ID column, this is intentionally done in a separate commit Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 52982576..367128bf 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -324,8 +324,7 @@ $(document).ready(function() { { "width" : "8%", "render": $.fn.dataTable.render.text() }, { "width" : "14%", "orderData": 4 }, { "width" : "8%", "orderData": 6 }, - { "width" : "10%", "orderData": 4 }, - { "width" : "0%", "searchable": false } + { "width" : "10%", "orderData": 4 } ], "lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]], "stateSave": true, @@ -374,9 +373,6 @@ $(document).ready(function() { function () { this.style.color=""; } ); api.$("td:eq(3)").css("cursor","pointer"); - }, - "createdRow": function(row, data, dataIndex){ - $("td:eq(7)", row).html(dataIndex); } }); From 2175efd605c0bfeff746c28382041e8deb8357e9 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 6 May 2019 07:39:22 +0200 Subject: [PATCH 48/80] Use the render option of the columns definition to specify how and when the date string is shown. Signed-off-by: DL6ER --- scripts/pi-hole/js/queries.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 367128bf..bcd3252a 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -293,8 +293,7 @@ $(document).ready(function() { var content = $("td:eq(5)", row).html(); $("td:eq(5)", row).html(content + " (" + (0.1*data[7]).toFixed(1)+"ms)"); } - var timestamp = Math.floor(parseFloat(data[0])/1e6); - $("td:eq(0)", row).html(moment.unix(timestamp).format("Y-MM-DD []HH:mm:ss z")); + }, dom: "<'row'<'col-sm-12'f>>" + "<'row'<'col-sm-4'l><'col-sm-8'p>>" + @@ -318,7 +317,7 @@ $(document).ready(function() { "processing": true, "order" : [[0, "desc"]], "columns": [ - { "width" : "15%"}, + { "width" : "15%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(Math.floor(data/1e6)).format("Y-MM-DD []HH:mm:ss z");}else{return data;} }}, { "width" : "4%" }, { "width" : "36%", "render": $.fn.dataTable.render.text() }, { "width" : "8%", "render": $.fn.dataTable.render.text() }, From a4fe802fb99b53fb0f30613cdf8de2c488f9f718 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 6 May 2019 07:44:11 +0200 Subject: [PATCH 49/80] Apply the same changes also for the database query list Signed-off-by: DL6ER --- scripts/pi-hole/js/db_queries.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index 2482f0ff..74edb06c 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -224,6 +224,7 @@ function refreshTableData() { $(document).ready(function() { var status; + var dataIndex = 0; var APIstring; if(instantquery) @@ -244,7 +245,6 @@ $(document).ready(function() { tableApi = $("#all-queries").DataTable( { "rowCallback": function( row, data, index ){ var blocked, fieldtext, buttontext, color; - switch (data[4]) { case 1: @@ -310,13 +310,26 @@ $(document).ready(function() { "<'row'<'col-sm-4'l><'col-sm-8'p>>" + "<'row'<'col-sm-12'<'table-responsive'tr>>>" + "<'row'<'col-sm-5'i><'col-sm-7'p>>", - "ajax": {"url": APIstring, "error": handleAjaxError }, + "ajax": { + "url": APIstring, + "error": handleAjaxError, + "dataSrc": function(data){ + var new_data = new Array(); + for(obj of data.data) + { + obj[0] *= parseInt(1e6); + obj[0] += dataIndex++; + new_data.push(obj); + } + return new_data; + } + }, "autoWidth" : false, "processing": true, "deferRender": true, "order" : [[0, "desc"]], "columns": [ - { "width" : "15%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(data).format("Y-MM-DD []HH:mm:ss z");}else{return data;} }}, + { "width" : "15%", "render": function (data, type, full, meta) { if(type === "display"){return moment.unix(Math.floor(data/1e6)).format("Y-MM-DD []HH:mm:ss z");}else{return data;} }}, { "width" : "10%" }, { "width" : "40%" }, { "width" : "20%" }, @@ -353,4 +366,3 @@ $("#querytime").on("apply.daterangepicker", function(ev, picker) { $(this).val(picker.startDate.format(dateformat) + " to " + picker.endDate.format(dateformat)); refreshTableData(); }); - From cdefb1ca6097a48e3bead95826fabffab5317cf8 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 May 2019 20:10:32 +0200 Subject: [PATCH 50/80] Use Array.map() instead of looping over elements and pushing the content to a new array Signed-off-by: DL6ER --- scripts/pi-hole/js/db_queries.js | 11 +---------- scripts/pi-hole/js/queries.js | 11 +---------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index 74edb06c..38a30442 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -313,16 +313,7 @@ $(document).ready(function() { "ajax": { "url": APIstring, "error": handleAjaxError, - "dataSrc": function(data){ - var new_data = new Array(); - for(obj of data.data) - { - obj[0] *= parseInt(1e6); - obj[0] += dataIndex++; - new_data.push(obj); - } - return new_data; - } + "dataSrc": function(data){ return data.data.map(function(x){ x[0] = x[0]*1e6+(dataIndex++); return x; }); } }, "autoWidth" : false, "processing": true, diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index bcd3252a..d7dfe0c0 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -302,16 +302,7 @@ $(document).ready(function() { "ajax": { "url": APIstring, "error": handleAjaxError, - "dataSrc": function(data){ - var new_data = new Array(); - for(obj of data.data) - { - obj[0] *= parseInt(1e6); - obj[0] += dataIndex++; - new_data.push(obj); - } - return new_data; - } + "dataSrc": function(data){ return data.data.map(function(x){ x[0] = x[0]*1e6+(dataIndex++); return x; }); } }, "autoWidth" : false, "processing": true, From 19d6f4ae9f08de2ff3c6459490ab203312070537 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 10 May 2019 22:02:52 +0200 Subject: [PATCH 51/80] Move dataIndex definition into dataSrc function Signed-off-by: DL6ER --- scripts/pi-hole/js/db_queries.js | 9 +++++++-- scripts/pi-hole/js/queries.js | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index 38a30442..b60af565 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -224,7 +224,6 @@ function refreshTableData() { $(document).ready(function() { var status; - var dataIndex = 0; var APIstring; if(instantquery) @@ -313,7 +312,13 @@ $(document).ready(function() { "ajax": { "url": APIstring, "error": handleAjaxError, - "dataSrc": function(data){ return data.data.map(function(x){ x[0] = x[0]*1e6+(dataIndex++); return x; }); } + "dataSrc": function(data){ + var dataIndex = 0; + return data.data.map(function(x){ + x[0] = x[0] * 1e6 + (dataIndex++); + return x; + }); + } }, "autoWidth" : false, "processing": true, diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index d7dfe0c0..74e79ee1 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -116,7 +116,6 @@ function autofilter(){ $(document).ready(function() { var status; - var dataIndex = 0; // Do we want to filter queries? var GETDict = {}; @@ -302,7 +301,13 @@ $(document).ready(function() { "ajax": { "url": APIstring, "error": handleAjaxError, - "dataSrc": function(data){ return data.data.map(function(x){ x[0] = x[0]*1e6+(dataIndex++); return x; }); } + "dataSrc": function(data){ + var dataIndex = 0; + return data.data.map(function(x){ + x[0] = x[0] * 1e6 + (dataIndex++); + return x; + }); + } }, "autoWidth" : false, "processing": true, From 3b24170edaa6336aa50943a4af3300c467b7f2ce Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Sun, 26 May 2019 12:27:00 -0700 Subject: [PATCH 52/80] Fix the sidebar toggle from overflowing when hovered over Thanks to @Th3M3 for finding the issue and solution. Fixes #945 Signed-off-by: Mcat12 --- style/pi-hole.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/style/pi-hole.css b/style/pi-hole.css index eec770c6..660a9bc9 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -83,7 +83,7 @@ } } -.main-header>.navbar { +.main-header>.navbar, .navbar>.sidebar-toggle { height: 50px; } From c8086b423910e98198a5fd3a5bb2464dab3b4142 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:24:43 +0200 Subject: [PATCH 53/80] Check fields against null before trying to obtain their lengths. Signed-off-by: DL6ER --- scripts/pi-hole/js/network.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index a851ea2e..a6c90f6d 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -105,7 +105,7 @@ $(document).ready(function() { } // Set hostname to "N/A" if not available - if(data["name"].length < 1) + if(data["name"] && data["name"].length < 1) { $("td:eq(3)", row).html("N/A"); } @@ -121,7 +121,7 @@ $(document).ready(function() { function () { this.style.color=""; } ); // MAC + Vendor field if available - if(data["macVendor"].length > 0) + if(data["macVendor"] && data["macVendor"].length > 0) { $("td:eq(1)", row).html(data["hwaddr"]+"
          "+data["macVendor"]); } From f830bfbc7b9052899fbc646213fe8d4eddc17500 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:30:43 +0200 Subject: [PATCH 54/80] Add IP address sorting for the network table, based on https://datatables.net/plug-ins/sorting/ip-address Signed-off-by: DL6ER --- scripts/pi-hole/js/network.js | 107 +++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index a6c90f6d..ed14ca70 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -61,6 +61,111 @@ function mixColors(ratio, rgb1, rgb2) (1.0-ratio)*rgb1[2]+ratio*rgb2[2]]; } +jQuery.extend( jQuery.fn.dataTableExt.oSort, +{ + "ip-address-pre": function ( a ) + { + if (!a) { return 0 } + var i, item; + var m = a.split("."), + n = a.split(":"), + x = "", + xa = ""; + if (m.length == 4) + { + // IPV4 + for(i = 0; i < m.length; i++) + { + item = m[i]; + + if(item.length == 1) + { + x += "00" + item; + } + else if(item.length == 2) + { + x += "0" + item; + } + else + { + x += item; + } + } + } + else if (n.length > 0) + { + // IPV6 + var count = 0; + for(i = 0; i < n.length; i++) + { + item = n[i]; + + if (i > 0) + { + xa += ":"; + } + + if(item.length === 0) + { + count += 0; + } + else if(item.length == 1) + { + xa += "000" + item; + count += 4; + } + else if(item.length == 2) + { + xa += "00" + item; + count += 4; + } + else if(item.length == 3) + { + xa += "0" + item; + count += 4; + } + else + { + xa += item; + count += 4; + } + } + + // Padding the :: + n = xa.split(":"); + var paddDone = 0; + + for (i = 0; i < n.length; i++) + { + item = n[i]; + if (item.length === 0 && paddDone === 0) + { + for(var padding = 0 ; padding < (32-count) ; padding++) + { + x += "0"; + paddDone = 1; + } + } + else + { + x += item; + } + } + } + return x; + }, + + "ip-address-asc": function ( a, b ) + { + return ((a < b) ? -1 : ((a > b) ? 1 : 0)); + }, + + "ip-address-desc": function ( a, b ) + { + return ((a < b) ? 1 : ((a > b) ? -1 : 0)); + } +}); + $(document).ready(function() { tableApi = $("#network-entries").DataTable( { "rowCallback": function( row, data, index ) @@ -136,7 +241,7 @@ $(document).ready(function() { "processing": true, "order" : [[5, "desc"]], "columns": [ - {data: "ip", "width" : "10%", "render": $.fn.dataTable.render.text() }, + {data: "ip", "type": "ip-address", "width" : "10%", "render": $.fn.dataTable.render.text() }, {data: "hwaddr", "width" : "10%", "render": $.fn.dataTable.render.text() }, {data: "interface", "width" : "4%", "render": $.fn.dataTable.render.text() }, {data: "name", "width" : "15%", "render": $.fn.dataTable.render.text() }, From a027715247b02fee61ae60bf0fa697985a54362a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:43:53 +0200 Subject: [PATCH 55/80] Move ip-address-sorting into its own file Signed-off-by: DL6ER --- network.php | 1 + scripts/pi-hole/js/ip-address-sorting.js | 110 +++++++++++++++++++++++ scripts/pi-hole/js/network.js | 105 ---------------------- 3 files changed, 111 insertions(+), 105 deletions(-) create mode 100644 scripts/pi-hole/js/ip-address-sorting.js diff --git a/network.php b/network.php index cc783f3a..e92f0ef6 100644 --- a/network.php +++ b/network.php @@ -73,4 +73,5 @@ $token = $_SESSION['token']; ?> + diff --git a/scripts/pi-hole/js/ip-address-sorting.js b/scripts/pi-hole/js/ip-address-sorting.js new file mode 100644 index 00000000..552c47e0 --- /dev/null +++ b/scripts/pi-hole/js/ip-address-sorting.js @@ -0,0 +1,110 @@ +/* Pi-hole: A black hole for Internet advertisements +* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* Network-wide ad blocking via your own hardware. +* +* This file is copyright under the latest version of the EUPL. +* Please see LICENSE file for your rights under this license. */ +jQuery.extend( jQuery.fn.dataTableExt.oSort, +{ + "ip-address-pre": function ( a ) + { + if (!a) { return 0 } + var i, item; + var m = a.split("."), + n = a.split(":"), + x = "", + xa = ""; + if (m.length == 4) + { + // IPV4 + for(i = 0; i < m.length; i++) + { + item = m[i]; + + if(item.length == 1) + { + x += "00" + item; + } + else if(item.length == 2) + { + x += "0" + item; + } + else + { + x += item; + } + } + } + else if (n.length > 0) + { + // IPV6 + var count = 0; + for(i = 0; i < n.length; i++) + { + item = n[i]; + + if (i > 0) + { + xa += ":"; + } + + if(item.length === 0) + { + count += 0; + } + else if(item.length == 1) + { + xa += "000" + item; + count += 4; + } + else if(item.length == 2) + { + xa += "00" + item; + count += 4; + } + else if(item.length == 3) + { + xa += "0" + item; + count += 4; + } + else + { + xa += item; + count += 4; + } + } + + // Padding the :: + n = xa.split(":"); + var paddDone = 0; + + for (i = 0; i < n.length; i++) + { + item = n[i]; + if (item.length === 0 && paddDone === 0) + { + for(var padding = 0 ; padding < (32-count) ; padding++) + { + x += "0"; + paddDone = 1; + } + } + else + { + x += item; + } + } + } + return x; + }, + + "ip-address-asc": function ( a, b ) + { + return ((a < b) ? -1 : ((a > b) ? 1 : 0)); + }, + + "ip-address-desc": function ( a, b ) + { + return ((a < b) ? 1 : ((a > b) ? -1 : 0)); + } +}); diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index ed14ca70..352f6046 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -61,111 +61,6 @@ function mixColors(ratio, rgb1, rgb2) (1.0-ratio)*rgb1[2]+ratio*rgb2[2]]; } -jQuery.extend( jQuery.fn.dataTableExt.oSort, -{ - "ip-address-pre": function ( a ) - { - if (!a) { return 0 } - var i, item; - var m = a.split("."), - n = a.split(":"), - x = "", - xa = ""; - if (m.length == 4) - { - // IPV4 - for(i = 0; i < m.length; i++) - { - item = m[i]; - - if(item.length == 1) - { - x += "00" + item; - } - else if(item.length == 2) - { - x += "0" + item; - } - else - { - x += item; - } - } - } - else if (n.length > 0) - { - // IPV6 - var count = 0; - for(i = 0; i < n.length; i++) - { - item = n[i]; - - if (i > 0) - { - xa += ":"; - } - - if(item.length === 0) - { - count += 0; - } - else if(item.length == 1) - { - xa += "000" + item; - count += 4; - } - else if(item.length == 2) - { - xa += "00" + item; - count += 4; - } - else if(item.length == 3) - { - xa += "0" + item; - count += 4; - } - else - { - xa += item; - count += 4; - } - } - - // Padding the :: - n = xa.split(":"); - var paddDone = 0; - - for (i = 0; i < n.length; i++) - { - item = n[i]; - if (item.length === 0 && paddDone === 0) - { - for(var padding = 0 ; padding < (32-count) ; padding++) - { - x += "0"; - paddDone = 1; - } - } - else - { - x += item; - } - } - } - return x; - }, - - "ip-address-asc": function ( a, b ) - { - return ((a < b) ? -1 : ((a > b) ? 1 : 0)); - }, - - "ip-address-desc": function ( a, b ) - { - return ((a < b) ? 1 : ((a > b) ? -1 : 0)); - } -}); - $(document).ready(function() { tableApi = $("#network-entries").DataTable( { "rowCallback": function( row, data, index ) From 83013993cdbdafe878f334480d822e11d1ac3d4c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:48:53 +0200 Subject: [PATCH 56/80] Also show "N/A" if the name column is NULL Signed-off-by: DL6ER --- scripts/pi-hole/js/ip-address-sorting.js | 2 +- scripts/pi-hole/js/network.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/ip-address-sorting.js b/scripts/pi-hole/js/ip-address-sorting.js index 552c47e0..42f05cba 100644 --- a/scripts/pi-hole/js/ip-address-sorting.js +++ b/scripts/pi-hole/js/ip-address-sorting.js @@ -1,5 +1,5 @@ /* Pi-hole: A black hole for Internet advertisements -* (c) 2017 Pi-hole, LLC (https://pi-hole.net) +* (c) 2019 Pi-hole, LLC (https://pi-hole.net) * Network-wide ad blocking via your own hardware. * * This file is copyright under the latest version of the EUPL. diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index 352f6046..48fa178b 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -105,7 +105,7 @@ $(document).ready(function() { } // Set hostname to "N/A" if not available - if(data["name"] && data["name"].length < 1) + if(!data["name"] || data["name"].length < 1) { $("td:eq(3)", row).html("N/A"); } From c7aa2556a1646e4e95ef6392695b6faf2c415646 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 17:27:31 +0200 Subject: [PATCH 57/80] Add source URL to the JS file Signed-off-by: DL6ER --- scripts/pi-hole/js/ip-address-sorting.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/pi-hole/js/ip-address-sorting.js b/scripts/pi-hole/js/ip-address-sorting.js index 42f05cba..6ef1fd3d 100644 --- a/scripts/pi-hole/js/ip-address-sorting.js +++ b/scripts/pi-hole/js/ip-address-sorting.js @@ -4,6 +4,9 @@ * * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license. */ + +// This code has been taken from +// https://datatables.net/plug-ins/sorting/ip-address jQuery.extend( jQuery.fn.dataTableExt.oSort, { "ip-address-pre": function ( a ) From 896be197d0f9dbca9ce9cdd21465934982f69447 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Sun, 28 Apr 2019 19:31:24 -0700 Subject: [PATCH 58/80] Fix audit log button being too wide All of the divs surrounding the button tag were unnecessary. Signed-off-by: Mcat12 --- auditlog.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/auditlog.php b/auditlog.php index 20c36fdb..4f105a70 100644 --- a/auditlog.php +++ b/auditlog.php @@ -75,13 +75,7 @@ -
          -
          -
          - -
          -
          -
          + From bb162f6fcd76b28f8a0e92858d9b040803fc0103 Mon Sep 17 00:00:00 2001 From: Donny Kurnia Date: Tue, 4 Jun 2019 16:08:41 +0700 Subject: [PATCH 59/80] update font-awesome class for clock icon in Fontawesome 5 Signed-off-by: Donny Kurnia --- db_graph.php | 2 +- db_lists.php | 2 +- db_queries.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/db_graph.php b/db_graph.php index a4ce5b47..f6fe1ebf 100644 --- a/db_graph.php +++ b/db_graph.php @@ -30,7 +30,7 @@ $token = $_SESSION['token'];
          - +
          diff --git a/db_lists.php b/db_lists.php index fdeb62c7..014d0a74 100644 --- a/db_lists.php +++ b/db_lists.php @@ -31,7 +31,7 @@ $token = $_SESSION['token'];
          - +
          diff --git a/db_queries.php b/db_queries.php index 7d2c0d37..fd8e3a85 100644 --- a/db_queries.php +++ b/db_queries.php @@ -31,7 +31,7 @@ $token = $_SESSION['token'];
          - +
          From a8b4cb74d8ca4ee3130aa0b98a6ec2e9abcbeae1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 17:20:49 +0200 Subject: [PATCH 60/80] Check data before converting. This allows us to detect if there is no data for the graph so we can hide it. Fixes #932 Signed-off-by: DL6ER --- scripts/pi-hole/js/index.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 17f1e092..21e00bda 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -261,15 +261,16 @@ function updateClientsOverTime() { return; } - // convert received objects to arrays - data.over_time = objectToArray(data.over_time); - // Remove graph if there are no results (e.g. privacy mode enabled) if(jQuery.isEmptyObject(data.over_time)) { $("#clients").parent().remove(); return; } + + // convert received objects to arrays + data.over_time = objectToArray(data.over_time); + // remove last data point since it not representative data.over_time[0].splice(-1,1); var timestamps = data.over_time[0]; From d2ff1f90939f001df1a8af315ebad050cebb4d85 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 17:47:07 +0200 Subject: [PATCH 61/80] Clarify that the router does not always have to be the DHCP server. Fixes #893 Signed-off-by: DL6ER --- settings.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/settings.php b/settings.php index e2063097..73993429 100644 --- a/settings.php +++ b/settings.php @@ -856,11 +856,11 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists" determine the names of devices on your local network. As a result, tables such as Top Clients will only show IP addresses.

          One solution for this is to configure Pi-hole to forward these - requests to your home router, but only for devices on your + requests to your DHCP server (most likely your router), but only for devices on your home network. To configure this we will need to know the IP - address of your router and the name of your local network.

          + address of your DHCP server and the name of your local network.

          Note: The local domain name must match the domain name specified - in your router, likely found within the DHCP settings.

          + in your DHCP server, likely found within the DHCP settings.

          "+printdomain+""; adtable.append(" " + url + - " " + data.top_ads[domain] + " "); + " " + data.top_ads[domain] + " "); } } } diff --git a/scripts/pi-hole/js/db_queries.js b/scripts/pi-hole/js/db_queries.js index b60af565..c7a746b3 100644 --- a/scripts/pi-hole/js/db_queries.js +++ b/scripts/pi-hole/js/db_queries.js @@ -250,7 +250,7 @@ $(document).ready(function() { blocked = true; color = "red"; fieldtext = "Blocked (gravity)"; - buttontext = ""; + buttontext = ""; break; case 2: blocked = false; @@ -268,13 +268,13 @@ $(document).ready(function() { blocked = true; color = "red"; fieldtext = "Blocked (regex/wildcard)"; - buttontext = "" ; + buttontext = "" ; break; case 5: blocked = true; color = "red"; fieldtext = "Blocked (blacklist)"; - buttontext = "" ; + buttontext = "" ; break; case 6: blocked = true; diff --git a/scripts/pi-hole/php/loginpage.php b/scripts/pi-hole/php/loginpage.php index b9128f8b..0545f4f4 100644 --- a/scripts/pi-hole/php/loginpage.php +++ b/scripts/pi-hole/php/loginpage.php @@ -16,7 +16,7 @@
          diff --git a/scripts/vendor/daterangepicker.js b/scripts/vendor/daterangepicker.js index be710052..6aac9665 100644 --- a/scripts/vendor/daterangepicker.js +++ b/scripts/vendor/daterangepicker.js @@ -1,39 +1,34 @@ /** -* @version: 2.1.19 +* @version: 3.0.5 * @author: Dan Grossman http://www.dangrossman.info/ -* @copyright: Copyright (c) 2012-2015 Dan Grossman. All rights reserved. +* @copyright: Copyright (c) 2012-2019 Dan Grossman. All rights reserved. * @license: Licensed under the MIT license. See http://www.opensource.org/licenses/mit-license.php -* @website: https://www.improvely.com/ +* @website: http://www.daterangepicker.com/ */ - -(function(root, factory) { - - if (typeof define === 'function' && define.amd) { - define(['moment', 'jquery', 'exports'], function(momentjs, $, exports) { - root.daterangepicker = factory(root, exports, momentjs, $); - }); - - } else if (typeof exports !== 'undefined') { - var momentjs = require('moment'); - var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined; //isomorphic issue - if (!jQuery) { - try { - jQuery = require('jquery'); - if (!jQuery.fn) jQuery.fn = {}; //isomorphic issue - } catch (err) { - if (!jQuery) throw new Error('jQuery dependency not found'); - } - } - - factory(root, exports, momentjs, jQuery); - - // Finally, as a browser global. - } else { - root.daterangepicker = factory(root, {}, root.moment || moment, (root.jQuery || root.Zepto || root.ender || root.$)); - } - -}(this || {}, function(root, daterangepicker, moment, $) { // 'this' doesn't exist on a server - +// Following the UMD template https://github.com/umdjs/umd/blob/master/templates/returnExportsGlobal.js +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Make globaly available as well + define(['moment', 'jquery'], function (moment, jquery) { + if (!jquery.fn) jquery.fn = {}; // webpack server rendering + if (typeof moment !== 'function' && moment.default) moment = moment.default + return factory(moment, jquery); + }); + } else if (typeof module === 'object' && module.exports) { + // Node / Browserify + //isomorphic issue + var jQuery = (typeof window != 'undefined') ? window.jQuery : undefined; + if (!jQuery) { + jQuery = require('jquery'); + if (!jQuery.fn) jQuery.fn = {}; + } + var moment = (typeof window != 'undefined' && typeof window.moment != 'undefined') ? window.moment : require('moment'); + module.exports = factory(moment, jQuery); + } else { + // Browser globals + root.daterangepicker = factory(root.moment, root.jQuery); + } +}(this, function(moment, $) { var DateRangePicker = function(element, options, cb) { //default settings for options @@ -43,12 +38,15 @@ this.endDate = moment().endOf('day'); this.minDate = false; this.maxDate = false; - this.dateLimit = false; + this.maxSpan = false; this.autoApply = false; this.singleDatePicker = false; this.showDropdowns = false; + this.minYear = moment().subtract(100, 'year').format('YYYY'); + this.maxYear = moment().add(100, 'year').format('YYYY'); this.showWeekNumbers = false; this.showISOWeekNumbers = false; + this.showCustomRangeLabel = true; this.timePicker = false; this.timePicker24Hour = false; this.timePickerIncrement = 1; @@ -67,11 +65,12 @@ this.drops = 'up'; this.buttonClasses = 'btn btn-sm'; - this.applyClass = 'btn-success'; - this.cancelClass = 'btn-default'; + this.applyButtonClasses = 'btn-primary'; + this.cancelButtonClasses = 'btn-default'; this.locale = { - format: 'MM/DD/YYYY', + direction: 'ltr', + format: moment.localeData().longDateFormat('L'), separator: ' - ', applyLabel: 'Apply', cancelLabel: 'Cancel', @@ -99,34 +98,21 @@ //html template for the picker UI if (typeof options.template !== 'string' && !(options.template instanceof $)) - options.template = ' - - -
          - -
          -
          -
          -

          Query Types (over time)

          -
          -
          -
          - -
          -
          -
          - -
          - -
          -
          - -
          -
          -
          -

          Upstreams (over time)

          -
          -
          -
          - -
          -
          -
          - -
          - -
          -
          - -
          - Date: Tue, 27 Aug 2019 16:56:42 +0200 Subject: [PATCH 76/80] Use correct IDs for spinners in index.js Signed-off-by: DL6ER --- scripts/pi-hole/js/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 21e00bda..8058b16e 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -637,8 +637,8 @@ function updateSummaryData(runOnce) { $("#temperature").html(" FTL offline"); // Show spinner $("#queries-over-time .overlay").show(); - $("#forward-destinations .overlay").show(); - $("#query-types .overlay").show(); + $("#forward-destinations-pie .overlay").show(); + $("#query-types-pie .overlay").show(); $("#client-frequency .overlay").show(); $("#domain-frequency .overlay").show(); $("#ad-frequency .overlay").show(); From bfe7b76f346204b3513c795a1ec585e65819885b Mon Sep 17 00:00:00 2001 From: Mark Drobnak Date: Tue, 2 Jul 2019 21:49:26 -0400 Subject: [PATCH 77/80] Merge pull request #974 from pi-hole/fix/admin-email-validation Prevent command injection via admin email --- scripts/pi-hole/php/savesettings.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index d0c39710..6804f6a8 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -56,6 +56,16 @@ function validMAC($mac_addr) return (preg_match('/([a-fA-F0-9]{2}[:]?){6}/', $mac_addr) == 1); } +function validEmail($email) +{ + return filter_var($email, FILTER_VALIDATE_EMAIL) + // Make sure that the email does not contain special characters which + // may be used to execute shell commands, even though they may be valid + // in an email address. If the escaped email does not equal the original + // email, it is not safe to store in setupVars. + && escapeshellcmd($email) === $email; +} + $dhcp_static_leases = array(); function readStaticLeasesFile() { @@ -496,7 +506,7 @@ function readAdlists() { $adminemail = 'noadminemail'; } - elseif(!filter_var($adminemail, FILTER_VALIDATE_EMAIL) || strpos($adminemail, "'") !== false) + elseif(!validEmail($adminemail)) { $error .= "Administrator email address (".htmlspecialchars($adminemail).") is invalid!
          "; } From f3d8f0dfb51bcb2dc6a91565701a5741d64d8456 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 18 Sep 2019 00:36:03 +0200 Subject: [PATCH 78/80] Teleporter expects adlist.json during import, however, the file is called adlists.json during export. Fix this by using singular during export. Signed-off-by: DL6ER --- scripts/pi-hole/php/teleporter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/teleporter.php b/scripts/pi-hole/php/teleporter.php index 8e140bce..87e7752b 100644 --- a/scripts/pi-hole/php/teleporter.php +++ b/scripts/pi-hole/php/teleporter.php @@ -339,7 +339,7 @@ else archive_add_table("whitelist.regex.json", "regex_whitelist"); archive_add_table("blacklist.exact.json", "blacklist"); archive_add_table("blacklist.regex.json", "regex_blacklist"); - archive_add_table("adlists.json", "adlist"); + archive_add_table("adlist.json", "adlist"); archive_add_table("domain_audit.json", "domain_audit"); archive_add_file("/etc/pihole/","setupVars.conf"); archive_add_directory("/etc/dnsmasq.d/","dnsmasq.d/"); From d23df61a498e9b8ceb45aaa3a170d614972b2696 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Wed, 18 Sep 2019 17:16:39 -0400 Subject: [PATCH 79/80] Fix some XSS issues in settings.php Signed-off-by: Mcat12 --- settings.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/settings.php b/settings.php index 61b2f5d5..2406312e 100644 --- a/settings.php +++ b/settings.php @@ -787,7 +787,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
          @@ -1196,19 +1196,19 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists" Pi-hole Ethernet Interface: - + Pi-hole IPv4 address: - + Pi-hole IPv6 address: - + Pi-hole hostname: - + From ba4dd33b9feee6139f062effaab14156cdd6d57c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 3 Oct 2019 08:48:47 +0200 Subject: [PATCH 80/80] Reduce nesting by inverting if statement and rearranging code. Signed-off-by: DL6ER --- scripts/pi-hole/php/gravity.php | 60 ++++++++++++++++----------------- 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/scripts/pi-hole/php/gravity.php b/scripts/pi-hole/php/gravity.php index d29d7cf5..95922804 100644 --- a/scripts/pi-hole/php/gravity.php +++ b/scripts/pi-hole/php/gravity.php @@ -12,14 +12,26 @@ function gravity_last_update($raw = false) { $db = SQLite3_connect(getGravityDBFilename()); $date_file_created_unix = $db->querySingle("SELECT value FROM info WHERE property = 'updated';"); - if($date_file_created_unix !== false) + if($date_file_created_unix === false) { - $date_file_created = date_create("@".intval($date_file_created_unix)); - $date_now = date_create("now"); - $gravitydiff = date_diff($date_file_created,$date_now); if($raw) { - $output = array( + // Array output + return array("file_exists" => false); + } + else + { + // String output + return "Gravity database not available"; + } + } + $date_file_created = date_create("@".intval($date_file_created_unix)); + $date_now = date_create("now"); + $gravitydiff = date_diff($date_file_created,$date_now); + if($raw) + { + // Array output + return array( "file_exists"=> true, "absolute" => $date_file_created_unix, "relative" => array( @@ -28,34 +40,20 @@ function gravity_last_update($raw = false) "minutes" => $gravitydiff->format("%I"), ) ); - } - else - { - if($gravitydiff->d > 1) - { - $output = $gravitydiff->format("Blocking list updated %a days, %H:%I ago"); - } - elseif($gravitydiff->d == 1) - { - $output = $gravitydiff->format("Blocking list updated one day, %H:%I ago"); - } - else - { - $output = $gravitydiff->format("Blocking list updated %H:%I ago"); - } - } } - else + + if($gravitydiff->d > 1) { - if($raw) - { - $output = array("file_exists" => false); - } - else - { - $output = "Gravity database not available"; - } + // String output (more than one day ago) + return $gravitydiff->format("Blocking list updated %a days, %H:%I (hh:mm) ago"); } - return $output; + elseif($gravitydiff->d == 1) + { + // String output (one day ago) + return $gravitydiff->format("Blocking list updated one day, %H:%I (hh:mm) ago"); + } + + // String output (less than one day ago) + return $gravitydiff->format("Blocking list updated %H:%I (hh:mm) ago"); } ?>