From ab31284f263d095527b36c56ff2c2a565dc71730 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 20 Jan 2019 11:54:15 +0100 Subject: [PATCH 001/100] Always show the full requested range. Fill data with zero at the end when the daterange is extending into the future. Similarly, fill data wil zero at the beginning if the requested range extends into a region without recorded queries. Signed-off-by: DL6ER --- api_db.php | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/api_db.php b/api_db.php index 7687fb6e..1fe77449 100644 --- a/api_db.php +++ b/api_db.php @@ -378,15 +378,18 @@ if (isset($_GET['getGraphData']) && $auth) $interval = $q; } + $from = intval($_GET['from']); + $until = intval($_GET['until']); + // Count permitted queries in intervals $stmt = $db->prepare('SELECT (timestamp/:interval)*:interval interval, COUNT(*) FROM queries WHERE (status != 0 )'.$limit.' GROUP by interval ORDER by interval'); - $stmt->bindValue(":from", intval($_GET['from']), SQLITE3_INTEGER); - $stmt->bindValue(":until", intval($_GET['until']), SQLITE3_INTEGER); + $stmt->bindValue(":from", $from, SQLITE3_INTEGER); + $stmt->bindValue(":until", $until, SQLITE3_INTEGER); $stmt->bindValue(":interval", $interval, SQLITE3_INTEGER); $results = $stmt->execute(); // Parse the DB result into graph data, filling in missing sections with zero - function parseDBData($results, $interval) { + function parseDBData($results, $interval, $from, $until) { $data = array(); $min = null; $max = null; @@ -406,7 +409,7 @@ if (isset($_GET['getGraphData']) && $auth) } // Fill the missing intervals with zero - for($i = $min; $i < $max; $i += $interval) { + for($i = min($min,$from); $i < max($max,$until); $i += $interval) { if(!array_key_exists($i, $data)) $data[$i] = 0; } @@ -415,19 +418,19 @@ if (isset($_GET['getGraphData']) && $auth) return $data; } - $domains = parseDBData($results, $interval); + $domains = parseDBData($results, $interval, $from, $until); $result = array('domains_over_time' => $domains); $data = array_merge($data, $result); // Count blocked queries in intervals $stmt = $db->prepare('SELECT (timestamp/:interval)*:interval interval, COUNT(*) FROM queries WHERE (status == 1 OR status == 4 OR status == 5)'.$limit.' GROUP by interval ORDER by interval'); - $stmt->bindValue(":from", intval($_GET['from']), SQLITE3_INTEGER); - $stmt->bindValue(":until", intval($_GET['until']), SQLITE3_INTEGER); + $stmt->bindValue(":from", $from, SQLITE3_INTEGER); + $stmt->bindValue(":until", $until, SQLITE3_INTEGER); $stmt->bindValue(":interval", $interval, SQLITE3_INTEGER); $results = $stmt->execute(); - $addomains = parseDBData($results, $interval); + $addomains = parseDBData($results, $interval, $from, $until); $result = array('ads_over_time' => $addomains); $data = array_merge($data, $result); From 506644b67120f985af7d03e5083065a6df686f0d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 25 Apr 2019 15:02:39 +0200 Subject: [PATCH 002/100] Rewrite web interface to allow interaction with database-based lists Signed-off-by: DL6ER --- api_db.php | 2 +- scripts/pi-hole/js/list.js | 178 ++++++++++++++++++------------------ scripts/pi-hole/php/add.php | 28 +++--- scripts/pi-hole/php/get.php | 123 +++++++++++++++++-------- scripts/pi-hole/php/sub.php | 35 ++----- style/pi-hole.css | 17 ++++ 6 files changed, 214 insertions(+), 169 deletions(-) diff --git a/api_db.php b/api_db.php index 7687fb6e..57a52d62 100644 --- a/api_db.php +++ b/api_db.php @@ -48,7 +48,7 @@ function resolveHostname($clientip, $printIP) return $clientname; } -// Get posible non-standard location of FTL's database +// Get possible non-standard location of FTL's database $FTLsettings = parse_ini_file("/etc/pihole/pihole-FTL.conf"); if(isset($FTLsettings["DBFILE"])) { diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 530a3b3b..26eaa5c5 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -12,6 +12,96 @@ var token = $("#token").html(); var listType = $("#list-type").html(); var fullName = listType === "white" ? "Whitelist" : "Blacklist"; +function refresh(fade) { + var listw; + var list = $("#list"); + if(listType === "black") + { + listw = $("#list-regex"); + } + if(fade) { + list.fadeOut(100); + if(listw) + { + listw.fadeOut(100); + } + } + $.ajax({ + url: "scripts/pi-hole/php/get.php", + method: "get", + data: {"list":listType}, + success: function(response) { + list.html(""); + if(listw) + { + listw.html(""); + } + + if((listType === "black" && + response.blacklist.length === 0 && + response.regex.length === 0) || + (listType === "white" && + response.whitelist.length === 0)) + { + $("h3").hide(); + list.html("
Your " + fullName + " is empty!
"); + } + else + { + $("h3").show(); + if(listType === "white") + { + data = response.whitelist.sort(); + data2 = []; // No regex data, use empty array + } + else if(listType === "black") + { + data = response.blacklist.sort(); + data2 = response.regex.sort(); + } + data.forEach(function (entry, index) + { + var used = entry.enabled === "1" ? "used" : "not-used"; + // Whitelist entry or Blacklist (exact entry) + list.append( + "
  • " + entry.domain + + "
  • "); + // Handle button + $("#list #"+index+"").on("click", "button", function() { + sub(index, entry.domain, "exact"); + }); + }); + + // Add regex domains if present in returned list data + data2.forEach(function (entry, index) + { + var used = entry.enabled === "1" ? "used" : "not-used"; + // Regex entry + listw.append( + "
  • " + entry.filter + + "
  • "); + // Handle button + $("#list-regex #"+index+"").on("click", "button", function() { + sub(index, entry.filter, "regex"); + }); + }); + } + list.fadeIn(100); + if(listw) + { + listw.fadeIn(100); + } + }, + error: function(jqXHR, exception) { + $("#alFailure").show(); + } + }); +} + +window.onload = refresh(false); + function sub(index, entry, arg) { var domain = $("#list #"+index); var locallistType = listType; @@ -38,92 +128,6 @@ function sub(index, entry, arg) { }); } -function refresh(fade) { - var listw; - var list = $("#list"); - if(listType === "black") - { - listw = $("#list-regex"); - } - if(fade) { - list.fadeOut(100); - if(listw) - { - listw.fadeOut(100); - } - } - $.ajax({ - url: "scripts/pi-hole/php/get.php", - method: "get", - data: {"list":listType}, - success: function(response) { - list.html(""); - if(listw) - { - listw.html(""); - } - var data = JSON.parse(response); - - if(data.length === 0) { - $("h3").hide(); - if(listw) - { - listw.html("
    Your " + fullName + " is empty!
    "); - } - else - { - list.html("
    Your " + fullName + " is empty!
    "); - } - } - else - { - $("h3").show(); - data[0] = data[0].sort(); - data[0].forEach(function (entry, index) { - // Whitelist entry or Blacklist (exact entry) are in the zero-th - // array returned by get.php - list.append( - "
  • " + entry + - "
  • "); - // Handle button - $("#list #"+index+"").on("click", "button", function() { - sub(index, entry, "exact"); - }); - }); - - // Add regex domains if present in returned list data - if(data.length === 2) - { - data[1] = data[1].sort(); - data[1].forEach(function (entry, index) { - // Whitelist entry or Blacklist (exact entry) are in the zero-th - // array returned by get.php - listw.append( - "
  • " + entry + - "
  • "); - // Handle button - $("#list-regex #"+index+"").on("click", "button", function() { - sub(index, entry, "regex"); - }); - }); - } - } - list.fadeIn(100); - if(listw) - { - listw.fadeIn(100); - } - }, - error: function(jqXHR, exception) { - $("#alFailure").show(); - } - }); -} - -window.onload = refresh(false); - function add(arg) { var locallistType = listType; var domain = $("#domain"); @@ -161,7 +165,7 @@ function add(arg) { alInfo.delay(8000).fadeOut(2000, function() { alInfo.hide(); }); - } else if (!wild && response.indexOf("] Pi-hole blocking is ") === -1 || + } else if (!wild && response.indexOf("DONE") === -1 || wild && response.length > 1) { alFailure.show(); err.html(response); diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php index 762ea2cf..476b2410 100644 --- a/scripts/pi-hole/php/add.php +++ b/scripts/pi-hole/php/add.php @@ -22,36 +22,36 @@ if($type !== "regex") { check_domain(); } +// Escape shell metacharacters +$domains = escapeshellcmd($_POST['domain']); + switch($type) { case "white": if(!isset($_POST["auditlog"])) - echo shell_exec("sudo pihole -w ${_POST['domain']}"); + echo shell_exec("sudo pihole -w --web ".$domains); else { - echo shell_exec("sudo pihole -w -n ${_POST['domain']}"); - echo shell_exec("sudo pihole -a audit ${_POST['domain']}"); + echo shell_exec("sudo pihole -w --web -n ".$domains); + echo shell_exec("sudo pihole -a audit ".$domains); } break; case "black": if(!isset($_POST["auditlog"])) - echo shell_exec("sudo pihole -b ${_POST['domain']}"); + echo shell_exec("sudo pihole -b --web ".$domains); else { - echo shell_exec("sudo pihole -b -n ${_POST['domain']}"); - echo shell_exec("sudo pihole -a audit ${_POST['domain']}"); + echo shell_exec("sudo pihole -b --web -n ".$domains); + echo shell_exec("sudo pihole -a audit ".$domains); } break; - case "wild": - // Escape "." so it won't be interpreted as the wildcard character - $domain = str_replace(".","\.",$_POST['domain']); - // Add regex filter for legacy wildcard behavior - add_regex("(^|\.)".$domain."$"); - break; case "regex": - add_regex($_POST['domain']); + echo shell_exec("sudo pihole --regex --web ".$domains); + break; + case "wild": + echo shell_exec("sudo pihole --wild --web ".$domains); break; case "audit": - echo exec("sudo pihole -a audit ${_POST['domain']}"); + echo exec("sudo pihole -a audit ".$domain); break; } diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php index 156622c7..5a707b49 100644 --- a/scripts/pi-hole/php/get.php +++ b/scripts/pi-hole/php/get.php @@ -11,57 +11,100 @@ if(!isset($_GET['list'])) $listtype = $_GET['list']; -$basedir = "/etc/pihole/"; +require_once("func.php"); -require_once "func.php"; - -switch ($listtype) { - case "white": - $list = array(getListContent("whitelist.txt")); - break; - - case "black": - $exact = getListContent("blacklist.txt"); - $regex = getListContent("regex.list"); - $list = array($exact, $regex); - break; - - default: - die("Invalid list parameter"); - break; +// Get possible non-standard location of FTL's database +$FTLsettings = parse_ini_file("/etc/pihole/pihole-FTL.conf"); +if(isset($FTLsettings["GRAVITYDB"])) +{ + $GRAVITYDB = $FTLsettings["GRAVITYDB"]; +} +else +{ + $GRAVITYDB = "/etc/pihole/gravity.db"; } +function SQLite3_connect($dbfile, $trytoreconnect) +{ + try + { + // connect to database + return new SQLite3($dbfile, SQLITE3_OPEN_READONLY); + } + catch (Exception $exception) + { + // sqlite3 throws an exception when it is unable to connect, try to reconnect after 3 seconds + if($trytoreconnect) + { + sleep(3); + $db = SQLite3_connect($dbfile, false); + } + } +} -function getListContent($listname) { - global $basedir; - $rawList = file_get_contents(checkfile($basedir.$listname)); - $list = explode("\n", $rawList); +if(strlen($GRAVITYDB) > 0) +{ + $db = SQLite3_connect($GRAVITYDB, true); - // Get rid of empty lines and comments - for($i = sizeof($list)-1; $i >= 0; $i--) { - if(strlen($list[$i]) < 1 || $list[$i][0] === '#') - unset($list[$i]); - } + // Check if we successfully opened the database + if(!$db) + { + die("Error connecting to database"); + } +} +else +{ + die("No database available"); +} - // Re-index list after possible unset() activity - $newlist = array_values($list); +function getTableContent($listname) { + global $db; + $entries = array(); + $results = $db->query("SELECT * FROM $listname"); - return $newlist; + while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) + { + array_push($entries, $res); + } + return array($listname => $entries); } function filterArray(&$inArray) { - $outArray = array(); - foreach ($inArray as $key=>$value) { - if (is_array($value)) { - $outArray[htmlspecialchars($key)] = filterArray($value); - } else { - $outArray[htmlspecialchars($key)] = htmlspecialchars($value); - } - } - return $outArray; + $outArray = array(); + foreach ($inArray as $key => $value) + { + if (is_array($value)) + { + $outArray[htmlspecialchars($key)] = filterArray($value); + } + else + { + $outArray[htmlspecialchars($key)] = htmlspecialchars($value); + } + } + return $outArray; } +switch ($listtype) +{ + case "white": + $list = getTableContent("whitelist"); + break; + + case "black": + $exact = getTableContent("blacklist"); + $regex = getTableContent("regex"); + $list = array_merge($exact, $regex); + break; + + default: + die("Invalid list parameter"); + break; +} // Protect against XSS attacks -$list = filterArray($list); -echo json_encode(array_values($list)); +$output = filterArray($list); + +// Return results +header('Content-type: application/json'); +echo json_encode($output); diff --git a/scripts/pi-hole/php/sub.php b/scripts/pi-hole/php/sub.php index 1c9bc8fe..97c8f84b 100644 --- a/scripts/pi-hole/php/sub.php +++ b/scripts/pi-hole/php/sub.php @@ -16,43 +16,24 @@ if (empty($api)) { list_verify($type); } -// 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 +// 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") { check_domain(); } +// Escape shell metacharacters +$domain = escapeshellcmd($_POST['domain']); + switch($type) { case "white": - exec("sudo pihole -w -q -d ${_POST['domain']}"); + exec("sudo pihole -w -q -d ".$domain); break; case "black": - exec("sudo pihole -b -q -d ${_POST['domain']}"); + exec("sudo pihole -b -q -d ".$domain); break; case "regex": - if(($list = file_get_contents($regexfile)) === FALSE) - { - $err = error_get_last()["message"]; - echo "Unable to read ${regexfile}
    Error message: $err"; - } - - // Remove the regex and any empty lines from the list - $list = explode("\n", $list); - $list = array_diff($list, array($_POST['domain'], "")); - $list = implode("\n", $list); - - if(file_put_contents($regexfile, $list."\n") === FALSE) - { - $err = error_get_last()["message"]; - echo "Unable to remove regex \"".htmlspecialchars($_POST['domain'])."\" from ${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"); - } + exec("sudo pihole --regex -q -d ".$domain); break; } diff --git a/style/pi-hole.css b/style/pi-hole.css index eec770c6..311731c5 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -24,6 +24,23 @@ .skin-blue .list-group-item:hover { background: #ddd; } + +.skin-blue .not-used { + background: #eee; +} + +.skin-blue .not-used:hover { + background: #c5c5c95; +} + +.skin-blue .used { + background: #fff; +} + +.skin-blue .used:hover { + background: #ddd; +} + @-webkit-keyframes Pulse{ from {color:#630030;-webkit-text-shadow:0 0 2px transparent;} 50% {color:#e33100;-webkit-text-shadow:0 0 5px #e33100;} From d8613aa53b0e4088c3c8c9ac98fd32409bcf08d3 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 25 Apr 2019 15:48:26 +0200 Subject: [PATCH 003/100] Show comments when available Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 26eaa5c5..db1c62b3 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -62,9 +62,10 @@ function refresh(fade) { data.forEach(function (entry, index) { var used = entry.enabled === "1" ? "used" : "not-used"; + var comment = entry.comment.length > 0 ? " - " + entry.comment : ""; // Whitelist entry or Blacklist (exact entry) list.append( - "
  • " + entry.domain + + "
  • " + entry.domain + comment + "
  • "); // Handle button @@ -77,9 +78,10 @@ function refresh(fade) { data2.forEach(function (entry, index) { var used = entry.enabled === "1" ? "used" : "not-used"; + var comment = entry.comment.length > 0 ? "(" + entry.comment + ")" : ""; // Regex entry listw.append( - "
  • " + entry.filter + + "
  • " + entry.filter + comment + "
  • "); // Handle button From cedc59ba78757c51cfda4939319db80a3039fc9a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 25 Apr 2019 16:06:24 +0200 Subject: [PATCH 004/100] Show date added Signed-off-by: DL6ER --- scripts/pi-hole/js/list.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index db1c62b3..71ee3532 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -63,9 +63,14 @@ function refresh(fade) { { var used = entry.enabled === "1" ? "used" : "not-used"; var comment = entry.comment.length > 0 ? " - " + entry.comment : ""; + var time = new Date(parseInt(entry.date_added)*1000); + var added = "Added: " + time.toLocaleString(); + console.log(time); // Whitelist entry or Blacklist (exact entry) list.append( - "
  • " + entry.domain + comment + + "
  • " + + "" + + entry.domain + comment + "" + "
  • "); // Handle button @@ -78,10 +83,14 @@ function refresh(fade) { data2.forEach(function (entry, index) { var used = entry.enabled === "1" ? "used" : "not-used"; - var comment = entry.comment.length > 0 ? "(" + entry.comment + ")" : ""; + var comment = entry.comment.length > 0 ? " - " + entry.comment : ""; + var time = new Date(parseInt(entry.date_added)*1000); + var added = "Added: " + time.toLocaleString(); // Regex entry listw.append( - "
  • " + entry.filter + comment + + "
  • " + + "" + + entry.filter + comment + "" + "
  • "); // Handle button From 8b0ee8f4fa35491e69f2b48bb7cacaa5f53a85eb Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 26 Apr 2019 17:58:27 +0200 Subject: [PATCH 005/100] Reduce code duplication Signed-off-by: DL6ER --- api_db.php | 33 ++------------------- scripts/pi-hole/js/list.js | 57 +++++++++++++++---------------------- scripts/pi-hole/php/add.php | 4 +-- scripts/pi-hole/php/get.php | 34 ++-------------------- 4 files changed, 29 insertions(+), 99 deletions(-) diff --git a/api_db.php b/api_db.php index 57a52d62..00c6ba2e 100644 --- a/api_db.php +++ b/api_db.php @@ -8,6 +8,7 @@ $api = true; header('Content-type: application/json'); +require("scripts/pi-hole/php/database.php"); require("scripts/pi-hole/php/password.php"); require("scripts/pi-hole/php/auth.php"); check_cors(); @@ -62,37 +63,7 @@ else // Needs package php5-sqlite, e.g. // sudo apt-get install php5-sqlite -function SQLite3_connect($trytoreconnect) -{ - global $DBFILE; - try - { - // connect to database - return new SQLite3($DBFILE, SQLITE3_OPEN_READONLY); - } - catch (Exception $exception) - { - // sqlite3 throws an exception when it is unable to connect, try to reconnect after 3 seconds - if($trytoreconnect) - { - sleep(3); - $db = SQLite3_connect(false); - } - } -} - -if(strlen($DBFILE) > 0) -{ - $db = SQLite3_connect(true); -} -else -{ - die("No database available"); -} -if(!$db) -{ - die("Error connecting to database"); -} +$db = SQLite3_connect($DBFILE); if(isset($_GET["network"]) && $auth) { diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index 71ee3532..0dd24fb8 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -12,6 +12,25 @@ var token = $("#token").html(); var listType = $("#list-type").html(); var fullName = listType === "white" ? "Whitelist" : "Blacklist"; +function addListEntry(entry, index, list, button, type) +{ + var used = entry.enabled === "1" ? "used" : "not-used"; + var comment = entry.comment.length > 0 ? " - " + entry.comment : ""; + var time = new Date(parseInt(entry.date_added)*1000); + var added = "Added: " + time.toLocaleString(); + list.append( + "
  • " + + "" + + entry.domain + comment + "" + + "
  • " + ); + // Handle button + $(button+" #"+index).on("click", "button", function() { + sub(index, entry.domain, type); + }); +} + function refresh(fade) { var listw; var list = $("#list"); @@ -61,42 +80,13 @@ function refresh(fade) { } data.forEach(function (entry, index) { - var used = entry.enabled === "1" ? "used" : "not-used"; - var comment = entry.comment.length > 0 ? " - " + entry.comment : ""; - var time = new Date(parseInt(entry.date_added)*1000); - var added = "Added: " + time.toLocaleString(); - console.log(time); - // Whitelist entry or Blacklist (exact entry) - list.append( - "
  • " + - "" + - entry.domain + comment + "" + - "
  • "); - // Handle button - $("#list #"+index+"").on("click", "button", function() { - sub(index, entry.domain, "exact"); - }); + addListEntry(entry, index, list, "#list", "exact"); }); // Add regex domains if present in returned list data data2.forEach(function (entry, index) { - var used = entry.enabled === "1" ? "used" : "not-used"; - var comment = entry.comment.length > 0 ? " - " + entry.comment : ""; - var time = new Date(parseInt(entry.date_added)*1000); - var added = "Added: " + time.toLocaleString(); - // Regex entry - listw.append( - "
  • " + - "" + - entry.filter + comment + "" + - "
  • "); - // Handle button - $("#list-regex #"+index+"").on("click", "button", function() { - sub(index, entry.filter, "regex"); - }); + addListEntry(entry, index, listw, "#list-regex", "regex"); }); } list.fadeIn(100); @@ -167,7 +157,7 @@ function add(arg) { method: "post", data: {"domain":domain.val().trim(), "list":locallistType, "token":token}, success: function(response) { - if (!wild && response.indexOf(" already exists in ") !== -1) { + if (response.indexOf(" already exists in ") !== -1) { alWarning.show(); warn.html(response); alWarning.delay(8000).fadeOut(2000, function() { @@ -176,8 +166,7 @@ function add(arg) { alInfo.delay(8000).fadeOut(2000, function() { alInfo.hide(); }); - } else if (!wild && response.indexOf("DONE") === -1 || - wild && response.length > 1) { + } else if (response.indexOf("DONE") === -1) { alFailure.show(); err.html(response); alFailure.delay(8000).fadeOut(2000, function() { diff --git a/scripts/pi-hole/php/add.php b/scripts/pi-hole/php/add.php index 476b2410..dbb9df2e 100644 --- a/scripts/pi-hole/php/add.php +++ b/scripts/pi-hole/php/add.php @@ -31,7 +31,7 @@ switch($type) { echo shell_exec("sudo pihole -w --web ".$domains); else { - echo shell_exec("sudo pihole -w --web -n ".$domains); + echo shell_exec("sudo pihole -w --web ".$domains); echo shell_exec("sudo pihole -a audit ".$domains); } break; @@ -40,7 +40,7 @@ switch($type) { echo shell_exec("sudo pihole -b --web ".$domains); else { - echo shell_exec("sudo pihole -b --web -n ".$domains); + echo shell_exec("sudo pihole -b --web ".$domains); echo shell_exec("sudo pihole -a audit ".$domains); } break; diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php index 5a707b49..cbf77bae 100644 --- a/scripts/pi-hole/php/get.php +++ b/scripts/pi-hole/php/get.php @@ -24,38 +24,8 @@ else $GRAVITYDB = "/etc/pihole/gravity.db"; } -function SQLite3_connect($dbfile, $trytoreconnect) -{ - try - { - // connect to database - return new SQLite3($dbfile, SQLITE3_OPEN_READONLY); - } - catch (Exception $exception) - { - // sqlite3 throws an exception when it is unable to connect, try to reconnect after 3 seconds - if($trytoreconnect) - { - sleep(3); - $db = SQLite3_connect($dbfile, false); - } - } -} - -if(strlen($GRAVITYDB) > 0) -{ - $db = SQLite3_connect($GRAVITYDB, true); - - // Check if we successfully opened the database - if(!$db) - { - die("Error connecting to database"); - } -} -else -{ - die("No database available"); -} +require("database.php"); +$db = SQLite3_connect($GRAVITYDB); function getTableContent($listname) { global $db; From 406a946b24202517a7b2988f914fb1d4a39b1641 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 26 Apr 2019 18:32:33 +0200 Subject: [PATCH 006/100] Add new file scripts/pi-hole/php/database.php Signed-off-by: DL6ER --- scripts/pi-hole/php/database.php | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 scripts/pi-hole/php/database.php diff --git a/scripts/pi-hole/php/database.php b/scripts/pi-hole/php/database.php new file mode 100644 index 00000000..72d06322 --- /dev/null +++ b/scripts/pi-hole/php/database.php @@ -0,0 +1,42 @@ + 0) + { + $db = SQLite3_connect_try($filename, $mode, true); + } + else + { + die("No database available"); + } + if(!$db) + { + die("Error connecting to database"); + } + return $db; +} +?> From ced7174c37ce3e420d34a46277796b462dd50813 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 28 Apr 2019 20:42:50 +0200 Subject: [PATCH 007/100] Modify adlists subpage of the settings page to sources the lists from the gravity database. Signed-off-by: DL6ER --- scripts/pi-hole/php/database.php | 17 +++++++++++- scripts/pi-hole/php/get.php | 12 +------- scripts/pi-hole/php/savesettings.php | 41 +++++++++------------------- settings.php | 4 +-- 4 files changed, 32 insertions(+), 42 deletions(-) diff --git a/scripts/pi-hole/php/database.php b/scripts/pi-hole/php/database.php index 72d06322..3a453677 100644 --- a/scripts/pi-hole/php/database.php +++ b/scripts/pi-hole/php/database.php @@ -6,8 +6,23 @@ * This file is copyright under the latest version of the EUPL. * Please see LICENSE file for your rights under this license */ +function getGravityDBFilename() +{ + // Get possible non-standard location of FTL's database + $FTLsettings = parse_ini_file("/etc/pihole/pihole-FTL.conf"); + if(isset($FTLsettings["GRAVITYDB"])) + { + return $FTLsettings["GRAVITYDB"]; + } + else + { + return "/etc/pihole/gravity.db"; + } +} + function SQLite3_connect_try($filename, $mode, $trytoreconnect) -{ try +{ + try { // connect to database return new SQLite3($filename, $mode); diff --git a/scripts/pi-hole/php/get.php b/scripts/pi-hole/php/get.php index cbf77bae..d2b75b95 100644 --- a/scripts/pi-hole/php/get.php +++ b/scripts/pi-hole/php/get.php @@ -13,18 +13,8 @@ $listtype = $_GET['list']; require_once("func.php"); -// Get possible non-standard location of FTL's database -$FTLsettings = parse_ini_file("/etc/pihole/pihole-FTL.conf"); -if(isset($FTLsettings["GRAVITYDB"])) -{ - $GRAVITYDB = $FTLsettings["GRAVITYDB"]; -} -else -{ - $GRAVITYDB = "/etc/pihole/gravity.db"; -} - require("database.php"); +$GRAVITYDB = getGravityDBFilename(); $db = SQLite3_connect($GRAVITYDB); function getTableContent($listname) { diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index fc358d66..ed7f2556 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -155,37 +155,22 @@ function readDNSserversList() return $list; } +require_once("database.php"); $adlist = []; function readAdlists() { // Reset list $list = []; - $handle = @fopen("/etc/pihole/adlists.list", "r"); - if ($handle) + $db = SQLite3_connect(getGravityDBFilename()); + if ($db) { - while (($line = fgets($handle)) !== false) + $results = $db->query("SELECT * FROM adlists"); + + while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) { - if(strlen($line) < 3) - { - continue; - } - elseif($line[0] === "#") - { - // Comments start either with "##" or "# " - if($line[1] !== "#" && - $line[1] !== " ") - { - // Commented list - array_push($list, [false,rtrim(substr($line, 1))]); - } - } - else - { - // Active list - array_push($list, [true,rtrim($line)]); - } + array_push($list, $res); } - fclose($handle); + $db->close(); } return $list; } @@ -689,18 +674,18 @@ function readAdlists() if(isset($_POST["adlist-del-".$key])) { // Delete list - exec("sudo pihole -a adlist del ".escapeshellcmd($value[1])); + exec("sudo pihole -a adlist del ".escapeshellcmd($value["address"])); } - elseif(isset($_POST["adlist-enable-".$key]) && !$value[0]) + elseif(isset($_POST["adlist-enable-".$key]) && $value["enabled"] !== 1) { // Is not enabled, but should be - exec("sudo pihole -a adlist enable ".escapeshellcmd($value[1])); + exec("sudo pihole -a adlist enable ".escapeshellcmd($value["address"])); } - elseif(!isset($_POST["adlist-enable-".$key]) && $value[0]) + elseif(!isset($_POST["adlist-enable-".$key]) && $value["enabled"] === 1) { // Is enabled, but shouldn't be - exec("sudo pihole -a adlist disable ".escapeshellcmd($value[1])); + exec("sudo pihole -a adlist disable ".escapeshellcmd($value["address"])); } } diff --git a/settings.php b/settings.php index 3a4f8ffe..44133542 100644 --- a/settings.php +++ b/settings.php @@ -266,10 +266,10 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists" $value) { ?> - checked> + checked> - + " target="_new" id="adlist-text-"> " From 05901278659244508fc0c39f3d25cd372baf0d85 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 5 May 2019 13:49:22 +0200 Subject: [PATCH 013/100] 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 c4df1bcceda86339253a5110bca223628e32f10a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 5 May 2019 13:51:14 +0200 Subject: [PATCH 014/100] 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 ce88417d553c67538e7e84c28d42fe258b2d6ed0 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 6 May 2019 07:39:22 +0200 Subject: [PATCH 015/100] 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 1d5b212d9325cbc2c32212170de30868d37018f0 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Mon, 6 May 2019 07:44:11 +0200 Subject: [PATCH 016/100] 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 6d896f98920d2c08162181d92ddd532d0daeba2b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 9 May 2019 20:10:32 +0200 Subject: [PATCH 017/100] 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 468cd0ccfda6cc0bea6660b48e23557415f05ba4 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 10 May 2019 22:02:52 +0200 Subject: [PATCH 018/100] 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 7c50e0ba8d9c947da6035dd3032005a54542629b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20J=C3=A4ckel?= Date: Sat, 18 May 2019 23:38:00 +0200 Subject: [PATCH 019/100] Update link to git-scm.com, make the checkboxes actual checkboxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Sandro Jäckel --- .github/PULL_REQUEST_TEMPLATE.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7c288989..1be609f0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -2,15 +2,15 @@ `{Please ensure that your pull request is for the 'devel' branch!}` -- [] I have read and understood the [contributors guide](https://github.com/pi-hole/AdminLTE/blob/master/CONTRIBUTING.md), as well as this entire template. -- [] I have made only one major change in my proposed changes. -- [] I have commented my proposed changes within the code. -- [] I have tested my proposed changes. -- [] I am willing to help maintain this change if there are issues with it later. -- [] I give this submission freely and claim no ownership. -- [] It is compatible with the [EUPL 1.2 license](https://opensource.org/licenses/EUPL-1.1) -- [] I have squashed any insignificant commits. ([`git rebase`](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)) -- [] I have Signed Off all commits. ([`git commit --signoff`](https://git-scm.com/docs/git-commit#git-commit---signoff)) +- [ ] I have read and understood the [contributors guide](https://github.com/pi-hole/AdminLTE/blob/master/CONTRIBUTING.md), as well as this entire template. +- [ ] I have made only one major change in my proposed changes. +- [ ] I have commented my proposed changes within the code. +- [ ] I have tested my proposed changes. +- [ ] I am willing to help maintain this change if there are issues with it later. +- [ ] I give this submission freely and claim no ownership. +- [ ] It is compatible with the [EUPL 1.2 license](https://opensource.org/licenses/EUPL-1.1) +- [ ] I have squashed any insignificant commits. ([`git rebase`](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)) +- [ ] I have Signed Off all commits. ([`git commit --signoff`](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---signoff)) --- From db68782f6201c87a0546c36011e6c4fee20c683a Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Sun, 19 May 2019 14:16:55 -0700 Subject: [PATCH 020/100] Replace fa-refresh with fa-sync Font Awesome 5 replaced the refresh icon from 4 with fa-sync: https://fontawesome.com/how-to-use/on-the-web/setup/upgrading-from-version-4#name-changes Fixes #941 Signed-off-by: Mcat12 --- auditlog.php | 4 ++-- db_graph.php | 2 +- db_lists.php | 6 +++--- index.php | 20 ++++++++++---------- list.php | 2 +- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/auditlog.php b/auditlog.php index 86ebb685..20c36fdb 100644 --- a/auditlog.php +++ b/auditlog.php @@ -35,7 +35,7 @@
    - +
    @@ -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 dd683cffc14bc2decb40950f4d33f996a3080437 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Sun, 28 Apr 2019 19:31:24 -0700 Subject: [PATCH 021/100] 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 7b796f2ad83e95e2b1b093e9486de42f74ab1cd3 Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Sun, 26 May 2019 12:27:00 -0700 Subject: [PATCH 022/100] 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 311731c5..1f47baa7 100644 --- a/style/pi-hole.css +++ b/style/pi-hole.css @@ -100,7 +100,7 @@ } } -.main-header>.navbar { +.main-header>.navbar, .navbar>.sidebar-toggle { height: 50px; } From 09eb4f5dc3781a292d80fc3b1ffc8e518b503270 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:24:43 +0200 Subject: [PATCH 023/100] 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 5f79025cc9330d0301f87e495bc9b26f677ca129 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:30:43 +0200 Subject: [PATCH 024/100] 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 dddc7910524b902d04b58d304d86b12b6cfb8130 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:43:53 +0200 Subject: [PATCH 025/100] 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 0e2d30dd11a42ba9f9c80bcab3a144890e9d56ae Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 12:48:53 +0200 Subject: [PATCH 026/100] 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 611db2b4a960e7b2c8c2ea5b44eb11fd09942d12 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 17:27:31 +0200 Subject: [PATCH 027/100] 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 72abc5dad4e132a53fb0e7c9d2f6e67f329acc2d Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 22:05:13 +0200 Subject: [PATCH 028/100] Add button for pihole arpflush on Pi-hole settings page Signed-off-by: DL6ER --- scripts/pi-hole/js/settings.js | 10 +++++----- scripts/pi-hole/php/savesettings.php | 9 +++++++++ settings.php | 9 +++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index a3b99195..51f8a09a 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -82,19 +82,19 @@ $(".confirm-flushlogs").confirm({ dialogClass: "modal-dialog modal-mg" }); -$(".confirm-disablelogging").confirm({ - text: "Are you sure you want to disable logging and flush your Pi-hole logs?", +$(".confirm-flusharp").confirm({ + text: "Are you sure you want to flush your network table?", title: "Confirmation required", confirm(button) { - $("#disablelogsform").submit(); + $("#flusharpform").submit(); }, cancel(button) { // nothing to do }, - confirmButton: "Yes, disable logs and flush my logs", + confirmButton: "Yes, flush my network table", cancelButton: "No, go back", post: true, - confirmButtonClass: "btn-danger", + confirmButtonClass: "btn-warning", cancelButtonClass: "btn-success", dialogClass: "modal-dialog modal-mg" }); diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index ed7f2556..7a532fa8 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -735,6 +735,15 @@ function readAdlists() $error .= "Invalid privacy level (".$level.")!"; } break; + // Flush network table + case "flusharp": + exec("sudo pihole arpflush quiet", $output); + $error = implode($output, "
    "); + if(strlen($error) == 0) + { + $success .= "The network table has been flushed"; + } + break; default: // Option not found diff --git a/settings.php b/settings.php index a77e3995..862864ed 100644 --- a/settings.php +++ b/settings.php @@ -1331,9 +1331,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists"
    - - - +
    @@ -1359,9 +1357,8 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "blocklists" -
    - - + +
    From 43fa24fbea5d52b22d0bb03c19250aa3ff57cca0 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 May 2019 22:11:13 +0200 Subject: [PATCH 029/100] Glue needs to be the first argument of implode Signed-off-by: DL6ER --- scripts/pi-hole/php/savesettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index 7a532fa8..c33a18a0 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -738,7 +738,7 @@ function readAdlists() // Flush network table case "flusharp": exec("sudo pihole arpflush quiet", $output); - $error = implode($output, "
    "); + $error = implode("
    ", $output); if(strlen($error) == 0) { $success .= "The network table has been flushed"; From 8d18cfcd8faa8ccfe61c141e0b8588dd02e4e772 Mon Sep 17 00:00:00 2001 From: Donny Kurnia Date: Tue, 4 Jun 2019 16:08:41 +0700 Subject: [PATCH 030/100] 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 4aea8999cb79eef5734d3ae49565dcf0486fbaab Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 11 Jun 2019 00:18:54 +0200 Subject: [PATCH 031/100] Offer multiple IP addresses through API if available Signed-off-by: DL6ER --- api_db.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api_db.php b/api_db.php index 00c6ba2e..bd23e828 100644 --- a/api_db.php +++ b/api_db.php @@ -71,7 +71,16 @@ if(isset($_GET["network"]) && $auth) $results = $db->query('SELECT * FROM network'); while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) + { + $id = $res["id"]; + $ipaddr = array(); + $ips = $db->query("SELECT ip FROM network_addresses WHERE id = $id"); + while($ips !== false && $ip = $ips->fetchArray(SQLITE3_ASSOC)) + array_push($ipaddr,$ip["ip"]); + if(count($ipaddr) > 0) + $res["ip"] = $ipaddr; array_push($network, $res); + } $data = array_merge($data, array('network' => $network)); } From 0895a6f04442a2075cbefdcce32ad64c28670aaf Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Jun 2019 18:09:27 +0200 Subject: [PATCH 032/100] network_addresses(id) has been renamed to netword_addresses(network_id) Signed-off-by: DL6ER --- api_db.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_db.php b/api_db.php index bd23e828..747f114f 100644 --- a/api_db.php +++ b/api_db.php @@ -74,7 +74,7 @@ if(isset($_GET["network"]) && $auth) { $id = $res["id"]; $ipaddr = array(); - $ips = $db->query("SELECT ip FROM network_addresses WHERE id = $id"); + $ips = $db->query("SELECT ip FROM network_addresses WHERE network_id = $id"); while($ips !== false && $ip = $ips->fetchArray(SQLITE3_ASSOC)) array_push($ipaddr,$ip["ip"]); if(count($ipaddr) > 0) From 425b6ba5a7fda70f71e0f4a2ec74c33a5798c262 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Jun 2019 19:17:52 +0200 Subject: [PATCH 033/100] Show at only the three most recent IP addresses for each device. The hover text of contains all IP addresses. Signed-off-by: DL6ER --- api_db.php | 2 +- scripts/pi-hole/js/network.js | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/api_db.php b/api_db.php index 747f114f..b2015e81 100644 --- a/api_db.php +++ b/api_db.php @@ -74,7 +74,7 @@ if(isset($_GET["network"]) && $auth) { $id = $res["id"]; $ipaddr = array(); - $ips = $db->query("SELECT ip FROM network_addresses WHERE network_id = $id"); + $ips = $db->query("SELECT ip FROM network_addresses WHERE network_id = $id ORDER BY lastSeen DESC"); while($ips !== false && $ip = $ips->fetchArray(SQLITE3_ASSOC)) array_push($ipaddr,$ip["ip"]); if(count($ipaddr) > 0) diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index 48fa178b..52aa2ec0 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -8,6 +8,9 @@ var tableApi; var APIstring = "api_db.php?network"; +// How many IPs do we show at most per device? +var MAXIPDISPLAY = 3; + function refreshData() { tableApi.ajax.url(APIstring).load(); } @@ -113,12 +116,15 @@ $(document).ready(function() { // Set number of queries to localized string (add thousand separators) $("td:eq(6)", row).html(data["numQueries"].toLocaleString()); - // Client -> jump to Query Log on click - $("td:eq(0)", row).click( function () { openInNewTab("/admin/queries.php?client="+this.innerHTML); } ); - $("td:eq(0)", row).css("cursor","pointer"); - $("td:eq(0)", row).hover( - function () { this.title="Click to show recent queries made by "+this.innerHTML; this.style.color="#72afd2"; }, - function () { this.style.color=""; } ); + var ips = data["ip"]; + var shortips = ips; + if(ips.length > MAXIPDISPLAY) + { + shortips = ips.slice(0,MAXIPDISPLAY-1); + shortips.push("..."); + } + $("td:eq(0)", row).html(shortips.join("
    ")); + $("td:eq(0)", row).hover(function () { this.title=ips.join("\n");}); // MAC + Vendor field if available if(data["macVendor"] && data["macVendor"].length > 0) From f0458e518a1fd6f7140d2414f7666121629ae96a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 14 Jun 2019 19:19:53 +0200 Subject: [PATCH 034/100] Disable ordering by IP address as this is not really meaningful if we have multiple IPs in each row Signed-off-by: DL6ER --- scripts/pi-hole/js/network.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/pi-hole/js/network.js b/scripts/pi-hole/js/network.js index 52aa2ec0..be432c1a 100644 --- a/scripts/pi-hole/js/network.js +++ b/scripts/pi-hole/js/network.js @@ -142,7 +142,7 @@ $(document).ready(function() { "processing": true, "order" : [[5, "desc"]], "columns": [ - {data: "ip", "type": "ip-address", "width" : "10%", "render": $.fn.dataTable.render.text() }, + {data: "ip", "width" : "10%", "render": $.fn.dataTable.render.text(), "orderable": false }, {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 cbd8eb614874f3427f0ccc400d37bce44af2212e Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 08:45:20 +0200 Subject: [PATCH 035/100] Remove variable $ipaddr Signed-off-by: DL6ER --- api_db.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/api_db.php b/api_db.php index b2015e81..4b02337b 100644 --- a/api_db.php +++ b/api_db.php @@ -73,12 +73,10 @@ if(isset($_GET["network"]) && $auth) while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) { $id = $res["id"]; - $ipaddr = array(); + $res["ip"] = array(); $ips = $db->query("SELECT ip FROM network_addresses WHERE network_id = $id ORDER BY lastSeen DESC"); while($ips !== false && $ip = $ips->fetchArray(SQLITE3_ASSOC)) - array_push($ipaddr,$ip["ip"]); - if(count($ipaddr) > 0) - $res["ip"] = $ipaddr; + array_push($res["ip"],$ip["ip"]); array_push($network, $res); } From 764a0b1a3a723801bbb31cba0c6682ce6e45706b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 08:52:47 +0200 Subject: [PATCH 036/100] If a device is unknown in the network_addresses table, we fall back to the IP address stored in the network table. This might be the case for devices that have not been seen since a long time. Signed-off-by: DL6ER --- api_db.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/api_db.php b/api_db.php index 4b02337b..90cb9218 100644 --- a/api_db.php +++ b/api_db.php @@ -73,10 +73,17 @@ if(isset($_GET["network"]) && $auth) while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC)) { $id = $res["id"]; + // Backup IP column of this request in case we want to reuse it further down + $resip = $res["ip"]; + // Empty array for holding the IP addresses $res["ip"] = array(); + // Get IP addresses for this device $ips = $db->query("SELECT ip FROM network_addresses WHERE network_id = $id ORDER BY lastSeen DESC"); while($ips !== false && $ip = $ips->fetchArray(SQLITE3_ASSOC)) array_push($res["ip"],$ip["ip"]); + // If the network_addresses table does not contain any IPs for this client, we use the IP stored in the network table + if(count($res) < 1) + array_push($res["ip"],$resip); array_push($network, $res); } From d2ce57b48bd19ff65bf5a8fad307126e71c8a521 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 15:58:12 +0200 Subject: [PATCH 037/100] Check IP field not entire object Signed-off-by: DL6ER --- api_db.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api_db.php b/api_db.php index 90cb9218..8342b914 100644 --- a/api_db.php +++ b/api_db.php @@ -82,7 +82,7 @@ if(isset($_GET["network"]) && $auth) while($ips !== false && $ip = $ips->fetchArray(SQLITE3_ASSOC)) array_push($res["ip"],$ip["ip"]); // If the network_addresses table does not contain any IPs for this client, we use the IP stored in the network table - if(count($res) < 1) + if(count($res["ip"]) < 1) array_push($res["ip"],$resip); array_push($network, $res); } From 1096179245b580b90c73243183395645724693a0 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 16:24:00 +0200 Subject: [PATCH 038/100] Fix (almost) endless loop when there are no (or no blocked) queries in the selected interval. Signed-off-by: DL6ER --- api_db.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/api_db.php b/api_db.php index 1fe77449..06f7b9a3 100644 --- a/api_db.php +++ b/api_db.php @@ -404,10 +404,17 @@ if (isset($_GET['getGraphData']) && $auth) if($max === null || $max < $row[0]) $max = $row[0]; - // Get the non-zero graph data + // $data[timestamp] = value_in_this_interval $data[$row[0]] = intval($row[1]); } + // Fall back to $from and $until if we read + // no data in the fetchArray while loop above + if($min === null) + $min = $from; + if($max === null) + $max = $until; + // Fill the missing intervals with zero for($i = min($min,$from); $i < max($max,$until); $i += $interval) { if(!array_key_exists($i, $data)) From d616a1180a1e4ac2dd3af35c91075c7c953f89a5 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 17:20:49 +0200 Subject: [PATCH 039/100] 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 2f5f95dba15aacab9a285eb6252a9a546cb320ba Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sat, 15 Jun 2019 17:47:07 +0200 Subject: [PATCH 040/100] 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 a77e3995..7acc5e63 100644 --- a/settings.php +++ b/settings.php @@ -868,11 +868,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.

    From 72057cfa26353f2260e4318fe5edb51b9c13ee7b Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 24 Jul 2019 22:14:07 +0200 Subject: [PATCH 064/100] 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 065/100] 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 066/100] 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 067/100] 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 068/100] 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 5ea4e6d185918a7cce7eb6675c41e3e03998c323 Mon Sep 17 00:00:00 2001 From: TheME Date: Sun, 4 Aug 2019 21:11:32 +0200 Subject: [PATCH 069/100] fix broken Icons Signed-off-by: Th3M3 --- scripts/pi-hole/js/auditlog.js | 2 +- scripts/pi-hole/js/db_queries.js | 6 +- scripts/pi-hole/php/loginpage.php | 2 +- scripts/vendor/daterangepicker.js | 601 +++++++++++++++-------------- settings.php | 2 +- style/vendor/daterangepicker.css | 606 +++++++++++++++++++----------- 6 files changed, 710 insertions(+), 509 deletions(-) diff --git a/scripts/pi-hole/js/auditlog.js b/scripts/pi-hole/js/auditlog.js index 52790602..95c7fc3d 100644 --- a/scripts/pi-hole/js/auditlog.js +++ b/scripts/pi-hole/js/auditlog.js @@ -60,7 +60,7 @@ function updateTopLists() { { url = ""+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 092/100] 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 093/100] 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 094/100] 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 095/100] 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() {