diff --git a/README.md b/README.md index 6e225856..c9770800 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ Pi-hole Admin Dashboard ============ +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/938b4d9e61b7487da77cf63ba05c683d)](https://www.codacy.com/app/Pi-hole/AdminLTE?utm_source=github.com&utm_medium=referral&utm_content=pi-hole/AdminLTE&utm_campaign=badger) [![Join the chat at https://gitter.im/pi-hole/AdminLTE](https://badges.gitter.im/pi-hole/AdminLTE.svg)](https://gitter.im/pi-hole/AdminLTE?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif "AdminLTE Presentation")](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY "Donate") diff --git a/list.php b/list.php index 9d030936..da146258 100644 --- a/list.php +++ b/list.php @@ -51,7 +51,7 @@ function getFullName() { diff --git a/scripts/pi-hole/js/footer.js b/scripts/pi-hole/js/footer.js index b9029236..ce640155 100644 --- a/scripts/pi-hole/js/footer.js +++ b/scripts/pi-hole/js/footer.js @@ -40,7 +40,7 @@ function piholeChange(action, duration) case "enable": btnStatus = $("#flip-status-enable"); btnStatus.html(" "); - $.getJSON("api.php?enable&token=" + token, (data) => { + $.getJSON("api.php?enable&token=" + token, function(data) { if(data.status === "enabled") { btnStatus.html(""); piholeChanged("enabled"); @@ -51,7 +51,7 @@ function piholeChange(action, duration) case "disable": btnStatus = $("#flip-status-disable"); btnStatus.html(" "); - $.getJSON("api.php?disable=" + duration + "&token=" + token, (data) => { + $.getJSON("api.php?disable=" + duration + "&token=" + token, function(data) { if(data.status === "disabled") { btnStatus.html(""); piholeChanged("disabled"); @@ -196,3 +196,11 @@ if(versionCompare(piholeVersion, "v2.9.5") < 1) { $("#btnSearchExact").hide(); } + +// Handle Strg + Enter button on Login page +$(document).keypress(function(e) { + if((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey && $("#loginpw").is(":focus")) { + $("#loginform").attr("action", "settings.php"); + $("#loginform").submit(); + } +}); diff --git a/scripts/pi-hole/js/index.js b/scripts/pi-hole/js/index.js index 3eec3bea..89b1d2ba 100644 --- a/scripts/pi-hole/js/index.js +++ b/scripts/pi-hole/js/index.js @@ -132,7 +132,7 @@ function escapeHtml(text) { function updateTopClientsChart() { $.getJSON("api.php?summaryRaw&getQuerySources", function(data) { var clienttable = $("#client-frequency").find("tbody:last"); - var domain, percentage, domainname; + var domain, percentage, domainname, domainip; for (domain in data.top_sources) { if ({}.hasOwnProperty.call(data.top_sources, domain)){ @@ -140,14 +140,17 @@ function updateTopClientsChart() { domain = escapeHtml(domain); if(domain.indexOf("|") > -1) { - domainname = domain.substr(0, domain.indexOf("|")); + var idx = domain.indexOf("|"); + domainname = domain.substr(0, idx); + domainip = domain.substr(idx+1, domain.length-idx); } else { domainname = domain; + domainip = domain; } - var url = ""+domainname+""; + var url = ""+domainname+""; percentage = data.top_sources[domain] / data.dns_queries_today * 100; clienttable.append(" " + url + " " + data.top_sources[domain] + "
-1) { - key = key.substr(0, key.indexOf("|")); + var idx = key.indexOf("|"); + key = key.substr(0, idx)+" ("+key.substr(idx+1, key.length-idx)+")"; } forwardDestinationChart.data.labels.push(key); }); @@ -291,7 +295,7 @@ $(document).ready(function() { enabled: true, mode: "x-axis", callbacks: { - title(tooltipItem, data) { + title: function(tooltipItem, data) { var label = tooltipItem[0].xLabel; var time = label.match(/(\d?\d):?(\d?\d?)/); var h = parseInt(time[1], 10); @@ -300,7 +304,7 @@ $(document).ready(function() { var to = padNumber(h)+":"+padNumber(m+9)+":59"; return "Queries from "+from+" to "+to; }, - label(tooltipItems, data) { + label: function(tooltipItems, data) { if(tooltipItems.datasetIndex === 1) { var percentage = 0.0; diff --git a/scripts/pi-hole/js/list.js b/scripts/pi-hole/js/list.js index ea9e5f44..12da6bcf 100644 --- a/scripts/pi-hole/js/list.js +++ b/scripts/pi-hole/js/list.js @@ -126,6 +126,7 @@ function add(arg) { var alInfo = $("#alInfo"); var alSuccess = $("#alSuccess"); var alFailure = $("#alFailure"); + var err = $("#err"); alInfo.show(); alSuccess.hide(); alFailure.hide(); @@ -137,10 +138,11 @@ function add(arg) { if (response.indexOf("not a valid argument") >= 0 || response.indexOf("is not a valid domain") >= 0) { alFailure.show(); - alFailure.delay(1000).fadeOut(2000, function() { + err.html(response); + alFailure.delay(4000).fadeOut(2000, function() { alFailure.hide(); }); - alInfo.delay(1000).fadeOut(2000, function() { + alInfo.delay(4000).fadeOut(2000, function() { alInfo.hide(); }); } else { diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 64be7586..428006c7 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -69,6 +69,17 @@ function add(domain,list) { } }); } +function handleAjaxError( xhr, textStatus, error ) { + if ( textStatus === "timeout" ) { + alert( "The server took too long to send the data." ); + } + else { + alert( "An error occured while loading the data. Presumably your log is too large to be processed." ); + } + $("#all-queries_processing").hide(); + tableApi.clear(); + tableApi.draw(); +} $(document).ready(function() { var status; @@ -94,11 +105,11 @@ $(document).ready(function() { status = data[4]; if (status.substr(0,2) === "Pi") { $(row).css("color","red"); - $("td:eq(5)", row).html( "" ); + $("td:eq(5)", row).html( "" ); } else{ $(row).css("color","green"); - $("td:eq(5)", row).html( "" ); + $("td:eq(5)", row).html( "" ); } }, @@ -106,8 +117,9 @@ $(document).ready(function() { "<'row'<'col-sm-4'l><'col-sm-8'p>>" + "<'row'<'col-sm-12'tr>>" + "<'row'<'col-sm-5'i><'col-sm-7'p>>", - "ajax": APIstring, + "ajax": {"url": APIstring, "error": handleAjaxError }, "autoWidth" : false, + "processing": true, "order" : [[0, "desc"]], "columns": [ { "width" : "20%", "type": "date" }, diff --git a/scripts/pi-hole/js/settings.js b/scripts/pi-hole/js/settings.js index e7e613ea..27620f5a 100644 --- a/scripts/pi-hole/js/settings.js +++ b/scripts/pi-hole/js/settings.js @@ -55,6 +55,7 @@ $(".confirm-flushlogs").confirm({ $("#DHCPchk").click(function() { $("input.DHCPgroup").prop("disabled", !this.checked); + $("#dhcpnotice").prop("hidden", !this.checked).addClass("lookatme"); }); var leasetable; diff --git a/scripts/pi-hole/php/auth.php b/scripts/pi-hole/php/auth.php index f6757ffa..188b47b6 100644 --- a/scripts/pi-hole/php/auth.php +++ b/scripts/pi-hole/php/auth.php @@ -35,7 +35,11 @@ function check_cors() { // to validate that the client is authorized, only unauthorized. $server_host = $_SERVER['HTTP_HOST']; - // If HTTP_HOST contains a non-standard port (!= 80) we have to strip the port + // Use parse_url if HTTP_HOST contains a colon (:) to get the host name + // e.g. + // https://pi.hole + // pi.hole:8080 + // However, we don't use parse_url(...) if there is no colon, since it will fail for e.g. "pi.hole" if(strpos($server_host, ":")) { $server_host = parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST); @@ -48,7 +52,7 @@ function check_cors() { if(isset($_SERVER['HTTP_ORIGIN'])) { $server_origin = $_SERVER['HTTP_ORIGIN']; - // If HTTP_ORIGIN contains a non-standard port (!= 80) we have to strip the port + // Detect colon in $_SERVER['HTTP_ORIGIN'] (see comment above) if(strpos($server_origin, ":")) { $server_origin = parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST); @@ -99,14 +103,19 @@ function check_csrf($token) { function check_domain() { if(isset($_POST['domain'])){ - $validDomain = is_valid_domain_name($_POST['domain']); - if(!$validDomain){ - log_and_die($_POST['domain']. ' is not a valid domain'); + $domains = explode(" ",$_POST['domain']); + foreach($domains as $domain) + { + $validDomain = is_valid_domain_name($domain); + if(!$validDomain){ + log_and_die(htmlspecialchars($domain. ' is not a valid domain')); + } } } } function list_verify($type) { + global $pwhash, $wrongpassword; if(!isset($_POST['domain']) || !isset($_POST['list']) || !(isset($_POST['pw']) || isset($_POST['token']))) { log_and_die("Missing POST variables"); } @@ -121,11 +130,11 @@ function list_verify($type) { require("password.php"); if(strlen($pwhash) == 0) { - log_and_die("No password set - ${type}listing with password not supported"); + log_and_die("No password set - ".htmlspecialchars($type)."listing with password not supported"); } elseif($wrongpassword) { - log_and_die("Wrong password - ${type}listing of ${_POST['domain']} not permitted"); + log_and_die("Wrong password - ".htmlspecialchars($type)."listing of ${_POST['domain']} not permitted"); } } else diff --git a/scripts/pi-hole/php/data.php b/scripts/pi-hole/php/data.php index 8dd827d6..fdb6135f 100644 --- a/scripts/pi-hole/php/data.php +++ b/scripts/pi-hole/php/data.php @@ -679,14 +679,12 @@ $byTimeAds[$time] = 1; } } - else - { - if (isset($byTimeDomains[$time])) { - $byTimeDomains[$time]++; - } - else { - $byTimeDomains[$time] = 1; - } + + if (isset($byTimeDomains[$time])) { + $byTimeDomains[$time]++; + } + else { + $byTimeDomains[$time] = 1; } } return [$byTimeDomains,$byTimeAds]; @@ -721,14 +719,12 @@ $byTimeAds[$time] = 1; } } - else - { - if (isset($byTimeDomains[$time])) { - $byTimeDomains[$time]++; - } - else { - $byTimeDomains[$time] = 1; - } + + if (isset($byTimeDomains[$time])) { + $byTimeDomains[$time]++; + } + else { + $byTimeDomains[$time] = 1; } } return [$byTimeDomains,$byTimeAds]; diff --git a/scripts/pi-hole/php/header.php b/scripts/pi-hole/php/header.php index c889ab24..e554d76a 100644 --- a/scripts/pi-hole/php/header.php +++ b/scripts/pi-hole/php/header.php @@ -83,15 +83,16 @@ $memory_usage = -1; } + if($auth) { + // For session timer + $maxlifetime = ini_get("session.gc_maxlifetime"); - // For session timer - $maxlifetime = ini_get("session.gc_maxlifetime"); - - // Generate CSRF token - if(empty($_SESSION['token'])) { - $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32)); + // Generate CSRF token + if(empty($_SESSION['token'])) { + $_SESSION['token'] = base64_encode(openssl_random_pseudo_bytes(32)); + } + $token = $_SESSION['token']; } - $token = $_SESSION['token']; if(isset($setupVars['WEBUIBOXEDLAYOUT'])) { @@ -171,7 +172,7 @@ - +
@@ -249,25 +250,25 @@
- Pi-hole logo + Pi-hole logo

Status

Active'; + echo ' Active'; } elseif ($pistatus == "0") { - echo ' Offline'; + echo ' Offline'; } elseif ($pistatus == "-1") { - echo ' DNS service not running'; + echo ' DNS service not running'; } else { - echo ' Unknown'; + echo ' Unknown'; } // CPU Temp if ($celsius >= -273.15) { - echo " 60) { echo "#FF0000"; } @@ -293,33 +294,33 @@ ?>
$nproc) { - echo '#FF0000'; + echo "#FF0000"; } else { - echo '#7FFF00'; + echo "#7FFF00"; } - echo '""> Load:  ' . $loaddata[0] . '  ' . $loaddata[1] . '  '. $loaddata[2] . '
'; + echo "\"> Load:  " . $loaddata[0] . "  " . $loaddata[1] . "  ". $loaddata[2] . ""; ?>
0.75 || $memory_usage < 0.0) { - echo '#FF0000'; + echo "#FF0000"; } else { - echo '#7FFF00'; + echo "#7FFF00"; } if($memory_usage > 0.0) { - echo '""> Memory usage:  ' . sprintf("%.1f",100.0*$memory_usage) . '%'; + echo "\"> Memory usage:  " . sprintf("%.1f",100.0*$memory_usage) . "%"; } else { - echo '"">
Memory usage:   N/A'; + echo "\">
Memory usage:   N/A"; } ?>
@@ -442,7 +443,7 @@ // Show Logout button if $auth is set and authorization is required if(strlen($pwhash) > 0) { ?>
  • - + Logout
  • diff --git a/scripts/pi-hole/php/loginpage.php b/scripts/pi-hole/php/loginpage.php index a0366dfb..beba3c87 100644 --- a/scripts/pi-hole/php/loginpage.php +++ b/scripts/pi-hole/php/loginpage.php @@ -13,13 +13,19 @@
    -
    +
    - +
    -
    +
    +
      +
    • Return → Log in and go to requested page ()
    • +
    • Ctrl+Return → Log in and go to Settings page
    • +
    +
    +
    diff --git a/scripts/pi-hole/php/password.php b/scripts/pi-hole/php/password.php index 1c1ce4c0..4abe9cfc 100644 --- a/scripts/pi-hole/php/password.php +++ b/scripts/pi-hole/php/password.php @@ -21,6 +21,7 @@ } $wrongpassword = false; + $auth = false; // Test if password is set if(strlen($pwhash) > 0) diff --git a/scripts/pi-hole/php/savesettings.php b/scripts/pi-hole/php/savesettings.php index e65a7a61..050165ec 100644 --- a/scripts/pi-hole/php/savesettings.php +++ b/scripts/pi-hole/php/savesettings.php @@ -355,10 +355,21 @@ function validDomain($domain_name) $error .= "Lease time ".$leasetime." is invalid!
    "; } + if(isset($_POST["useIPv6"])) + { + $ipv6 = "true"; + $type = "(IPv4 + IPv6)"; + } + else + { + $ipv6 = "false"; + $type = "(IPv4)"; + } + if(!strlen($error)) { - exec("sudo pihole -a enabledhcp ".$from." ".$to." ".$router." ".$leasetime." ".$domain); - $success .= "The DHCP server has been activated"; + exec("sudo pihole -a enabledhcp ".$from." ".$to." ".$router." ".$leasetime." ".$domain." ".$ipv6); + $success .= "The DHCP server has been activated ".$type; } } else diff --git a/settings.php b/settings.php index 21e89fc1..7983e107 100644 --- a/settings.php +++ b/settings.php @@ -9,6 +9,16 @@ max-width: none; white-space: nowrap; } + @-webkit-keyframes Pulse{ + from {color:#630030;-webkit-text-shadow:0 0 9px #333;} + 50% {color:#e33100;-webkit-text-shadow:0 0 18px #e33100;} + to {color:#630030;-webkit-text-shadow:0 0 9px #333;} + } + p.lookatme { + -webkit-animation-name: Pulse; + -webkit-animation-duration: 2s; + -webkit-animation-iteration-count: infinite; + } @@ -81,6 +91,7 @@
    +

    Warning: PHP has been compiled without IPv6 support.

    @@ -93,6 +104,17 @@
    0 && $piHoleIPv6 != "unknown") + { + if(substr($piHoleIPv6, 0, 4) != "fe80") + { + $usingipv6 = true; + } + } + if(isset($setupVars["DHCP_ACTIVE"])) { if($setupVars["DHCP_ACTIVE"] == 1) @@ -107,12 +129,29 @@ $DHCPstart = $setupVars["DHCP_START"]; $DHCPend = $setupVars["DHCP_END"]; $DHCProuter = $setupVars["DHCP_ROUTER"]; - $DHCPleasetime = $setupVars["DHCP_LEASETIME"]; - if(strlen($DHCPleasetime) < 1) + // This setting has been added later, we have to check if it exists + if(isset($setupVars["DHCP_LEASETIME"])) + { + $DHCPleasetime = $setupVars["DHCP_LEASETIME"]; + if(strlen($DHCPleasetime) < 1) + { + // Fallback if empty string + $DHCPleasetime = 24; + } + } + else { - // Fallback if it was not set before $DHCPleasetime = 24; } + if(isset($setupVars["DHCP_IPv6"])) + { + $DHCPIPv6 = $setupVars["DHCP_IPv6"]; + } + else + { + $DHCPIPv6 = $usingipv6; + } + } else { @@ -130,6 +169,7 @@ $DHCProuter = ""; } $DHCPleasetime = 24; + $DHCPIPv6 = $usingipv6; } if(isset($setupVars["PIHOLE_DOMAIN"])){ $piHoleDomain = $setupVars["PIHOLE_DOMAIN"]; @@ -143,8 +183,13 @@
    -
    -
    +
    +
    +
    +
    +
    +
    +

    hidden>Make sure your router's DHCP server is disabled when using the Pi-hole DHCP server!

    @@ -181,6 +226,11 @@
    +
    +
    +
    +
    +
    @@ -403,10 +453,10 @@
    -
    +
    -
    +

    Note that enabling these two options may increase your privacy slightly, but may also prevent you from being able to access local hostnames if the Pi-Hole is not used as DHCP server

    @@ -455,6 +505,7 @@

    Privacy mode

    -
    -
    +
    +
    +
    +