Merge pull request #372 from pi-hole/devel

[RELEASE] Pi-hole Admin Page v2.4
This commit is contained in:
Mcat12
2017-01-28 19:19:17 -05:00
committed by GitHub
26 changed files with 791 additions and 233 deletions
+7 -4
View File
@@ -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")
@@ -8,8 +9,7 @@ Using **[AdminLTE](https://almsaeedstudio.com)**, this project will create a Web
From this interface, you will be able to see stats on how well your Pi-hole is performing. You will also be able to update the lists used to block ads.
![Pi-hole Web interface](http://i.imgur.com/5lLAUGo.png)
![Fully responsive](http://i.imgur.com/fHuWR6E.png)
![Pi-hole Web interface](http://i.imgur.com/EgGZXbT.png)
## API
A read-only API can be accessed at `/admin/api.php`. With either no parameters or `api.php?summary` it returns the following JSON:
@@ -22,6 +22,9 @@ A read-only API can be accessed at `/admin/api.php`. With either no parameters o
}
```
There are many more parameters, such as `summaryRaw`, `overTimeData`, `topItems`, `recentItems`, `getQueryTypes`, `getForwardDestinations`, `getQuerySources`, and finally `getAllQueries`.
There are many more parameters, such as `summaryRaw`, `overTimeData10mins`, ` topClients` or `getQuerySources`, `getQueryTypes`, `getForwardDestinations`, and `getAllQueries`.
Together with a token it is also possible to enable and disable (also with a set timeout) blocking via the API.
`getAllQueries` can optionally be set with one of these values to return JSON hash ordered as value implies: `orderByClientDomainTime`, `orderByClientTimeDomain`, `orderByTimeClientDomain`, `orderByTimeDomainClient`, `orderByDomainClientTime` or `orderByDomainTimeClient`.
<hr>
<img src="https://assets.pi-hole.net/static/BStackLogo.png" height="80"><br>
We use BrowserStack for multi-platform multi-browser testing.
+8 -8
View File
@@ -89,20 +89,20 @@
$data = array_merge($data, getGravity());
}
if (isset($_GET['tailLog'])) {
if (isset($_GET['tailLog']) && $auth) {
$data = array_merge($data, tailPiholeLog($_GET['tailLog']));
}
function filterArray(&$inArray) {
$outArray = array();
foreach ($inArray as $key=>$value) {
if (is_array($value)) {
$outArray[htmlspecialchars($key)] = filterArray($value);
$outArray = array();
foreach ($inArray as $key=>$value) {
if (is_array($value)) {
$outArray[htmlspecialchars($key)] = filterArray($value);
} else {
$outArray[htmlspecialchars($key)] = htmlspecialchars($value);
$outArray[htmlspecialchars($key)] = !is_numeric($value) ? htmlspecialchars($value) : $value;
}
}
return $outArray;
}
return $outArray;
}
$data = filterArray($data);
+26 -18
View File
@@ -51,7 +51,7 @@
<li>and others</li>
</ul>
</li>
<li>Query Types: Shows to which upstream DNS the permitted requests have been forwarded to.</li>
<li>Forward Destinations: Shows to which upstream DNS the permitted requests have been forwarded to.</li>
<li>Top Domains: Ranking of requested sites by number of DNS lookups.</li>
<li>Top Advertisers: Ranking of requested advertisements by number of DNS lookups.</li>
<li>Top Clients: Ranking of how many DNS requests each client has made on the local network.</li>
@@ -70,19 +70,9 @@
<div class="row">
<div class="col-md-12">
<h2>White- / Blacklist</h2>
<p>Add or remove domains (or subdomains) from the white-/blacklist. If a domain is added to e.g. the whitelist, any possible entry of the same domain will be automatically removed from the blacklist and vice versa. Adding wildcards using the web UI is currently <em>not</em> supported.</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Update Lists</h2>
<p>Will download any updates from the third-party ad-serving domain lists that we source. By default, this command runs once a week via cron.</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Query adlists</h2>
This function is useful to find out what list a domain appears on. Since we don't control what the third-parties put on the block lists, you may find that a domain you normally visit stops working. If this is the case, you could run this command to scan for strings in the list of blocked domains and it will return the list the domain is found on. This proved useful a while back when the Mahakala list was adding apple.com and microsoft.com to their block list.</p>
<p>Add or remove domains (or subdomains) from the white-/blacklist. If a domain is added to e.g. the whitelist, any possible entry of the same domain will be automatically removed from the blacklist and vice versa.</p>
<p>Wildcard blacklisting is supported (entering <tt>something.de</tt> will block this domain including all subdomains like <tt>a.bb.c.999.something.de</tt>). Note that wildcard whitelisting is <em>not</em> supported.</p>
<p>You can white-/blacklist multiple entries at a time if you separate the domains by spaces.</p>
</div>
</div>
<div class="row">
@@ -91,18 +81,36 @@
Disables/enables Pi-Hole blocking completely. You may have to wait a few minutes for the changes to reach all of your devices. The change will be reflected by a changed status (top left)
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Tools &rarr; Update Lists</h2>
<p>Will download any updates from the third-party ad-serving domain lists that we source. By default, this command runs once a week via cron (Sunday at 01:59).</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Tools &rarr; Query adlists</h2>
This function is useful to find out what list a domain appears on. Since we don't control what the third-parties put on the block lists, you may find that a domain you normally visit stops working. If this is the case, you could run this command to scan for strings in the list of blocked domains and it will return the list the domain is found on. This proved useful a while back when the Mahakala list was adding <tt>apple.com</tt> and <tt>microsoft.com</tt> to their block list.</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Tools &rarr; Tail pihole.log</h2>
Live tailing of the raw Pi-hole log.</p>
</div>
</div>
<div class="row">
<div class="col-md-12">
<h2>Settings</h2>
Change settings for the Pi-Hole
<h4>Networking</h4>
Displays information about the interfaces of the Pi-Hole. No changes possible
Displays information about the interfaces of the Pi-Hole. No changes possible.
<h4>Pi-Hole DHCP Server</h4>
Using this setting you can enable/disable the DHCP server of the Pi-Hole. Note that you should disable any other DHCP server on your network to avoid IP addresses being used more than once. You have to give the range of IPs that DHCP will serve and the IP of the local router (gateway). If the DHCP server is active, the current leases are shown on the settings page.
Using this setting you can enable/disable the DHCP server of the Pi-Hole. Note that you should disable any other DHCP server on your network to avoid IP addresses being used more than once. You have to give the range of IPs that DHCP will serve and the IP of the local router (gateway). If the DHCP server is active, the current leases are shown on the settings page. IPv4 DHCP will always be activated, IPv6 (stateless + statefull) can be enabled.
<h4>Upstream DNS Servers</h4>
Customize used upstream DNS servers + advanced settings
Customize used upstream DNS servers + advanced settings for DNS servers. Note that any number of DNS servers may be enabled at a time.
<h4>Query Logging</h4>
Enabled/disable query logging on your Pi-hole
Enabled/disable query logging on your Pi-hole + provide option to flush the log
<h4>API</h4>
Change settings which apply to the API as well as the web UI<br>
Note that Top Clients have to be given as IP addresses
+16 -1
View File
@@ -29,10 +29,18 @@ function getFullName() {
<div class="form-group input-group">
<input id="domain" type="text" class="form-control" placeholder="Add a domain (example.com or sub.example.com)">
<span class="input-group-btn">
<?php if($list === "black") { ?>
<button id="btnAdd" class="btn btn-default" type="button">Add (exact)</button>
<button id="btnAddWildcard" class="btn btn-default" type="button">Add (wildcard)</button>
<?php }else{ ?>
<button id="btnAdd" class="btn btn-default" type="button">Add</button>
<?php } ?>
<button id="btnRefresh" class="btn btn-default" type="button">Refresh</button>
}
</span>
</div>
<?php if($list === "white") { ?>
<p>Note that whitelisting domains which are blocked using the wildcard method won't work.</p><?php } ?>
<!-- Alerts -->
<div id="alInfo" class="alert alert-info alert-dismissible fade in" role="alert" hidden="true">
@@ -45,11 +53,18 @@ function getFullName() {
</div>
<div id="alFailure" class="alert alert-danger alert-dismissible fade in" role="alert" hidden="true">
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Failure! Something went wrong.
Failure! Something went wrong.<br/><span id="err"></span>
</div>
<!-- Domain List -->
<?php if($list === "black") { ?>
<h3>Exact blocking</h3>
<?php } ?>
<ul class="list-group" id="list"></ul>
<?php if($list === "black") { ?>
<h3>Wildcard blocking</h3>
<ul class="list-group" id="list-wildcard"></ul>
<?php } ?>
<?php
require "scripts/pi-hole/php/footer.php";
+1 -1
View File
@@ -61,7 +61,7 @@ if(isset($setupVars["API_PRIVACY_MODE"]))
</div>
<div id="alFailure" class="alert alert-danger alert-dismissible fade in" role="alert" hidden="true">
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button>
Failure! Something went wrong.
Failure! Something went wrong.<span id="err"></span>
</div>
<div class="row">
+33 -9
View File
@@ -10,9 +10,9 @@ $("body").on("click", function(event) {
function piholeChanged(action)
{
const status = $("#status");
const ena = $("#pihole-enable");
const dis = $("#pihole-disable");
var status = $("#status");
var ena = $("#pihole-enable");
var dis = $("#pihole-disable");
switch(action) {
case "enabled":
@@ -33,7 +33,7 @@ function piholeChanged(action)
function piholeChange(action, duration)
{
const token = encodeURIComponent($("#token").html());
var token = encodeURIComponent($("#token").html());
var btnStatus;
switch(action) {
@@ -62,25 +62,25 @@ function piholeChange(action, duration)
}
// Handle Enable/Disable
$("#pihole-enable").on("click", (e) => {
$("#pihole-enable").on("click", function(e){
e.preventDefault();
piholeChange("enable","");
});
$("#pihole-disable-permanently").on("click", (e) => {
$("#pihole-disable-permanently").on("click", function(e){
e.preventDefault();
piholeChange("disable","0");
});
$("#pihole-disable-10s").on("click", (e) => {
$("#pihole-disable-10s").on("click", function(e){
e.preventDefault();
piholeChange("disable","10");
setTimeout(function(){piholeChanged("enabled");},10000);
});
$("#pihole-disable-30s").on("click", (e) => {
$("#pihole-disable-30s").on("click", function(e){
e.preventDefault();
piholeChange("disable","30");
setTimeout(function(){piholeChanged("enabled");},30000);
});
$("#pihole-disable-5m").on("click", (e) => {
$("#pihole-disable-5m").on("click", function(e){
e.preventDefault();
piholeChange("disable","300");
setTimeout(function(){piholeChanged("enabled");},300000);
@@ -204,3 +204,27 @@ $(document).keypress(function(e) {
$("#loginform").submit();
}
});
function testCookies()
{
if (navigator.cookieEnabled)
{
return true;
}
// set and read cookie
document.cookie = "cookietest=1";
var ret = document.cookie.indexOf("cookietest=") !== -1;
// delete cookie
document.cookie = "cookietest=1; expires=Thu, 01-Jan-1970 00:00:01 GMT";
return ret;
}
$(function() {
if(!testCookies() && $("#cookieInfo").length)
{
$("#cookieInfo").show();
}
});
+8 -1
View File
@@ -2,6 +2,13 @@ function eventsource() {
var alInfo = $("#alInfo");
var alSuccess = $("#alSuccess");
var ta = $("#output");
// IE does not support EventSource - exit early
if (typeof EventSource !== "function") {
ta.show();
ta.html("Updating lists of ad-serving domains is not supported with this browser!");
return;
}
var source = new EventSource("scripts/pi-hole/php/gravity.sh.php");
ta.html("");
@@ -27,7 +34,7 @@ function eventsource() {
}, false);
}
$("#gravityBtn").on("click", () => {
$("#gravityBtn").on("click", function(){
$("#gravityBtn").attr("disabled", true);
eventsource();
});
+8 -4
View File
@@ -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 = "<a href=\"queries.php?client="+domain+"\">"+domainname+"</a>";
var url = "<a href=\"queries.php?client="+domain+"\" title=\""+domainip+"\">"+domainname+"</a>";
percentage = data.top_sources[domain] / data.dns_queries_today * 100;
clienttable.append("<tr> <td>" + url +
"</td> <td>" + data.top_sources[domain] + "</td> <td> <div class=\"progress progress-sm\" title=\""+percentage.toFixed(1)+"%\"> <div class=\"progress-bar progress-bar-blue\" style=\"width: " +
@@ -172,7 +175,8 @@ function updateForwardDestinations() {
c.push(colors.shift());
if(key.indexOf("|") > -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);
});
+79 -17
View File
@@ -6,13 +6,18 @@ var token = $("#token").html();
var listType = $("#list-type").html();
var fullName = listType === "white" ? "Whitelist" : "Blacklist";
function sub(index, entry) {
function sub(index, entry, arg) {
var domain = $("#"+index);
var locallistType = listType;
domain.hide("highlight");
if(arg === "wild")
{
locallistType = "wild";
}
$.ajax({
url: "scripts/pi-hole/php/sub.php",
method: "post",
data: {"domain":entry, "list":listType, "token":token},
data: {"domain":entry, "list":locallistType, "token":token},
success: function(response) {
if(response.length !== 0){
return;
@@ -21,14 +26,24 @@ function sub(index, entry) {
},
error: function(jqXHR, exception) {
alert("Failed to remove the domain!");
domain.show({queue:true});
}
});
}
function refresh(fade) {
var listw;
var list = $("#list");
if(listType === "black")
{
listw = $("#list-wildcard");
}
if(fade) {
list.fadeOut(100);
if(listw)
{
listw.fadeOut(100);
}
}
$.ajax({
url: "scripts/pi-hole/php/get.php",
@@ -36,26 +51,60 @@ function refresh(fade) {
data: {"list":listType},
success: function(response) {
list.html("");
if(listw)
{
listw.html("");
}
var data = JSON.parse(response);
if(data.length === 0) {
list.html("<div class=\"alert alert-info\" role=\"alert\">Your " + fullName + " is empty!</div>");
$("h3").hide();
if(listw)
{
listw.html("<div class=\"alert alert-info\" role=\"alert\">Your " + fullName + " is empty!</div>");
}
else
{
list.html("<div class=\"alert alert-info\" role=\"alert\">Your " + fullName + " is empty!</div>");
}
}
else {
$("h3").show();
data.forEach(function (entry, index) {
list.append(
if(entry.substr(0,1) === "*")
{
// Wildcard entry
// remove leading *
entry = entry.substr(1, entry.length - 1);
listw.append(
"<li id=\"" + index + "\" class=\"list-group-item clearfix\">" + entry +
"<button class=\"btn btn-danger btn-xs pull-right\" type=\"button\">" +
"<span class=\"glyphicon glyphicon-trash\"></span></button></li>"
);
"<span class=\"glyphicon glyphicon-trash\"></span></button></li>");
// Handle button
$("#list-wildcard #"+index+"").on("click", "button", function() {
sub(index, entry, "wild");
});
}
else
{
// Normal entry
list.append(
"<li id=\"" + index + "\" class=\"list-group-item clearfix\">" + entry +
"<button class=\"btn btn-danger btn-xs pull-right\" type=\"button\">" +
"<span class=\"glyphicon glyphicon-trash\"></span></button></li>");
// Handle button
$("#list #"+index+"").on("click", "button", function() {
sub(index, entry, "exact");
});
}
// Handle button
$("#list #"+index+"").on("click", "button", function() {
sub(index, entry);
});
});
}
list.fadeIn("fast");
list.fadeIn(100);
if(listw)
{
listw.fadeIn(100);
}
},
error: function(jqXHR, exception) {
$("#alFailure").show();
@@ -65,30 +114,37 @@ function refresh(fade) {
window.onload = refresh(false);
function add() {
function add(arg) {
var locallistType = listType;
var domain = $("#domain");
if(domain.val().length === 0){
return;
}
if(arg === "wild")
{
locallistType = "wild";
}
var alInfo = $("#alInfo");
var alSuccess = $("#alSuccess");
var alFailure = $("#alFailure");
var err = $("#err");
alInfo.show();
alSuccess.hide();
alFailure.hide();
$.ajax({
url: "scripts/pi-hole/php/add.php",
method: "post",
data: {"domain":domain.val(), "list":listType, "token":token},
data: {"domain":domain.val(), "list":locallistType, "token":token},
success: function(response) {
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 {
@@ -105,6 +161,7 @@ function add() {
},
error: function(jqXHR, exception) {
alFailure.show();
err.html("");
alFailure.delay(1000).fadeOut(2000, function() {
alFailure.hide();
});
@@ -121,14 +178,19 @@ function add() {
$(document).keypress(function(e) {
if(e.which === 13 && $("#domain").is(":focus")) {
// Enter was pressed, and the input has focus
add();
add("exact");
}
});
// Handle buttons
$("#btnAdd").on("click", function() {
add();
add("exact");
});
$("#btnAddWildcard").on("click", function() {
add("wild");
});
$("#btnRefresh").on("click", function() {
refresh(true);
});
+28 -6
View File
@@ -22,6 +22,7 @@ function add(domain,list) {
alDomain.html(domain);
var alSuccess = $("#alSuccess");
var alFailure = $("#alFailure");
var err = $("#err");
if(list === "white")
{
@@ -43,7 +44,8 @@ function add(domain,list) {
if (response.indexOf("not a valid argument") >= 0 || response.indexOf("is not a valid domain") >= 0)
{
alFailure.show();
alFailure.delay(1000).fadeOut(2000, function() { alFailure.hide(); });
err.html(response);
alFailure.delay(4000).fadeOut(2000, function() { alFailure.hide(); });
}
else
{
@@ -58,6 +60,7 @@ function add(domain,list) {
},
error: function(jqXHR, exception) {
alFailure.show();
err.html("");
alFailure.delay(1000).fadeOut(2000, function() {
alFailure.hide();
});
@@ -69,8 +72,20 @@ 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;
// Do we want to filter queries?
var GETDict = {};
@@ -90,13 +105,18 @@ $(document).ready(function() {
tableApi = $("#all-queries").DataTable( {
"rowCallback": function( row, data, index ){
if (data[4] === "Pi-holed") {
status = data[4];
if (status === "Pi-holed (exact)") {
$(row).css("color","red");
$("td:eq(5)", row).html( "<button style=\"color:green;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button>" );
$("td:eq(5)", row).html( "<button style=\"color:green; white-space: nowrap;\"><i class=\"fa fa-pencil-square-o\"></i> Whitelist</button>" );
}
else if (status === "Pi-holed (wildcard)") {
$(row).css("color","red");
$("td:eq(5)", row).html( "" );
}
else{
$(row).css("color","green");
$("td:eq(5)", row).html( "<button style=\"color:red;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
$("td:eq(5)", row).html( "<button style=\"color:red; white-space: nowrap;\"><i class=\"fa fa-ban\"></i> Blacklist</button>" );
}
},
@@ -104,8 +124,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" },
@@ -124,7 +145,8 @@ $(document).ready(function() {
});
$("#all-queries tbody").on( "click", "button", function () {
var data = tableApi.row( $(this).parents("tr") ).data();
if (data[4] === "Pi-holed")
status = data[4];
if (status.substr(0,2) === "Pi")
{
add(data[2],"white");
}
+59 -13
View File
@@ -1,4 +1,54 @@
var exact = "";
function quietfilter(ta,data)
{
var lines = data.split("\n");
for(var i = 0;i<lines.length;i++)
{
if(lines[i].indexOf("results") !== -1 && lines[i].indexOf("0 results") === -1)
{
var shortstring = lines[i].replace("::: /etc/pihole/","");
// Remove "(x results)"
shortstring = shortstring.replace(/\(.*/,"");
ta.append(shortstring+"\n");
}
}
}
// Credit: http://stackoverflow.com/a/10642418/2087442
function httpGet(ta,quiet,theUrl)
{
var xmlhttp;
if (window.XMLHttpRequest)
{
// code for IE7+
xmlhttp = new XMLHttpRequest();
}
else
{
// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState === 4 && xmlhttp.status === 200)
{
ta.show();
ta.empty();
if(!quiet)
{
ta.append(xmlhttp.responseText);
}
else
{
quietfilter(ta,xmlhttp.responseText);
}
}
};
xmlhttp.open("GET", theUrl, false);
xmlhttp.send();
}
function eventsource() {
var ta = $("#output");
var domain = $("#domain");
@@ -12,11 +62,17 @@ function eventsource() {
if(q.val() === "yes")
{
quiet = true;
exact = "exact";
exact = "&exact";
}
// IE does not support EventSource - load whole content at once
if (typeof EventSource !== "function") {
httpGet(ta,quiet,"/admin/scripts/pi-hole/php/queryads.php?domain="+domain.val().toLowerCase()+exact+"&IE");
return;
}
var host = window.location.host;
var source = new EventSource("http://"+host+"/admin/scripts/pi-hole/php/queryads.php?domain="+domain.val().toLowerCase()+"&"+exact);
var source = new EventSource("/admin/scripts/pi-hole/php/queryads.php?domain="+domain.val().toLowerCase()+exact);
// Reset and show field
ta.empty();
@@ -29,17 +85,7 @@ function eventsource() {
}
else
{
var lines = e.data.split("\n");
for(var i = 0;i<lines.length;i++)
{
if(lines[i].indexOf("results") !== -1 && lines[i].indexOf("0 results") === -1)
{
var shortstring = lines[i].replace("::: /etc/pihole/","");
// Remove "(x results)"
shortstring = shortstring.replace(/\(.*/,"");
ta.append(shortstring+"\n");
}
}
quietfilter(ta,e.data);
}
}, false);
+11
View File
@@ -2,6 +2,17 @@ $(function () {
$("[data-mask]").inputmask();
});
$(function(){
$("#custom1val").ipAddress({s:4});
$("#custom2val").ipAddress({s:4});
$("#custom3val").ipAddress({v:6});
$("#custom4val").ipAddress({v:6});
$("#DHCPfrom").ipAddress({s:4});
$("#DHCPto").ipAddress({s:4});
$("#DHCProuter").ipAddress({s:4});
});
$(".confirm-reboot").confirm({
text: "Are you sure you want to send a reboot command to your Pi-Hole?",
title: "Confirmation required",
+3
View File
@@ -13,6 +13,9 @@ switch($type) {
case "black":
echo exec("sudo pihole -b -q ${_POST['domain']}");
break;
case "wild":
echo exec("sudo pihole -wild -q ${_POST['domain']}");
break;
}
?>
+20 -7
View File
@@ -17,10 +17,12 @@ function log_and_die($message) {
function check_cors() {
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
$ipv4 = isset($setupVars["IPV4_ADDRESS"]) ? explode("/", $setupVars["IPV4_ADDRESS"])[0] : $_SERVER['SERVER_ADDR'];
$ipv6 = isset($setupVars["IPV6_ADDRESS"]) ? explode("/", $setupVars["IPV6_ADDRESS"])[0] : $_SERVER['SERVER_ADDR'];
// Check CORS
$AUTHORIZED_HOSTNAMES = array(
$ipv4,
$ipv6,
$_SERVER["SERVER_NAME"],
"pi.hole",
"localhost"
@@ -40,10 +42,15 @@ function check_cors() {
// 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, ":"))
// Don't use parse_url for IPv6 addresses, since it does not support them
// see PHP bug report: https://bugs.php.net/bug.php?id=72811
if(strpos($server_host, ":") && !strpos($server_host, "[") && !strpos($server_host, "]"))
{
$server_host = parse_url($_SERVER['HTTP_HOST'], PHP_URL_HOST);
}
// Remove "[" ... "]"
$server_host = str_replace(array("[","]"), array("",""), $server_host);
if(isset($_SERVER['HTTP_HOST']) && !in_array($server_host, $AUTHORIZED_HOSTNAMES)) {
log_and_die("Failed Host Check: " . $server_host .' vs '. join(', ', $AUTHORIZED_HOSTNAMES));
@@ -53,10 +60,12 @@ function check_cors() {
$server_origin = $_SERVER['HTTP_ORIGIN'];
// Detect colon in $_SERVER['HTTP_ORIGIN'] (see comment above)
if(strpos($server_origin, ":"))
if(strpos($server_origin, ":") && !strpos($server_origin, "[") && !strpos($server_origin, "]"))
{
$server_origin = parse_url($_SERVER['HTTP_ORIGIN'], PHP_URL_HOST);
}
// Remove "[" ... "]"
$server_origin = str_replace(array("[","]"), array("",""), $server_origin);
if(!in_array($server_origin, $AUTHORIZED_HOSTNAMES)) {
log_and_die("Failed CORS: " . $server_origin .' vs '. join(', ', $AUTHORIZED_HOSTNAMES));
@@ -103,9 +112,13 @@ 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'));
}
}
}
}
@@ -126,11 +139,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
+74 -23
View File
@@ -333,6 +333,7 @@
// Create empty array for gravity
$gravity_domains = getGravity();
$wildcard_domains = getWildcardListContent();
if(isset($_GET["from"]))
{
@@ -380,10 +381,37 @@
$exploded = explode(" ", trim($query));
$domain = $exploded[count($exploded)-3];
$tmp = $exploded[count($exploded)-4];
$status = isset($gravity_domains[$domain]) ? "Pi-holed" : "OK";
if(($status === "Pi-holed" && $showBlocked) || ($status === "OK" && $showPermitted))
$status = "";
if(isset($gravity_domains[$domain]))
{
if($gravity_domains[$domain] > 0)
{
// Exact matching gravity domain
$status = "Pi-holed (exact)";
}
else
{
// Explicitly whitelisted
$status = "OK (whitelisted)";
}
}
else
{
// Test for wildcard blocking
foreach ($wildcard_domains as $entry) {
if(strpos($domain, $entry) !== false)
{
$status = "Pi-holed (wildcard)";
}
}
if(!strlen($status))
{
$status = "OK";
}
}
if((substr($status,0,2) === "Pi" && $showBlocked) || (substr($status,0,2) === "OK" && $showPermitted))
{
$type = substr($exploded[count($exploded)-4], 6, -1);
$client = $exploded[count($exploded)-1];
@@ -481,7 +509,7 @@
function countDnsQueries() {
global $logListName;
return exec("grep -c \": query\\[A\" $logListName");
return intval(exec("grep -c \": query\\[A\" $logListName"));
}
function getDnsQueriesAll(\SplFileObject $log) {
@@ -506,16 +534,33 @@
if($action && strlen($key) > 0)
{
// $action is true (we want to add) *and* key is not empty
$array[$key] = true;
$array[$key] = 1;
}
elseif(!$action && isset($array[$key]))
{
// $action is false (we want to remove) *and* key is set
unset($array[$key]);
$array[$key] = -1;
}
}
}
function getWildcardListContent() {
$rawList = file_get_contents(checkfile("/etc/dnsmasq.d/03-pihole-wildcard.conf"));
$wclist = explode("\n", $rawList);
$list = [];
foreach ($wclist as $entry) {
$expl = explode("/", $entry);
if(count($expl) == 3)
{
array_push($list,$expl[1]);
}
}
return array_unique($list);
}
function getGravity() {
global $gravity,$whitelist,$blacklist;
$domains = [];
@@ -577,7 +622,17 @@
function countBlockedQueries() {
global $logListName;
return exec("grep \"gravity.list\" $logListName | grep -c \" is \"");
// Blocked due to gravity entries (ad lists + blacklist)
$gravityblocked = intval(exec("grep -c -e \"gravity\.list.*is\" $logListName"));
// Blocked due to wildcard entries
$wildcard_domains = getWildcardListContent();
$wildcardblocked = 0;
foreach ($wildcard_domains as $domain) {
$wildcardblocked += intval(exec("grep -c -e \"config.*$domain is\" $logListName"));
}
return $gravityblocked +$wildcardblocked;
}
function getForwards(\SplFileObject $log) {
@@ -623,14 +678,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];
@@ -665,14 +718,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];
+45 -11
View File
@@ -1,21 +1,55 @@
<?php
if(!isset($_GET['list']))
die();
die("Missing parameter");
$type = $_GET['list'];
if($type !== "white" && $type !== "black")
die("Invalid list parameter");
$listtype = $_GET['list'];
require "func.php";
$rawList = file_get_contents(checkfile("/etc/pihole/${type}list.txt"));
$list = explode("\n", $rawList);
switch ($listtype) {
case "white":
$list = getListContent("white");
break;
case "black":
$list = array_merge(getListContent("black"),getWildcardListContent());
break;
default:
die("Invalid list parameter");
break;
}
function getListContent($type) {
$rawList = file_get_contents(checkfile("/etc/pihole/".$type."list.txt"));
$list = explode("\n", $rawList);
// Get rid of empty lines
for($i = sizeof($list)-1; $i >= 0; $i--) {
if($list[$i] == "")
unset($list[$i]);
}
return $list;
}
function getWildcardListContent() {
$rawList = file_get_contents(checkfile("/etc/dnsmasq.d/03-pihole-wildcard.conf"));
$wclist = explode("\n", $rawList);
$list = [];
foreach ($wclist as $entry) {
$expl = explode("/", $entry);
if(count($expl) == 3)
{
array_push($list,"*${expl[1]}");
}
}
return array_unique($list);
// Get rid of empty lines
for($i = sizeof($list)-1; $i >= 0; $i--) {
if($list[$i] == "")
unset($list[$i]);
}
function filterArray(&$inArray) {
+17 -15
View File
@@ -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']))
{
@@ -149,6 +150,7 @@
<link href="style/vendor/AdminLTE.min.css" rel="stylesheet" type="text/css" />
<link href="style/vendor/skin-blue.min.css" rel="stylesheet" type="text/css" />
<link href="style/pi-hole.css" rel="stylesheet" type="text/css" />
<link rel="icon" type="image/png" sizes="160x160" href="img/logo.svg" />
<style type="text/css">
.glow { text-shadow: 0px 0px 5px #fff; }
@@ -171,7 +173,7 @@
<!-- /JS Warning -->
<script src="scripts/pi-hole/js/header.js"></script>
<!-- Send token to JS -->
<div id="token" hidden><?php echo $token ?></div>
<div id="token" hidden><?php if($auth) echo $token; ?></div>
<div class="wrapper">
<header class="main-header">
<!-- Logo -->
@@ -256,18 +258,18 @@
<?php
$pistatus = exec('sudo pihole status web');
if ($pistatus == "1") {
echo '<a href="#" id="status"><i class="fa fa-circle" style="color:#7FFF00"></i> Active</a>';
echo '<a id="status"><i class="fa fa-circle" style="color:#7FFF00"></i> Active</a>';
} elseif ($pistatus == "0") {
echo '<a href="#" id="status"><i class="fa fa-circle" style="color:#FF0000"></i> Offline</a>';
echo '<a id="status"><i class="fa fa-circle" style="color:#FF0000"></i> Offline</a>';
} elseif ($pistatus == "-1") {
echo '<a href="#" id="status"><i class="fa fa-circle" style="color:#FF0000"></i> DNS service not running</a>';
echo '<a id="status"><i class="fa fa-circle" style="color:#FF0000"></i> DNS service not running</a>';
} else {
echo '<a href="#" id="status"><i class="fa fa-circle" style="color:#ff9900"></i> Unknown</a>';
echo '<a id="status"><i class="fa fa-circle" style="color:#ff9900"></i> Unknown</a>';
}
// CPU Temp
if ($celsius >= -273.15) {
echo "<a href=\"#\" id=\"temperature\"><i class=\"fa fa-fire\" style=\"color:";
echo "<a id=\"temperature\"><i class=\"fa fa-fire\" style=\"color:";
if ($celsius > 60) {
echo "#FF0000";
}
@@ -293,7 +295,7 @@
?>
<br/>
<?php
echo "<a href=\"#\"><i class=\"fa fa-circle\" style=\"color:";
echo "<a><i class=\"fa fa-circle\" style=\"color:";
if ($loaddata[0] > $nproc) {
echo "#FF0000";
}
@@ -305,7 +307,7 @@
?>
<br/>
<?php
echo "<a href=\"#\"><i class=\"fa fa-circle\" style=\"color:";
echo "<a><i class=\"fa fa-circle\" style=\"color:";
if ($memory_usage > 0.75 || $memory_usage < 0.0) {
echo "#FF0000";
}
+1 -1
View File
@@ -5,6 +5,7 @@
<div class="panel-title text-center"><span class="logo-lg" style="font-size: 25px;"><b>Pi</b>-hole</span></div>
<p class="login-box-msg">Sign in to start your session</p>
<div id="cookieInfo" class="panel-title text-center" style="color:#F00; font-size: 150%" hidden>Verify that cookies are allowed for <tt><?php echo $_SERVER['HTTP_HOST']; ?></tt></div>
<?php if ($wrongpassword) { ?>
<div class="form-group has-error login-box-msg">
<label class="control-label"><i class="fa fa-times-circle-o"></i> Wrong password!</label>
@@ -54,7 +55,6 @@
</div>
</div>
</form>
</div>
</div>
</div>
+1
View File
@@ -21,6 +21,7 @@
}
$wrongpassword = false;
$auth = false;
// Test if password is set
if(strlen($pwhash) > 0)
+4 -1
View File
@@ -6,7 +6,10 @@ header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
function echoEvent($datatext) {
echo "data: ".implode("\ndata: ", explode("\n", $datatext))."\n\n";
if(!isset($_GET["IE"]))
echo "data: ".implode("\ndata: ", explode("\n", $datatext))."\n\n";
else
echo $datatext;
}
// Credit: http://stackoverflow.com/a/4694816/2087442
+50 -50
View File
@@ -6,7 +6,7 @@ if(basename($_SERVER['SCRIPT_FILENAME']) !== "settings.php")
}
function validIP($address){
return !filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false;
return !filter_var($address, FILTER_VALIDATE_IP) === false;
}
// Check for existance of variable
@@ -32,20 +32,17 @@ function validDomain($domain_name)
return ( $validChars && $lengthCheck && $labelLengthCheck ); //length of each label
}
$primaryDNSservers = [
"8.8.8.8" => "Google",
"208.67.222.222" => "OpenDNS",
"4.2.2.1" => "Level3",
"199.85.126.10" => "Norton",
"8.26.56.26" => "Comodo"
];
$secondaryDNSservers = [
"8.8.4.4" => "Google",
"208.67.220.220" => "OpenDNS",
"4.2.2.2" => "Level3",
"199.85.127.10" => "Norton",
"8.20.247.20" => "Comodo"
$DNSserverslist = [
"8.8.8.8" => "Google (Primary)",
"208.67.222.222" => "OpenDNS (Primary)",
"4.2.2.1" => "Level3 (Primary)",
"199.85.126.10" => "Norton (Primary)",
"8.26.56.26" => "Comodo (Primary)",
"8.8.4.4" => "Google (Secondary)",
"208.67.220.220" => "OpenDNS (Secondary)",
"4.2.2.2" => "Level3 (Secondary)",
"199.85.127.10" => "Norton (Secondary)",
"8.20.247.20" => "Comodo (Secondary)"
];
$error = "";
@@ -57,46 +54,38 @@ function validDomain($domain_name)
switch ($_POST["field"]) {
// Set DNS server
case "DNS":
$primaryDNS = $_POST["primaryDNS"];
$secondaryDNS = $_POST["secondaryDNS"];
// Get primary DNS server IP address
if($primaryDNS === "Custom")
$DNSservers = [];
// Add selected predefined servers to list
foreach ($DNSserverslist as $key => $value)
{
$primaryIP = $_POST["DNS1IP"];
}
else
{
$primaryIP = array_flip($primaryDNSservers)[$primaryDNS];
}
// Validate primary IP
if (!validIP($primaryIP))
{
$error .= "Primary IP (".$primaryIP.") is invalid!<br>";
}
// Get secondary DNS server IP address
if($secondaryDNS === "Custom")
{
if(strlen($_POST["DNS2IP"]) > 0)
if(array_key_exists("DNSserver".str_replace(".","_",$key),$_POST))
{
$secondaryIP = $_POST["DNS2IP"];
}
else
{
$secondaryIP = "none";
array_push($DNSservers,$key);
}
}
else
// Test custom server fields
for($i=1;$i<=4;$i++)
{
$secondaryIP = array_flip($secondaryDNSservers)[$secondaryDNS];
if(array_key_exists("custom".$i,$_POST))
{
$IP = $_POST["custom".$i."val"];
if(validIP($IP))
{
array_push($DNSservers,$IP);
}
else
{
$error .= "IP (".$IP.") is invalid!<br>";
}
}
}
// Validate secondary IP
if (!validIP($secondaryIP) && $secondaryIP != "none" && strlen($secondaryIP) > 0)
// Check if at least one DNS server has been added
if(count($DNSservers) < 1)
{
$error .= "Secondary IP (".$secondaryIP.") is invalid!<br>";
$error .= "No DNS server has been selected.<br>";
}
// Check if domain-needed is requested
@@ -112,18 +101,29 @@ function validDomain($domain_name)
// Check if domain-needed is requested
if(isset($_POST["DNSbogusPriv"]))
{
$extra .= "bogus-priv";
$extra .= "bogus-priv ";
}
else
{
$extra .= "no-bogus-priv";
$extra .= "no-bogus-priv ";
}
// Check if DNSSEC is requested
if(isset($_POST["DNSSEC"]))
{
$extra .= "dnssec";
}
else
{
$extra .= "no-dnssec";
}
// If there has been no error we can save the new DNS server IPs
if(!strlen($error))
{
exec("sudo pihole -a setdns ".$primaryIP." ".$secondaryIP." ".$extra);
$success .= "The DNS settings have been updated";
$IPs = implode (",", $DNSservers);
exec("sudo pihole -a setdns ".$IPs." ".$extra);
$success .= "The DNS settings have been updated (using ".count($DNSservers)." DNS servers)";
}
else
{
+3
View File
@@ -13,6 +13,9 @@ switch($type) {
case "black":
exec("sudo pihole -b -q -d ${_POST['domain']}");
break;
case "wild":
exec("sudo pihole -wild -q -d ${_POST['domain']}");
break;
}
?>
+148
View File
@@ -0,0 +1,148 @@
<?php
require('func.php');
function process_zip($name)
{
global $zip;
$zippointer = $zip->getStream($name);
if(!$zippointer)
{
echo "$name not found in provided ZIP file, skipping...<br>";
return;
}
$contents = "";
while (!feof($zippointer)) {
$contents .= fread($zippointer, 4096);
}
fclose($zippointer);
$domains = array_filter(explode("\n",$contents));
check_domains($domains);
return $domains;
}
function add_to_zip($path,$name)
{
global $zip;
if(file_exists($path.$name))
$zip->addFile($path.$name,$name);
}
function check_domains($domains)
{
foreach($domains as $domain)
{
if(!is_valid_domain_name($domain)){
die(htmlspecialchars($domain).' is not a valid domain');
}
}
}
function getWildcardListContent() {
if(file_exists("/etc/dnsmasq.d/03-pihole-wildcard.conf"))
{
$rawList = file_get_contents("/etc/dnsmasq.d/03-pihole-wildcard.conf");
$wclist = explode("\n", $rawList);
$list = [];
foreach ($wclist as $entry) {
$expl = explode("/", $entry);
if(count($expl) == 3)
{
array_push($list,$expl[1]);
}
}
return implode("\n",array_unique($list));
}
return "";
}
if($_POST["action"] == "in")
{
if($_FILES["zip_file"]["name"])
{
$filename = $_FILES["zip_file"]["name"];
$source = $_FILES["zip_file"]["tmp_name"];
$type = $_FILES["zip_file"]["type"];
$name = explode(".", $filename);
$accepted_types = array('application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed');
$okay = false;
foreach($accepted_types as $mime_type) {
if($mime_type == $type) {
$okay = true;
break;
}
}
$continue = strtolower($name[1]) == 'zip' ? true : false;
if(!$continue || !$okay) {
die("The file you are trying to upload is not a .zip file. Please try again.");
}
$zip = new ZipArchive();
$x = $zip->open($source);
if ($x === true) {
if(isset($_POST["blacklist"]))
{
$blacklist = process_zip("blacklist.txt");
exec("sudo pihole -b -q ".implode(" ", $blacklist));
}
if(isset($_POST["whitelist"]))
{
$whitelist = process_zip("whitelist.txt");
exec("sudo pihole -w -q ".implode(" ", $whitelist));
}
if(isset($_POST["wildlist"]))
{
$wildlist = process_zip("wildcardblocking.txt");
exec("sudo pihole -wild -q ".implode(" ", $wildlist));
}
echo "OK";
$zip->close();
}
else
{
die("Error opening uploaded archive!");
}
}
else
{
die("No file transmitted.");
}
}
else
{
$archive_file_name = "/var/www/html/pi-hole-teleporter_".microtime(true).".zip";
$zip = new ZipArchive();
touch($archive_file_name);
$res = $zip->open($archive_file_name, ZipArchive::CREATE | ZipArchive::OVERWRITE);
if ($res !== TRUE) {
exit("cannot open/create $archive_file_name<br>Error: ".$zip->getStatusString()."<br>PHP user: ".exec('whoami')."\n");
}
add_to_zip("/etc/pihole/","whitelist.txt");
add_to_zip("/etc/pihole/","blacklist.txt");
add_to_zip("/etc/pihole/","adlists.list");
add_to_zip("/etc/pihole/","setupVars.conf");
$zip->addFromString("wildcardblocking.txt", getWildcardListContent());
$zip->close();
header("Content-type: application/zip");
header('Content-Transfer-Encoding: binary');
header("Content-Disposition: attachment; filename=pi-hole-teleporter.zip");
header("Content-length: " . filesize($archive_file_name));
header("Pragma: no-cache");
header("Expires: 0");
ob_end_clean();
readfile($archive_file_name);
exit;
}
?>
+7
View File
@@ -0,0 +1,7 @@
/*!
* jQuery Input Ip Address Control : v0.1beta (2010/11/09 16:15:43)
* Copyright (c) 2010 jquery-input-ip-address-control@googlecode.com
* Licensed under the MIT license and GPL licenses.
*
*/
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(l($){Q.1o.1t=l(){E=/\\b(?:(?:25[0-5]|2[0-4][0-9]|[1m]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[1m]?[0-9][0-9]?)\\b/;h E.1a(p.1i())};Q.1o.1S=l(){E=/\\b([A-16-9]{1,4}:){7}([A-16-9]{1,4})\\b/i;h E.1a(p.1i())};$.1X.1h({y:l(u,c){f(p.z==0)h;f(1k u==\'1f\'){c=(1k c==\'1f\')?c:u;h p.1d(l(){f(p.18){p.1s();p.18(u,c)}w f(p.1e){t C=p.1e();C.1L(S);C.1z(\'10\',c);C.1b(\'10\',u);C.1w()}})}w{f(p[0].18){u=p[0].1u;c=p[0].1C}w f(14.12&&14.12.19){t C=14.12.19();u=0-C.1D().1b(\'10\',-1E);c=u+C.1H.z}h{u:u,c:c}}},1B:l(s){s=$.1h({v:4},s);f(s.v==4){s.W=M I(\'[0-9]\',\'g\');s.r=\'R.R.R.R\'}f(s.v==6){s.W=M I(\'[A-16-9]\',\'1x\');s.r=\'x:x:x:x:x:x:x:x\'}s.D=s.r.K(\'\').Y();s.q=s.r.X(M I(s.D,\'g\'),\'\').K(\'\').Y();s.O=s.r.K(s.q).Y();h $(p).1d(l(){t a={k:T,n:T,o:T,d:T};a.d=$(p);f(a.d.m()==\'\'||!J(a.d.m()))a.d.m(s.r);a.d.1j(\'1Z\',(s.v==4?15:1c)).1j(\'1W\',(s.v==4?15:1c));l J(o){h 24("o.21"+s.v+"()")};l P(){a.k=a.d.y();a.o=J(L(a.d.m()))?L(a.d.m()):a.o;a.n=a.d.m().K(\'\')};l 1n(o){t G=o.K(s.q);1p(t j=0;j<G.z;j++){1M((s.O.z-G[j].z)>0)G[j]+=s.D}h G.H(s.q)};l L(o){t E=M I(s.O,\'g\');t 1g=M I(s.D,\'g\');h o.X(E,\'0\').X(1g,\'\')};l 11(e){1R(e.1Q){U 8:f(a.n[a.k.c-1]!=s.q){a.n[a.k.c-1]=s.D;a.d.m(a.n.H("")).m()}a.d.y(a.k.c-1);h B;V;U 13:U 1T:a.d.17();V;U 1P:f(a.n[a.k.c]!=s.q&&a.k.c<s.r.z){a.n[a.k.c]=s.D;a.d.m(a.n.H(""))}f(a.k.c<s.r.z)a.d.y(a.k.c+1);h B;V}h S};a.d.N(\'1O\',l(e){P();f($.1l.1K||$.1l.1V){h 11(e)}}).N(\'1U\',l(e){P();f(e.23||e.22||e.1Y)h S;w f((e.F>=20&&e.F<=1N)||e.F>1J){f(Q.1q(e.F).1y(s.W)){a.n[a.k.c]=Q.1q(e.F);f(!J(L(a.n.H(\'\')))){f((a.k.c==0||a.n[a.k.c-1]==s.q)){1p(t i=a.k.c+1;i<a.k.c+s.O.z;i++){a.n[i]=s.D}}w h B}a.d.m(a.n.H(\'\'));f(a.n[a.k.c+1]==s.q){a.d.y(a.k.c+2)}w{a.d.y(a.k.c+1)}h B}w f(s.q.1v(0)==e.F){t Z=a.d.m().1A(s.q,a.k.c);f(Z==-1)h B;f(a.n[a.k.c-1]==s.q)h B;a.k.c=Z;a.d.y(a.k.c+1);h B}w h B}h 11(e)}).N(\'17\',l(){f(a.d.m()==s.r)h S;t o=L($.1G(a.d.m()));f(J(o))a.d.m(o);w a.d.m(a.o)}).N(\'1s\',l(){1r(l(){P();f(a.d.m()!=s.r)a.d.m(1n(a.o));a.d.y(0)},0)}).N(\'1I\',l(e){a.d.m(\'\');1r(l(){a.d.17()},0)})})}})})(1F);',62,130,'||||||||||ctx||end|input||if||return|||cursor|function|val|buffer|ip|this|separator|label||var|begin||else|____|caret|length||false|range|place|rgx|which|part|join|RegExp|isIp|split|unmask|new|bind|partplace|loadCtx|String|___|true|null|case|break|rgxcase|replace|pop|pos|character|entryNoCharacter|selection||document||F0|blur|setSelectionRange|createRange|test|moveStart|39|each|createTextRange|number|rgx2|extend|toString|attr|typeof|browser|01|mask|prototype|for|fromCharCode|setTimeout|focus|isIpv4|selectionStart|charCodeAt|select|gi|match|moveEnd|indexOf|ipAddress|selectionEnd|duplicate|100000|jQuery|trim|text|paste|186|webkit|collapse|while|125|keydown|46|keyCode|switch|isIpv6|27|keypress|msie|size|fn|metaKey|maxlength|32|isIpv|altKey|ctrlKey|eval|'.split('|'),0,{}))
+125 -43
View File
@@ -198,7 +198,7 @@
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">From</div>
<input type="text" class="form-control DHCPgroup" name="from" value="<?php echo $DHCPstart; ?>" data-inputmask="'alias': 'ip'" data-mask <?php if(!$DHCP){ ?>disabled<?php } ?>>
<input type="text" class="form-control DHCPgroup" name="from" id="DHCPfrom" value="<?php echo $DHCPstart; ?>" <?php if(!$DHCP){ ?>disabled<?php } ?>>
</div>
</div>
</div>
@@ -206,7 +206,7 @@
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">To</div>
<input type="text" class="form-control DHCPgroup" name="to" value="<?php echo $DHCPend; ?>" data-inputmask="'alias': 'ip'" data-mask <?php if(!$DHCP){ ?>disabled<?php } ?>>
<input type="text" class="form-control DHCPgroup" name="to" id="DHCPto" value="<?php echo $DHCPend; ?>" <?php if(!$DHCP){ ?>disabled<?php } ?>>
</div>
</div>
</div>
@@ -215,7 +215,7 @@
<div class="form-group">
<div class="input-group">
<div class="input-group-addon">Router</div>
<input type="text" class="form-control DHCPgroup" name="router" value="<?php echo $DHCProuter; ?>" data-inputmask="'alias': 'ip'" data-mask <?php if(!$DHCP){ ?>disabled<?php } ?>>
<input type="text" class="form-control DHCPgroup" name="router" id="DHCProuter" value="<?php echo $DHCProuter; ?>" <?php if(!$DHCP){ ?>disabled<?php } ?>>
</div>
</div>
</div>
@@ -360,30 +360,41 @@
<?php
// DNS settings
if(isset($setupVars["PIHOLE_DNS_1"])){
if(isset($primaryDNSservers[$setupVars["PIHOLE_DNS_1"]]))
{
$piHoleDNS1 = $primaryDNSservers[$setupVars["PIHOLE_DNS_1"]];
$DNSservers = [];
$DNSactive = [];
for($i=1;$i<=12;$i++)
{
if(isset($setupVars["PIHOLE_DNS_".$i])){
if(isset($DNSserverslist[$setupVars["PIHOLE_DNS_".$i]]))
{
$DNSservers[] = [$setupVars["PIHOLE_DNS_".$i],$DNSserverslist[$setupVars["PIHOLE_DNS_".$i]]];
array_push($DNSactive,$setupVars["PIHOLE_DNS_".$i]);
}
elseif(strpos($setupVars["PIHOLE_DNS_".$i],"."))
{
$DNSservers[] = [$setupVars["PIHOLE_DNS_".$i],"CustomIPv4"];
if(!isset($custom1))
{
$custom1 = $setupVars["PIHOLE_DNS_".$i];
}
else
{
$custom2 = $setupVars["PIHOLE_DNS_".$i];
}
}
elseif(strpos($setupVars["PIHOLE_DNS_".$i],":"))
{
$DNSservers[] = [$setupVars["PIHOLE_DNS_".$i],"CustomIPv6"];
if(!isset($custom3))
{
$custom3 = $setupVars["PIHOLE_DNS_".$i];
}
else
{
$custom4 = $setupVars["PIHOLE_DNS_".$i];
}
}
}
else
{
$piHoleDNS1 = "Custom";
}
} else {
$piHoleDNS1 = "unknown";
}
if(isset($setupVars["PIHOLE_DNS_2"])){
if(isset($secondaryDNSservers[$setupVars["PIHOLE_DNS_2"]]))
{
$piHoleDNS2 = $secondaryDNSservers[$setupVars["PIHOLE_DNS_2"]];
}
else
{
$piHoleDNS2 = "Custom";
}
} else {
$piHoleDNS2 = "unknown";
}
if(isset($setupVars["DNS_FQDN_REQUIRED"])){
@@ -411,6 +422,19 @@
} else {
$DNSbogusPriv = true;
}
if(isset($setupVars["DNSSEC"])){
if($setupVars["DNSSEC"])
{
$DNSSEC = true;
}
else
{
$DNSSEC = false;
}
} else {
$DNSSEC = false;
}
?>
<div class="box box-warning">
<div class="box-header with-border">
@@ -419,28 +443,41 @@
<div class="box-body">
<form role="form" method="post">
<div class="col-lg-6">
<label>Primary DNS Server</label>
<label>Upstream DNS Servers</label>
<div class="form-group">
<?php foreach ($primaryDNSservers as $key => $value) { ?> <div class="radio"><label><input type="radio" name="primaryDNS" value="<?php echo $value;?>" <?php if($piHoleDNS1 === $value){ ?>checked<?php } ?> ><?php echo $value;?> (<?php echo $key;?>)</label></div> <?php } ?>
<label>Custom</label>
<div class="input-group">
<div class="input-group-addon"><input type="radio" name="primaryDNS" value="Custom"
<?php if($piHoleDNS1 === "Custom"){ ?>checked<?php } ?>></div>
<input type="text" name="DNS1IP" class="form-control" data-inputmask="'alias': 'ip'" data-mask
<?php if($piHoleDNS1 === "Custom"){ ?>value="<?php echo $setupVars["PIHOLE_DNS_1"]; ?>"<?php } ?>>
</div>
<?php foreach ($DNSserverslist as $key => $value) { ?>
<div class="checkbox">
<label title="<?php echo $key;?>">
<input type="checkbox" name="DNSserver<?php echo $key;?>" value="true" <?php if(in_array($key,$DNSactive)){ ?>checked<?php } ?> ><?php echo $value;?></label>
</div> <?php } ?>
</div>
</div>
<div class="col-lg-6">
<label>Secondary DNS Server</label>
<label>&nbsp;</label>
<div class="form-group">
<?php foreach ($secondaryDNSservers as $key => $value) { ?> <div class="radio"><label><input type="radio" name="secondaryDNS" value="<?php echo $value;?>" <?php if($piHoleDNS2 === $value){ ?>checked<?php } ?> ><?php echo $value;?> (<?php echo $key;?>)</label></div> <?php } ?>
<label>Custom</label>
<label>Custom 1 (IPv4)</label>
<div class="input-group">
<div class="input-group-addon"><input type="radio" name="secondaryDNS" value="Custom"
<?php if($piHoleDNS2 === "Custom"){ ?>checked<?php } ?>></div>
<input type="text" name="DNS2IP" class="form-control" data-inputmask="'alias': 'ip'" data-mask
<?php if($piHoleDNS2 === "Custom"){ ?>value="<?php echo $setupVars["PIHOLE_DNS_2"]; ?>"<?php } ?>>
<div class="input-group-addon"><input type="checkbox" name="custom1" value="Customv4"
<?php if(isset($custom1)){ ?>checked<?php } ?>></div>
<input type="text" name="custom1val" class="form-control" id="custom1val" <?php if(isset($custom1)){ ?>value="<?php echo $custom1; ?>"<?php } ?>>
</div>
<label>Custom 2 (IPv4)</label>
<div class="input-group">
<div class="input-group-addon"><input type="checkbox" name="custom2" value="Customv4"
<?php if(isset($custom2)){ ?>checked<?php } ?>></div>
<input type="text" name="custom2val" class="form-control" id="custom2val" <?php if(isset($custom2)){ ?>value="<?php echo $custom2; ?>"<?php } ?>>
</div>
<label>Custom 3 (IPv6)</label>
<div class="input-group">
<div class="input-group-addon"><input type="checkbox" name="custom3" value="Customv6"
<?php if(isset($custom3)){ ?>checked<?php } ?>></div>
<input type="text" name="custom3val" class="form-control" id="custom3val" <?php if(isset($custom3)){ ?>value="<?php echo $custom3; ?>"<?php } ?>>
</div>
<label>Custom 4 (IPv6)</label>
<div class="input-group">
<div class="input-group-addon"><input type="checkbox" name="custom4" value="Customv6"
<?php if(isset($custom4)){ ?>checked<?php } ?>></div>
<input type="text" name="custom4val" class="form-control" id="custom4val" <?php if(isset($custom4)){ ?>value="<?php echo $custom4; ?>"<?php } ?>>
</div>
</div>
</div>
@@ -459,6 +496,10 @@
<div class="checkbox"><label><input type="checkbox" name="DNSbogusPriv" <?php if($DNSbogusPriv){ ?>checked<?php } ?> title="bogus-priv"> never forward reverse lookups for private IP ranges</label></div>
</div>
<p>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</p>
<div class="form-group">
<div class="checkbox"><label><input type="checkbox" name="DNSSEC" <?php if($DNSSEC){ ?>checked<?php } ?>> Use DNSSEC</label></div>
</div>
<p>Validate DNS replies and cache DNSSEC data. When forwarding DNS queries, Pi-hole requests the DNSSEC records needed to validate the replies. Use Google or Norton DNS servers when activating DNSSEC. Note that the size of your log might increase significantly when enabling DNSSEC. A DNSSEC resolver test can be found <a href="http://dnssec.vs.uni-due.de/" target="_blank">here</a>.</p>
</div>
</div>
</div>
@@ -711,6 +752,47 @@
</form>
</div>
</div>
<div class="box box-danger collapsed-box">
<div class="box-header with-border">
<h3 class="box-title">Pi-hole Teleporter</h3>
<div class="box-tools pull-right"><button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-plus"></i></button></div>
</div>
<div class="box-body">
<?php if (extension_loaded('zip')) { ?>
<form role="form" method="post" id="takeoutform" action="scripts/pi-hole/php/teleporter.php" target="_blank" enctype="multipart/form-data">
<div class="col-lg-12">
<p>Export your Pi-hole lists as downloadable ZIP file</p>
<button type="submit" class="btn btn-default" name="action" value="out">Export</button>
<hr>
</div>
<div class="col-lg-6">
<label>Import ...</label>
<div class="form-group">
<div class="checkbox">
<label><input type="checkbox" name="whitelist" value="true" checked> Whitelist</label>
</div>
<div class="checkbox">
<label><input type="checkbox" name="blacklist" value="true" checked> Blacklist (exact)</label>
</div>
<div class="checkbox">
<label><input type="checkbox" name="wildlist" value="true" checked> Blacklist (wildcard)</label>
</div>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label for="zip_file">File input</label>
<input type="file" name="zip_file" id="zip_file">
<p class="help-block">Upload only Pi-hole backup files.</p>
<button type="submit" class="btn btn-default" name="action" value="in">Import</button>
</div>
</div>
</form>
<?php } else { ?>
<p>The PHP extension <tt>zip</tt> is not loaded. Please ensure it is installed and loaded if you want to use the Pi-hole teleporter.</p>
<?php } ?>
</div>
</div>
</div>
</div>
@@ -719,6 +801,6 @@
?>
<script src="scripts/vendor/jquery.inputmask.js"></script>
<script src="scripts/vendor/jquery.inputmask.extensions.js"></script>
<script src="scripts/vendor/jquery.input-ip-address-control-1.0.min.js"></script>
<script src="scripts/vendor/jquery.confirm.min.js"></script>
<script src="scripts/pi-hole/js/settings.js"></script>
+9
View File
@@ -0,0 +1,9 @@
.small-box {
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
/* Rules below not implemented in browsers yet */
-o-user-select: none;
user-select: none;
}