mirror of
https://github.com/pi-hole/web.git
synced 2024-12-06 19:36:21 +01:00
Merge pull request #1638 from pi-hole/release/v5.2
Pi-hole Web 5.2 Release PR
This commit is contained in:
+12
@@ -288,6 +288,11 @@ else
|
||||
// Get specific domain only
|
||||
sendRequestFTL("getallqueries-domain ".$_GET['domain']);
|
||||
}
|
||||
else if(isset($_GET['client']) && (isset($_GET['type']) && $_GET['type'] === "blocked"))
|
||||
{
|
||||
// Get specific client only
|
||||
sendRequestFTL("getallqueries-client-blocked ".$_GET['client']);
|
||||
}
|
||||
else if(isset($_GET['client']))
|
||||
{
|
||||
// Get specific client only
|
||||
@@ -411,6 +416,13 @@ else
|
||||
$data = array_merge($data, $result);
|
||||
}
|
||||
|
||||
if (isset($_GET['delete_lease']) && $auth)
|
||||
{
|
||||
sendRequestFTL("delete-lease ".$_GET['delete_lease']);
|
||||
$return = getResponseFTL();
|
||||
$data["delete_lease"] = $return[0];
|
||||
}
|
||||
|
||||
disconnectFTL();
|
||||
}
|
||||
?>
|
||||
|
||||
+20
-11
@@ -62,18 +62,27 @@ if(isset($_GET["network"]) && $auth)
|
||||
|
||||
while($results !== false && $res = $results->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
$id = $res["id"];
|
||||
// Empty array for holding the IP addresses
|
||||
$id = intval($res["id"]);
|
||||
|
||||
// Get IP addresses and host names for this device
|
||||
$res["ip"] = array();
|
||||
// Get IP addresses for this device
|
||||
$network_addresses = $db->query("SELECT ip FROM network_addresses WHERE network_id = $id ORDER BY lastSeen DESC");
|
||||
while($network_addresses !== false && $ip = $network_addresses->fetchArray(SQLITE3_ASSOC))
|
||||
array_push($res["ip"],$ip["ip"]);
|
||||
// UTF-8 encode host name and vendor
|
||||
$res["name"] = utf8_encode($res["name"]);
|
||||
$res["name"] = array();
|
||||
$network_addresses = $db->query("SELECT ip,name FROM network_addresses WHERE network_id = $id ORDER BY lastSeen DESC");
|
||||
while($network_addresses !== false && $network_address = $network_addresses->fetchArray(SQLITE3_ASSOC))
|
||||
{
|
||||
array_push($res["ip"],$network_address["ip"]);
|
||||
if($network_address["name"] !== null)
|
||||
array_push($res["name"],utf8_encode($network_address["name"]));
|
||||
else
|
||||
array_push($res["name"],"");
|
||||
}
|
||||
$network_addresses->finalize();
|
||||
|
||||
// UTF-8 encode vendor
|
||||
$res["macVendor"] = utf8_encode($res["macVendor"]);
|
||||
array_push($network, $res);
|
||||
}
|
||||
$results->finalize();
|
||||
|
||||
$data = array_merge($data, array('network' => $network));
|
||||
}
|
||||
@@ -85,7 +94,7 @@ if (isset($_GET['getAllQueries']) && $auth)
|
||||
{
|
||||
$from = intval($_GET["from"]);
|
||||
$until = intval($_GET["until"]);
|
||||
$dbquery = "SELECT timestamp, type, domain, client, status FROM queries WHERE timestamp >= :from AND timestamp <= :until ";
|
||||
$dbquery = "SELECT timestamp, type, domain, client, status, forward FROM queries WHERE timestamp >= :from AND timestamp <= :until ";
|
||||
if(isset($_GET["types"]))
|
||||
{
|
||||
$types = $_GET["types"];
|
||||
@@ -144,8 +153,8 @@ if (isset($_GET['getAllQueries']) && $auth)
|
||||
$query_type = "UNKN";
|
||||
break;
|
||||
}
|
||||
// array: time type domain client status
|
||||
$allQueries[] = [$row[0], $query_type, utf8_encode($row[2]), utf8_encode($c), $row[4]];
|
||||
// array: time type domain client status upstream destination
|
||||
$allQueries[] = [$row[0], $query_type, utf8_encode($row[2]), utf8_encode($c), $row[4], utf8_encode($row[5])];
|
||||
}
|
||||
}
|
||||
$result = array('data' => $allQueries);
|
||||
|
||||
+2
-2
@@ -72,8 +72,8 @@
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/auditlog.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/auditlog.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php /*
|
||||
* 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. */
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
|
||||
?>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="page-header">
|
||||
<h1>Local CNAME Records</h1>
|
||||
<small>On this page, you can add CNAME records.</small>
|
||||
</div>
|
||||
|
||||
<!-- Domain Input -->
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<!-- /.box-header -->
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
Add a new CNAME record
|
||||
</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6">
|
||||
<label for="domain">Domain:</label>
|
||||
<input id="domain" type="url" class="form-control" placeholder="Add a domain (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="target">Target Domain:</label>
|
||||
<input id="target" type="url" class="form-control" placeholder="Associated Target Domain" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer clearfix">
|
||||
<button type="button" id="btnAdd" class="btn btn-primary pull-right">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Alerts -->
|
||||
<div id="alInfo" class="alert alert-info alert-dismissible fade in" role="alert" hidden>
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Updating CNAME records...
|
||||
</div>
|
||||
<div id="alSuccess" class="alert alert-success alert-dismissible fade in" role="alert" hidden>
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Success! The list will refresh.
|
||||
</div>
|
||||
<div id="alFailure" class="alert alert-danger alert-dismissible fade in" role="alert" hidden>
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
Failure! Something went wrong, see output below:<br/><br/><pre><span id="err"></span></pre>
|
||||
</div>
|
||||
<div id="alWarning" class="alert alert-warning alert-dismissible fade in" role="alert" hidden>
|
||||
<button type="button" class="close" data-hide="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
At least one domain was already present, see output below:<br/><br/><pre><span id="warn"></span></pre>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box" id="recent-queries">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
List of local CNAME records
|
||||
</h3>
|
||||
</div>
|
||||
<!-- /.box-header -->
|
||||
<div class="box-body">
|
||||
<table id="customCNAMETable" class="table table-striped table-bordered" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domain</th>
|
||||
<th>Target</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<button type="button" id="resetButton" class="btn btn-default btn-sm text-red hidden">Clear Filters</button>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
</div>
|
||||
<!-- /.box -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/customcname.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
?>
|
||||
+3
-3
@@ -67,9 +67,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/vendor/daterangepicker.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/db_graph.js"></script>
|
||||
<script src="scripts/vendor/daterangepicker.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/db_graph.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+3
-3
@@ -139,9 +139,9 @@ else
|
||||
<!-- /.col -->
|
||||
</div>
|
||||
|
||||
<script src="scripts/vendor/daterangepicker.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/db_lists.js"></script>
|
||||
<script src="scripts/vendor/daterangepicker.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/db_lists.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+39
-3
@@ -45,6 +45,7 @@
|
||||
<div class="col-md-3">
|
||||
<div><input type="checkbox" id="type_forwarded" checked><label for="type_forwarded">Permitted: forwarded</label><br></div>
|
||||
<div><input type="checkbox" id="type_cached" checked><label for="type_cached">Permitted: cached</label></div>
|
||||
<div><input type="checkbox" id="type_retried" checked><label for="type_retried">Permitted: Retried</label></div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div><input type="checkbox" id="type_gravity" checked><label for="type_gravity">Blocked: gravity</label><br></div>
|
||||
@@ -122,6 +123,40 @@
|
||||
<!-- ./col -->
|
||||
</div>
|
||||
|
||||
<!-- Alert Modal -->
|
||||
<div id="alertModal" class="modal fade" role="dialog" data-backdrop="static" data-keyboard="false">
|
||||
<div class="vertical-alignment-helper">
|
||||
<div class="modal-dialog vertical-align-center">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body text-center">
|
||||
<span class="fa-stack fa-2x" style="margin-bottom: 10px">
|
||||
<div class="alProcessing">
|
||||
<i class="fa-stack-2x alSpinner"></i>
|
||||
</div>
|
||||
<div class="alSuccess" style="display: none">
|
||||
<i class="fa fa-circle fa-stack-2x text-green"></i>
|
||||
<i class="fa fa-check fa-stack-1x fa-inverse"></i>
|
||||
</div>
|
||||
<div class="alFailure" style="display: none">
|
||||
<i class="fa fa-circle fa-stack-2x text-red"></i>
|
||||
<i class="fa fa-times fa-stack-1x fa-inverse"></i>
|
||||
</div>
|
||||
</span>
|
||||
<div class="alProcessing">Adding <span id="alDomain"></span> to the <span id="alList"></span>...</div>
|
||||
<div class="alSuccess text-bold text-green" style="display: none"><span id="alDomain"></span> successfully added to the <span id="alList"></span></div>
|
||||
<div class="alFailure text-bold text-red" style="display: none">
|
||||
<span id="alNetErr">Timeout or Network Connection Error!</span>
|
||||
<span id="alCustomErr"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="box" id="recent-queries">
|
||||
@@ -159,9 +194,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
|
||||
<script src="scripts/vendor/daterangepicker.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/db_queries.js"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/daterangepicker.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/db_queries.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<button type="button" id="debugBtn" class="btn btn-lg btn-primary btn-block">Generate debug log</button>
|
||||
<pre id="output" style="width: 100%; height: 100%;" hidden></pre>
|
||||
|
||||
<script src="scripts/pi-hole/js/debug.js"></script>
|
||||
<script src="scripts/pi-hole/js/debug.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+3
-3
@@ -89,9 +89,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
|
||||
<script src="scripts/pi-hole/js/customdns.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/customdns.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@
|
||||
<button type="button" id="gravityBtn" class="btn btn-lg btn-primary btn-block">Update</button>
|
||||
<pre id="output" style="width: 100%; height: 100%;" hidden></pre>
|
||||
|
||||
<script src="scripts/pi-hole/js/gravity.js"></script>
|
||||
<script src="scripts/pi-hole/js/gravity.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+4
-4
@@ -77,10 +77,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/vendor/bootstrap-select.min.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups-adlists.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-select.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/groups-adlists.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+19
-8
@@ -30,14 +30,25 @@
|
||||
<label for="select">Known clients:</label>
|
||||
<select id="select" class="form-control" placeholder="">
|
||||
<option disabled selected>Loading...</option>
|
||||
</select><br>
|
||||
<input id="ip-custom" type="text" class="form-control" disabled placeholder="Client IP address (IPv4 or IPv6, CIDR subnetting available, optional)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-6">
|
||||
<label for="new_comment">Comment:</label>
|
||||
<input id="new_comment" type="text" class="form-control" placeholder="Client description (optional)">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>You can select an existing client or add a custom one by typing into the field above and confirming your entry with <kbd>⏎</kbd>.</p>
|
||||
<p>Clients may be described either by their IP addresses (IPv4 and IPv6 are supported),
|
||||
IP subnets (CIDR notation, like <code>192.168.2.0/24</code>),
|
||||
their MAC addresses (like <code>12:34:56:78:9A:BC</code>),
|
||||
by their hostnames (like <code>localhost</code>), or by the interface they are connected to (prefaced with a colon, like <code>:eth0</code>).</p>
|
||||
<p>Note that client recognition by IP addresses (incl. subnet ranges) are prefered over MAC address, host name or interface recognition as
|
||||
the two latter will only be available after some time.
|
||||
Furthermore, MAC address recognition only works for devices at most one networking hop away from your Pi-hole.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-footer clearfix">
|
||||
<button type="button" id="btnAdd" class="btn btn-primary pull-right">Add</button>
|
||||
@@ -59,7 +70,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>IP address</th>
|
||||
<th title="Acceptable values are: IP address, subnet (CIDR notation), MAC address (AA:BB:CC:DD:EE:FF format) or host names.">Client</th>
|
||||
<th>Comment</th>
|
||||
<th>Group assignment</th>
|
||||
<th>Action</th>
|
||||
@@ -74,11 +85,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/vendor/bootstrap-select.min.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups-clients.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-select.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/groups-clients.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+4
-4
@@ -138,10 +138,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/vendor/bootstrap-select.min.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups-domains.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-select.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/groups-domains.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+4
-4
@@ -76,10 +76,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/vendor/bootstrap-select.min.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/groups.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-select.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/bootstrap-toggle.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/groups.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
$indexpage = true;
|
||||
require "scripts/pi-hole/php/header.php";
|
||||
require_once("scripts/pi-hole/php/gravity.php");
|
||||
require_once "scripts/pi-hole/php/gravity.php";
|
||||
|
||||
function getinterval()
|
||||
{
|
||||
@@ -294,8 +294,8 @@ else
|
||||
<!-- /.row -->
|
||||
<?php } ?>
|
||||
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/index.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/index.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/messages.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/messages.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+3
-3
@@ -66,9 +66,9 @@
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
|
||||
<script src="scripts/pi-hole/js/network.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/network.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
Generated
+670
-476
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -23,11 +23,11 @@
|
||||
"test": "npm run prettier:check && npm run xo"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.8.5",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"eslint-plugin-compat": "^3.8.0",
|
||||
"postcss-cli": "^7.1.1",
|
||||
"prettier": "2.0.4",
|
||||
"xo": "^0.32.1"
|
||||
"postcss-cli": "^7.1.2",
|
||||
"prettier": "2.2.1",
|
||||
"xo": "^0.35.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
|
||||
+31
-5
@@ -28,6 +28,10 @@ if(isset($setupVars["API_QUERY_LOG_SHOW"]))
|
||||
$showing = "showing no queries (due to setting)";
|
||||
}
|
||||
}
|
||||
else if(isset($_GET["type"]) && $_GET["type"] === "blocked")
|
||||
{
|
||||
$showing = "showing blocked";
|
||||
}
|
||||
else
|
||||
{
|
||||
// If filter variable is not set, we
|
||||
@@ -44,6 +48,24 @@ else if(isset($_GET["client"]))
|
||||
{
|
||||
$showing .= " queries for client ".htmlentities($_GET["client"]);
|
||||
}
|
||||
else if(isset($_GET["forwarddest"]))
|
||||
{
|
||||
if($_GET["forwarddest"] === "blocklist")
|
||||
$showing .= " queries answered from blocklists";
|
||||
elseif($_GET["forwarddest"] === "cache")
|
||||
$showing .= " queries answered from cache";
|
||||
else
|
||||
$showing .= " queries for upstream destination ".htmlentities($_GET["forwarddest"]);
|
||||
}
|
||||
else if(isset($_GET["querytype"]))
|
||||
{
|
||||
$qtypes = ["A (IPv4)", "AAAA (IPv6)", "ANY", "SRV", "SOA", "PTR", "TXT", "NAPTR"];
|
||||
$qtype = intval($_GET["querytype"]);
|
||||
if($qtype > 0 && $qtype <= count($qtypes))
|
||||
$showing .= " ".$qtypes[$qtype-1]." queries";
|
||||
else
|
||||
$showing .= " type ".$qtype." queries";
|
||||
}
|
||||
else if(isset($_GET["domain"]))
|
||||
{
|
||||
$showing .= " queries for domain ".htmlentities($_GET["domain"]);
|
||||
@@ -101,6 +123,9 @@ if(strlen($showing) > 0)
|
||||
<span id="alCustomErr"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -140,8 +165,9 @@ if(strlen($showing) > 0)
|
||||
</table>
|
||||
<p><strong>Filtering options:</strong></p>
|
||||
<ul>
|
||||
<li>Use <kbd>Ctrl</kbd> or <kbd>⌘</kbd> + <i class="fas fa-mouse-pointer"></i> to add columns to the current filter</li>
|
||||
<li>Use <kbd>Shift</kbd> + <i class="fas fa-mouse-pointer"></i> to remove columns from the current filter</li>
|
||||
<li>Click a value in a column to add/remove that value to/from the filter</li>
|
||||
<li>On a computer: Hold down <kbd>Ctrl</kbd>, <kbd>Alt</kbd>, or <kbd>⌘</kbd> to allow highlighting for copying to clipboard</li>
|
||||
<li>On a mobile: Long press to highlight the text and enable copying to clipboard
|
||||
</ul><br/><button type="button" id="resetButton" class="btn btn-default btn-sm text-red hidden">Clear filters</button>
|
||||
</div>
|
||||
<!-- /.box-body -->
|
||||
@@ -150,9 +176,9 @@ if(strlen($showing) > 0)
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.row -->
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/queries.js"></script>
|
||||
<script src="scripts/pi-hole/js/ip-address-sorting.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/queries.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+17
-6
@@ -15,13 +15,24 @@
|
||||
<div class="col-md-12">
|
||||
<div class="box">
|
||||
<div class="box-body">
|
||||
<div class="form-group">
|
||||
<div class="input-group">
|
||||
<input id="domain" type="text" class="form-control" placeholder="Domain to look for (example.com or sub.example.com)">
|
||||
<!-- Domain Input <992px -->
|
||||
<div class="visible-xs-block visible-sm-block">
|
||||
<div class="input-group-block">
|
||||
<input id="domain_1" type="url" class="form-control" placeholder="Domain to look for (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off" style="margin-bottom: 5px">
|
||||
<input id="quiet" type="hidden" value="no">
|
||||
<div class="text-center" style="display: block; width: 100%">
|
||||
<button type="button" id="btnSearch_1" class="btn btn-default">Search partial match</button>
|
||||
<button type="button" id="btnSearchExact_1" class="btn btn-default">Search exact match</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Domain Input >=992px -->
|
||||
<div class="visible-md-block visible-lg-block">
|
||||
<div class="input-group">
|
||||
<input id="domain_2" type="url" class="form-control" placeholder="Domain to look for (example.com or sub.example.com)" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off">
|
||||
<span class="input-group-btn">
|
||||
<button type="button" id="btnSearch" class="btn btn-default">Search partial match</button>
|
||||
<button type="button" id="btnSearchExact" class="btn btn-default">Search exact match</button>
|
||||
<button type="button" id="btnSearch_2" class="btn btn-default">Search partial match</button>
|
||||
<button type="button" id="btnSearchExact_2" class="btn btn-default">Search exact match</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -32,7 +43,7 @@
|
||||
|
||||
<pre id="output" style="width: 100%; height: 100%;" hidden></pre>
|
||||
|
||||
<script src="scripts/pi-hole/js/queryads.js"></script>
|
||||
<script src="scripts/pi-hole/js/queryads.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/* 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. */
|
||||
|
||||
/* global utils:false */
|
||||
|
||||
var table;
|
||||
var token = $("#token").text();
|
||||
|
||||
function showAlert(type, message) {
|
||||
var alertElement = null;
|
||||
var messageElement = null;
|
||||
|
||||
switch (type) {
|
||||
case "info":
|
||||
alertElement = $("#alInfo");
|
||||
break;
|
||||
case "success":
|
||||
alertElement = $("#alSuccess");
|
||||
break;
|
||||
case "warning":
|
||||
alertElement = $("#alWarning");
|
||||
messageElement = $("#warn");
|
||||
break;
|
||||
case "error":
|
||||
alertElement = $("#alFailure");
|
||||
messageElement = $("#err");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (messageElement !== null) messageElement.html(message);
|
||||
|
||||
alertElement.fadeIn(200);
|
||||
alertElement.delay(8000).fadeOut(2000);
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$("#btnAdd").on("click", addCustomCNAME);
|
||||
|
||||
table = $("#customCNAMETable").DataTable({
|
||||
ajax: {
|
||||
url: "scripts/pi-hole/php/customcname.php",
|
||||
data: { action: "get", token: token },
|
||||
type: "POST"
|
||||
},
|
||||
columns: [{}, {}, { orderable: false, searchable: false }],
|
||||
columnDefs: [
|
||||
{
|
||||
targets: 2,
|
||||
render: function (data, type, row) {
|
||||
return (
|
||||
'<button type="button" class="btn btn-danger btn-xs deleteCustomCNAME" data-domain=\'' +
|
||||
row[0] +
|
||||
"' data-target='" +
|
||||
row[1] +
|
||||
"'>" +
|
||||
'<span class="far fa-trash-alt"></span>' +
|
||||
"</button>"
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
order: [[0, "asc"]],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("LocalCNAMETable", data);
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
return utils.stateLoadCallback("LocalCNAMETable");
|
||||
},
|
||||
drawCallback: function () {
|
||||
$(".deleteCustomCNAME").on("click", deleteCustomCNAME);
|
||||
}
|
||||
});
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
input.setAttribute("autocomplete", "off");
|
||||
input.setAttribute("autocorrect", "off");
|
||||
input.setAttribute("autocapitalize", "off");
|
||||
input.setAttribute("spellcheck", false);
|
||||
});
|
||||
|
||||
function addCustomCNAME() {
|
||||
var domain = utils.escapeHtml($("#domain").val());
|
||||
var target = utils.escapeHtml($("#target").val());
|
||||
|
||||
showAlert("info");
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/customcname.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "add", domain: domain, target: target, token: token },
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
showAlert("success");
|
||||
table.ajax.reload();
|
||||
} else showAlert("error", response.message);
|
||||
},
|
||||
error: function () {
|
||||
showAlert("error", "Error while adding this custom CNAME record");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteCustomCNAME() {
|
||||
var domain = $(this).attr("data-domain");
|
||||
var target = $(this).attr("data-target");
|
||||
|
||||
showAlert("info");
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/customcname.php",
|
||||
method: "post",
|
||||
dataType: "json",
|
||||
data: { action: "delete", domain: domain, target: target, token: token },
|
||||
success: function (response) {
|
||||
if (response.success) {
|
||||
showAlert("success");
|
||||
table.ajax.reload();
|
||||
} else showAlert("error", response.message);
|
||||
},
|
||||
error: function (jqXHR, exception) {
|
||||
showAlert("error", "Error while deleting this custom CNAME record");
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -65,6 +65,18 @@ $(function () {
|
||||
}
|
||||
}
|
||||
],
|
||||
lengthMenu: [
|
||||
[10, 25, 50, 100, -1],
|
||||
[10, 25, 50, 100, "All"]
|
||||
],
|
||||
order: [[0, "asc"]],
|
||||
stateSave: true,
|
||||
stateSaveCallback: function (settings, data) {
|
||||
utils.stateSaveCallback("LocalDNSTable", data);
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
return utils.stateLoadCallback("LocalDNSTable");
|
||||
},
|
||||
drawCallback: function () {
|
||||
$(".deleteCustomDNS").on("click", deleteCustomDNS);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ $(function () {
|
||||
{
|
||||
timePicker: true,
|
||||
timePickerIncrement: 15,
|
||||
timePicker24Hour: true,
|
||||
locale: { format: dateformat },
|
||||
startDate: start__,
|
||||
endDate: end__,
|
||||
|
||||
@@ -22,6 +22,7 @@ $(function () {
|
||||
{
|
||||
timePicker: true,
|
||||
timePickerIncrement: 15,
|
||||
timePicker24Hour: true,
|
||||
locale: { format: dateformat },
|
||||
startDate: start__,
|
||||
endDate: end__,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* This file is copyright under the latest version of the EUPL.
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global moment:false */
|
||||
/* global moment:false, utils:false */
|
||||
|
||||
var start__ = moment().subtract(6, "days");
|
||||
var from = moment(start__).utc().valueOf() / 1000;
|
||||
@@ -40,6 +40,7 @@ $(function () {
|
||||
{
|
||||
timePicker: true,
|
||||
timePickerIncrement: 15,
|
||||
timePicker24Hour: true,
|
||||
locale: { format: dateformat },
|
||||
startDate: start__,
|
||||
endDate: end__,
|
||||
@@ -72,70 +73,6 @@ $(function () {
|
||||
|
||||
var tableApi, statistics;
|
||||
|
||||
function add(domain, list) {
|
||||
var token = $("#token").text();
|
||||
var alInfo = $("#alInfo");
|
||||
var alList = $("#alList");
|
||||
var alDomain = $("#alDomain");
|
||||
alDomain.html(domain);
|
||||
var alSuccess = $("#alSuccess");
|
||||
var alFailure = $("#alFailure");
|
||||
var err = $("#err");
|
||||
|
||||
if (list === "white") {
|
||||
alList.html("Whitelist");
|
||||
} else {
|
||||
alList.html("Blacklist");
|
||||
}
|
||||
|
||||
alInfo.show();
|
||||
alSuccess.hide();
|
||||
alFailure.hide();
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
data: {
|
||||
domain: domain,
|
||||
list: list,
|
||||
token: token,
|
||||
action: "add_domain",
|
||||
comment: "Added from Long-Term-Data Query Log"
|
||||
},
|
||||
success: function (response) {
|
||||
if (!response.success) {
|
||||
alFailure.show();
|
||||
err.html(response.message);
|
||||
alFailure.delay(4000).fadeOut(2000, function () {
|
||||
alFailure.hide();
|
||||
});
|
||||
} else {
|
||||
alSuccess.show();
|
||||
alSuccess.delay(1000).fadeOut(2000, function () {
|
||||
alSuccess.hide();
|
||||
});
|
||||
}
|
||||
|
||||
alInfo.delay(1000).fadeOut(2000, function () {
|
||||
alInfo.hide();
|
||||
alList.html("");
|
||||
alDomain.html("");
|
||||
});
|
||||
},
|
||||
error: function () {
|
||||
alFailure.show();
|
||||
err.html("");
|
||||
alFailure.delay(1000).fadeOut(2000, function () {
|
||||
alFailure.hide();
|
||||
});
|
||||
alInfo.delay(1000).fadeOut(2000, function () {
|
||||
alInfo.hide();
|
||||
alList.html("");
|
||||
alDomain.html("");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleAjaxError(xhr, textStatus) {
|
||||
if (textStatus === "timeout") {
|
||||
alert("The server took too long to send the data.");
|
||||
@@ -194,6 +131,12 @@ function getQueryTypes() {
|
||||
queryType.push(11);
|
||||
}
|
||||
|
||||
if ($("#type_retried").prop("checked")) {
|
||||
// Multiple IDs correspond to this status
|
||||
// We request queries with all of them
|
||||
queryType.push([12, 13]);
|
||||
}
|
||||
|
||||
return queryType.join(",");
|
||||
}
|
||||
|
||||
@@ -238,13 +181,9 @@ function refreshTableData() {
|
||||
}
|
||||
|
||||
$(function () {
|
||||
var APIstring;
|
||||
|
||||
if (instantquery) {
|
||||
APIstring = "api_db.php?getAllQueries&from=" + from + "&until=" + until;
|
||||
} else {
|
||||
APIstring = "api_db.php?getAllQueries=empty";
|
||||
}
|
||||
var APIstring = instantquery
|
||||
? "api_db.php?getAllQueries&from=" + from + "&until=" + until
|
||||
: "api_db.php?getAllQueries=empty";
|
||||
|
||||
// Check if query type filtering is enabled
|
||||
var queryType = getQueryTypes();
|
||||
@@ -265,7 +204,10 @@ $(function () {
|
||||
break;
|
||||
case 2:
|
||||
color = "green";
|
||||
fieldtext = "OK <br class='hidden-lg'>(forwarded)";
|
||||
fieldtext =
|
||||
"OK <br class='hidden-lg'>(forwarded to " +
|
||||
(data.length > 5 && data[5] !== "N/A" ? data[5] : "") +
|
||||
")";
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
|
||||
break;
|
||||
@@ -320,6 +262,16 @@ $(function () {
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
break;
|
||||
case 12:
|
||||
color = "green";
|
||||
fieldtext = "Retried";
|
||||
buttontext = "";
|
||||
break;
|
||||
case 13:
|
||||
color = "green";
|
||||
fieldtext = "Retried <br class='hidden-lg'>(ignored)";
|
||||
buttontext = "";
|
||||
break;
|
||||
default:
|
||||
color = "black";
|
||||
fieldtext = "Unknown";
|
||||
@@ -392,10 +344,10 @@ $(function () {
|
||||
});
|
||||
$("#all-queries tbody").on("click", "button", function () {
|
||||
var data = tableApi.row($(this).parents("tr")).data();
|
||||
if (data[4] === 1 || data[4] === 4 || data[5] === 5) {
|
||||
add(data[2], "white");
|
||||
if ([1, 4, 5, 9, 10, 11].indexOf(data[4]) !== -1) {
|
||||
utils.addFromQueryLog(data[2], "white");
|
||||
} else {
|
||||
add(data[2], "black");
|
||||
utils.addFromQueryLog(data[2], "black");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -96,12 +96,10 @@ function piholeChange(action, duration) {
|
||||
function checkMessages() {
|
||||
$.getJSON("api_db.php?status", function (data) {
|
||||
if ("message_count" in data && data.message_count > 0) {
|
||||
var title;
|
||||
if (data.message_count > 1) {
|
||||
title = "There are " + data.message_count + " warnings. Click for further details.";
|
||||
} else {
|
||||
title = "There is one warning. Click for further details.";
|
||||
}
|
||||
var title =
|
||||
data.message_count > 1
|
||||
? "There are " + data.message_count + " warnings. Click for further details."
|
||||
: "There is one warning. Click for further details.";
|
||||
|
||||
$("#pihole-diagnosis").prop("title", title);
|
||||
$("#pihole-diagnosis-count").text(data.message_count);
|
||||
@@ -133,7 +131,8 @@ function initCheckboxRadioStyle() {
|
||||
|
||||
function applyCheckboxRadioStyle(style) {
|
||||
boxsheet.attr("href", getCheckboxURL(style));
|
||||
var sel = $("input[type='radio'],input[type='checkbox']");
|
||||
// Get all radio/checkboxes for theming, with the exception of the two radio buttons on the custom disable timer
|
||||
var sel = $("input[type='radio'],input[type='checkbox']").not("#selSec").not("#selMin");
|
||||
sel.parent().removeClass();
|
||||
sel.parent().addClass("icheck-" + style);
|
||||
}
|
||||
@@ -170,7 +169,7 @@ function initCPUtemp() {
|
||||
switch (unit) {
|
||||
case "K":
|
||||
temperature += 273.15;
|
||||
displaytemp.html(temperature.toFixed(1) + " °K");
|
||||
displaytemp.html(temperature.toFixed(1) + " K");
|
||||
break;
|
||||
|
||||
case "F":
|
||||
|
||||
@@ -56,8 +56,10 @@ function initTable() {
|
||||
var tooltip =
|
||||
"Added: " +
|
||||
utils.datetime(data.date_added, false) +
|
||||
"\nLast modified: " +
|
||||
"\nLast modified (database entry): " +
|
||||
utils.datetime(data.date_modified, false) +
|
||||
"\nLast updated (list content): " +
|
||||
(data.date_updated !== null ? utils.datetime(data.date_updated, false) : "N/A") +
|
||||
"\nDatabase ID: " +
|
||||
data.id;
|
||||
$("td:eq(0)", row).html(
|
||||
@@ -86,7 +88,7 @@ function initTable() {
|
||||
|
||||
$("td:eq(2)", row).html('<input id="comment_' + data.id + '" class="form-control">');
|
||||
var commentEl = $("#comment_" + data.id, row);
|
||||
commentEl.val(data.comment);
|
||||
commentEl.val(utils.unescapeHtml(data.comment));
|
||||
commentEl.on("change", editAdlist);
|
||||
|
||||
$("td:eq(3)", row).empty();
|
||||
@@ -176,6 +178,7 @@ function initTable() {
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("groups-adlists-table");
|
||||
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
@@ -219,6 +222,8 @@ function addAdlist() {
|
||||
utils.showAlert("info", "", "Adding adlist...", address);
|
||||
|
||||
if (address.length === 0) {
|
||||
// enable the ui elements again
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Please specify an adlist address");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -17,28 +17,35 @@ function reloadClientSuggestions() {
|
||||
{ action: "get_unconfigured_clients", token: token },
|
||||
function (data) {
|
||||
var sel = $("#select");
|
||||
var customWasSelected = sel.val() === "custom";
|
||||
sel.empty();
|
||||
|
||||
// In order for the placeholder value to appear, we have to have a blank
|
||||
// <option> as the first option in our <select> control. This is because
|
||||
// the browser tries to select the first option by default. If our first
|
||||
// option were non-empty, the browser would display this instead of the
|
||||
// placeholder.
|
||||
sel.append($("<option />"));
|
||||
|
||||
// Add data obtained from API
|
||||
for (var key in data) {
|
||||
if (!Object.prototype.hasOwnProperty.call(data, key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var text = key;
|
||||
var keyPlain = key;
|
||||
if (key.startsWith("IP-")) {
|
||||
// Mock MAC address for address-only devices
|
||||
keyPlain = key.substring(3);
|
||||
text = keyPlain;
|
||||
}
|
||||
|
||||
// Append host name if available
|
||||
if (data[key].length > 0) {
|
||||
text += " (" + data[key] + ")";
|
||||
}
|
||||
|
||||
sel.append($("<option />").val(key).text(text));
|
||||
}
|
||||
|
||||
if (data.length === 0) {
|
||||
$("#ip-custom").prop("disabled", false);
|
||||
}
|
||||
|
||||
sel.append($("<option />").val("custom").text("Custom, specified below..."));
|
||||
if (customWasSelected) {
|
||||
sel.val("custom");
|
||||
sel.append($("<option />").val(keyPlain).text(text));
|
||||
}
|
||||
},
|
||||
"json"
|
||||
@@ -59,6 +66,11 @@ function getGroups() {
|
||||
|
||||
$(function () {
|
||||
$("#btnAdd").on("click", addClient);
|
||||
$("select").select2({
|
||||
tags: true,
|
||||
placeholder: "Select client...",
|
||||
allowClear: true
|
||||
});
|
||||
|
||||
reloadClientSuggestions();
|
||||
utils.setBsSelectDefaults();
|
||||
@@ -120,7 +132,7 @@ function initTable() {
|
||||
|
||||
$("td:eq(1)", row).html('<input id="comment_' + data.id + '" class="form-control">');
|
||||
var commentEl = $("#comment_" + data.id, row);
|
||||
commentEl.val(data.comment);
|
||||
commentEl.val(utils.unescapeHtml(data.comment));
|
||||
commentEl.on("change", editClient);
|
||||
|
||||
$("td:eq(2)", row).empty();
|
||||
@@ -210,6 +222,7 @@ function initTable() {
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("groups-clients-table");
|
||||
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
@@ -221,6 +234,7 @@ function initTable() {
|
||||
return data;
|
||||
}
|
||||
});
|
||||
|
||||
// Disable autocorrect in the search box
|
||||
var input = document.querySelector("input[type=search]");
|
||||
if (input !== null) {
|
||||
@@ -238,6 +252,7 @@ function initTable() {
|
||||
$("#resetButton").addClass("hidden");
|
||||
}
|
||||
});
|
||||
|
||||
$("#resetButton").on("click", function () {
|
||||
table.order([[0, "asc"]]).draw();
|
||||
$("#resetButton").addClass("hidden");
|
||||
@@ -245,33 +260,34 @@ function initTable() {
|
||||
}
|
||||
|
||||
function addClient() {
|
||||
var ip = $("#select").val();
|
||||
var ip = $("#select").val().trim();
|
||||
var comment = utils.escapeHtml($("#new_comment").val());
|
||||
if (ip === "custom") {
|
||||
ip = utils.escapeHtml($("#ip-custom").val().trim());
|
||||
}
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Adding client...", ip);
|
||||
|
||||
if (ip.length === 0) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a client IP address");
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a client IP or MAC address");
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate IP address (may contain CIDR details)
|
||||
var ipv6format = ip.includes(":");
|
||||
|
||||
if (!ipv6format && !utils.validateIPv4CIDR(ip)) {
|
||||
// Validate input, can be:
|
||||
// - IPv4 address (with and without CIDR)
|
||||
// - IPv6 address (with and without CIDR)
|
||||
// - MAC address (in the form AA:BB:CC:DD:EE:FF)
|
||||
// - host name (arbitrary form, we're only checking against some reserved charaters)
|
||||
if (utils.validateIPv4CIDR(ip) || utils.validateIPv6CIDR(ip) || utils.validateMAC(ip)) {
|
||||
// Convert input to upper case (important for MAC addresses)
|
||||
ip = ip.toUpperCase();
|
||||
} else if (!utils.validateHostname(ip)) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Invalid IPv4 address!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ipv6format && !utils.validateIPv6CIDR(ip)) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Invalid IPv6 address!");
|
||||
utils.showAlert(
|
||||
"warning",
|
||||
"",
|
||||
"Warning",
|
||||
"Input is neither a valid IP or MAC address nor a valid host name!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ function initTable() {
|
||||
{ data: "type", searchable: false },
|
||||
{ data: "enabled", searchable: false },
|
||||
{ data: "comment" },
|
||||
{ data: "groups", searchable: false },
|
||||
{ data: "groups", searchable: false, visible: showtype === "all" },
|
||||
{ data: null, width: "80px", orderable: false }
|
||||
],
|
||||
drawCallback: function () {
|
||||
@@ -149,7 +149,7 @@ function initTable() {
|
||||
|
||||
$("td:eq(3)", row).html('<input id="comment_' + data.id + '" class="form-control">');
|
||||
var commentEl = $("#comment_" + data.id, row);
|
||||
commentEl.val(data.comment);
|
||||
commentEl.val(utils.unescapeHtml(data.comment));
|
||||
commentEl.on("change", editDomain);
|
||||
|
||||
// Show group assignment field only if in full domain management mode
|
||||
@@ -251,6 +251,7 @@ function initTable() {
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("groups-domains-table");
|
||||
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
|
||||
@@ -43,7 +43,7 @@ $(function () {
|
||||
'<input id="name_' + data.id + '" title="' + tooltip + '" class="form-control">'
|
||||
);
|
||||
var nameEl = $("#name_" + data.id, row);
|
||||
nameEl.val(data.name);
|
||||
nameEl.val(utils.unescapeHtml(data.name));
|
||||
nameEl.on("change", editGroup);
|
||||
|
||||
var disabled = data.enabled === 0;
|
||||
@@ -63,7 +63,7 @@ $(function () {
|
||||
$("td:eq(2)", row).html('<input id="desc_' + data.id + '" class="form-control">');
|
||||
var desc = data.description !== null ? data.description : "";
|
||||
var descEl = $("#desc_" + data.id, row);
|
||||
descEl.val(desc);
|
||||
descEl.val(utils.unescapeHtml(desc));
|
||||
descEl.on("change", editGroup);
|
||||
|
||||
$("td:eq(3)", row).empty();
|
||||
@@ -91,6 +91,7 @@ $(function () {
|
||||
},
|
||||
stateLoadCallback: function () {
|
||||
var data = utils.stateLoadCallback("groups-table");
|
||||
|
||||
// Return if not available
|
||||
if (data === null) {
|
||||
return null;
|
||||
@@ -134,6 +135,8 @@ function addGroup() {
|
||||
utils.showAlert("info", "", "Adding group...", name);
|
||||
|
||||
if (name.length === 0) {
|
||||
// enable the ui elements again
|
||||
utils.enableAll();
|
||||
utils.showAlert("warning", "", "Warning", "Please specify a group name");
|
||||
return;
|
||||
}
|
||||
|
||||
+26
-17
@@ -251,15 +251,11 @@ function updateQueriesOverTime() {
|
||||
// Add data for each hour that is available
|
||||
for (var hour in data.ads_over_time[0]) {
|
||||
if (Object.prototype.hasOwnProperty.call(data.ads_over_time[0], hour)) {
|
||||
var d, h;
|
||||
h = parseInt(data.domains_over_time[0][hour], 10);
|
||||
if (parseInt(data.ads_over_time[0][0], 10) < 1200) {
|
||||
// Fallback - old style
|
||||
d = new Date().setHours(Math.floor(h / 6), 10 * (h % 6), 0, 0);
|
||||
} else {
|
||||
// New style: Get Unix timestamps
|
||||
d = new Date(1000 * h);
|
||||
}
|
||||
var h = parseInt(data.domains_over_time[0][hour], 10);
|
||||
var d =
|
||||
parseInt(data.ads_over_time[0][0], 10) < 1200
|
||||
? new Date().setHours(Math.floor(h / 6), 10 * (h % 6), 0, 0)
|
||||
: new Date(1000 * h);
|
||||
|
||||
timeLineChart.data.labels.push(d);
|
||||
var blocked = data.ads_over_time[1][hour];
|
||||
@@ -297,14 +293,9 @@ function updateQueryTypesPie() {
|
||||
var v = [],
|
||||
c = [],
|
||||
k = [],
|
||||
i = 0,
|
||||
iter;
|
||||
i = 0;
|
||||
// Collect values and colors, and labels
|
||||
if (Object.prototype.hasOwnProperty.call(data, "querytypes")) {
|
||||
iter = data.querytypes;
|
||||
} else {
|
||||
iter = data;
|
||||
}
|
||||
var iter = Object.prototype.hasOwnProperty.call(data, "querytypes") ? data.querytypes : data;
|
||||
|
||||
querytypeids = [];
|
||||
Object.keys(iter).forEach(function (key) {
|
||||
@@ -592,7 +583,7 @@ function updateTopClientsChart() {
|
||||
url =
|
||||
'<a href="queries.php?client=' +
|
||||
clientip +
|
||||
'" title="' +
|
||||
'&type=blocked" title="' +
|
||||
clientip +
|
||||
'">' +
|
||||
clientname +
|
||||
@@ -1001,6 +992,24 @@ $(function () {
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#clientsChart").click(function (evt) {
|
||||
var activePoints = clientsChart.getElementAtEvent(evt);
|
||||
if (activePoints.length > 0) {
|
||||
//get the internal index of slice in pie chart
|
||||
var clickedElementindex = activePoints[0]._index;
|
||||
|
||||
//get specific label by index
|
||||
var label = clientsChart.data.labels[clickedElementindex];
|
||||
|
||||
//get value by index
|
||||
var from = label / 1000 - 300;
|
||||
var until = label / 1000 + 300;
|
||||
window.location.href = "queries.php?from=" + from + "&until=" + until;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (document.getElementById("queryTypePieChart")) {
|
||||
ctx = document.getElementById("queryTypePieChart").getContext("2d");
|
||||
queryTypePieChart = new Chart(ctx, {
|
||||
|
||||
@@ -5,12 +5,17 @@
|
||||
* 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 adapted from
|
||||
// This code has been taken from
|
||||
// https://datatables.net/plug-ins/sorting/ip-address
|
||||
// and was modified by the Pi-hole team to support
|
||||
// CIDR notation and be more robust against invalid
|
||||
// input data (like empty IP addresses)
|
||||
$.extend($.fn.dataTableExt.oSort, {
|
||||
"ip-address-pre": function (a) {
|
||||
if (!a) {
|
||||
return 0;
|
||||
// Skip empty fields (IP address might have expired or
|
||||
// reassigned to a differenct device)
|
||||
if (!a || a.length === 0) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
var i, item;
|
||||
|
||||
@@ -73,6 +73,9 @@ function renderMessage(data, type, row) {
|
||||
"↑</pre>"
|
||||
);
|
||||
|
||||
case "DNSMASQ_CONFIG":
|
||||
return "FTL failed to start due to " + row.message;
|
||||
|
||||
default:
|
||||
return "Unknown message type<pre>" + JSON.stringify(row) + "</pre>";
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ $(function () {
|
||||
tableApi = $("#network-entries").DataTable({
|
||||
rowCallback: function (row, data) {
|
||||
var color;
|
||||
var index;
|
||||
var maxiter;
|
||||
var iconClasses;
|
||||
var lastQuery = parseInt(data.lastQuery, 10);
|
||||
var diff = getTimestamp() - lastQuery;
|
||||
@@ -93,7 +95,7 @@ $(function () {
|
||||
} else {
|
||||
// This client has never sent a query to Pi-hole, color light-red
|
||||
color = networkNever;
|
||||
iconClasses = "fas fa-check";
|
||||
iconClasses = "fas fa-times";
|
||||
}
|
||||
|
||||
// Set determined background color
|
||||
@@ -109,14 +111,47 @@ $(function () {
|
||||
// Set hostname to "unknown" if not available
|
||||
if (!data.name || data.name.length === 0) {
|
||||
$("td:eq(3)", row).html("<em>unknown</em>");
|
||||
} else {
|
||||
var names = [];
|
||||
var name = "";
|
||||
maxiter = Math.min(data.name.length, MAXIPDISPLAY);
|
||||
index = 0;
|
||||
for (index = 0; index < maxiter; index++) {
|
||||
name = data.name[index];
|
||||
if (name.length === 0) continue;
|
||||
names.push('<a href="queries.php?client=' + name + '">' + name + "</a>");
|
||||
}
|
||||
|
||||
if (data.name.length > MAXIPDISPLAY) {
|
||||
// We hit the maximum above, add "..." to symbolize we would
|
||||
// have more to show here
|
||||
names.push("...");
|
||||
}
|
||||
|
||||
maxiter = Math.min(data.ip.length, data.name.length);
|
||||
var allnames = [];
|
||||
for (index = 0; index < maxiter; index++) {
|
||||
name = data.name[index];
|
||||
if (name.length > 0) {
|
||||
allnames.push(name + " (" + data.ip[index] + ")");
|
||||
} else {
|
||||
allnames.push("No host name for " + data.ip[index] + " known");
|
||||
}
|
||||
}
|
||||
|
||||
$("td:eq(3)", row).html(names.join("<br>"));
|
||||
$("td:eq(3)", row).hover(function () {
|
||||
this.title = allnames.join("\n");
|
||||
});
|
||||
}
|
||||
|
||||
// Set number of queries to localized string (add thousand separators)
|
||||
$("td:eq(6)", row).html(data.numQueries.toLocaleString());
|
||||
|
||||
var ips = [];
|
||||
var maxiter = Math.min(data.ip.length, MAXIPDISPLAY);
|
||||
for (var index = 0; index < maxiter; index++) {
|
||||
maxiter = Math.min(data.ip.length, MAXIPDISPLAY);
|
||||
index = 0;
|
||||
for (index = 0; index < maxiter; index++) {
|
||||
var ip = data.ip[index];
|
||||
ips.push('<a href="queries.php?client=' + ip + '">' + ip + "</a>");
|
||||
}
|
||||
|
||||
@@ -25,86 +25,6 @@ var replyTypes = [
|
||||
];
|
||||
var colTypes = ["time", "query type", "domain", "client", "status", "reply type"];
|
||||
|
||||
function add(domain, list) {
|
||||
var token = $("#token").text();
|
||||
var alertModal = $("#alertModal");
|
||||
var alProcessing = alertModal.find(".alProcessing");
|
||||
var alSuccess = alertModal.find(".alSuccess");
|
||||
var alFailure = alertModal.find(".alFailure");
|
||||
var alNetworkErr = alertModal.find(".alFailure #alNetErr");
|
||||
var alCustomErr = alertModal.find(".alFailure #alCustomErr");
|
||||
var alList = "#alList";
|
||||
var alDomain = "#alDomain";
|
||||
|
||||
// Exit the function here if the Modal is already shown (multiple running interlock)
|
||||
if (alertModal.css("display") !== "none") {
|
||||
return;
|
||||
}
|
||||
|
||||
var listtype;
|
||||
if (list === "white") {
|
||||
listtype = "Whitelist";
|
||||
} else {
|
||||
listtype = "Blacklist";
|
||||
}
|
||||
|
||||
alProcessing.children(alDomain).html(domain);
|
||||
alProcessing.children(alList).html(listtype);
|
||||
alertModal.modal("show");
|
||||
|
||||
// add Domain to List after Modal has faded in
|
||||
alertModal.one("shown.bs.modal", function () {
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
data: {
|
||||
domain: domain,
|
||||
list: list,
|
||||
token: token,
|
||||
action: "add_domain",
|
||||
comment: "Added from Query Log"
|
||||
},
|
||||
success: function (response) {
|
||||
alProcessing.hide();
|
||||
if (!response.success) {
|
||||
// Failure
|
||||
alNetworkErr.hide();
|
||||
alCustomErr.html(response.message);
|
||||
alFailure.fadeIn(1000);
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 3000);
|
||||
} else {
|
||||
// Success
|
||||
alSuccess.children(alDomain).html(domain);
|
||||
alSuccess.children(alList).html(listtype);
|
||||
alSuccess.fadeIn(1000);
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
// Network Error
|
||||
alProcessing.hide();
|
||||
alNetworkErr.show();
|
||||
alFailure.fadeIn(1000);
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 3000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Reset Modal after it has faded out
|
||||
alertModal.one("hidden.bs.modal", function () {
|
||||
alProcessing.show();
|
||||
alSuccess.add(alFailure).hide();
|
||||
alProcessing.add(alSuccess).children(alDomain).html("").end().children(alList).html("");
|
||||
alCustomErr.html("");
|
||||
});
|
||||
}
|
||||
|
||||
function handleAjaxError(xhr, textStatus) {
|
||||
if (textStatus === "timeout") {
|
||||
alert("The server took too long to send the data.");
|
||||
@@ -148,6 +68,10 @@ $(function () {
|
||||
APIstring += "=100";
|
||||
}
|
||||
|
||||
if ("type" in GETDict) {
|
||||
APIstring += "&type=" + GETDict.type;
|
||||
}
|
||||
|
||||
tableApi = $("#all-queries").DataTable({
|
||||
rowCallback: function (row, data) {
|
||||
// DNSSEC status
|
||||
@@ -189,7 +113,11 @@ $(function () {
|
||||
break;
|
||||
case "2":
|
||||
colorClass = "text-green";
|
||||
fieldtext = "OK <br class='hidden-lg'>(forwarded)" + dnssecStatus;
|
||||
fieldtext =
|
||||
"OK <br class='hidden-lg'>(forwarded to " +
|
||||
(data.length > 10 && data[10] !== "N/A" ? data[10] : "") +
|
||||
")" +
|
||||
dnssecStatus;
|
||||
buttontext =
|
||||
'<button type="button" class="btn btn-default btn-sm text-red"><i class="fa fa-ban"></i> Blacklist</button>';
|
||||
break;
|
||||
@@ -257,6 +185,16 @@ $(function () {
|
||||
'<button type="button" class="btn btn-default btn-sm text-green"><i class="fas fa-check"></i> Whitelist</button>';
|
||||
isCNAME = true;
|
||||
break;
|
||||
case 12:
|
||||
colorClass = "text-green";
|
||||
fieldtext = "Retried";
|
||||
buttontext = "";
|
||||
break;
|
||||
case 13:
|
||||
colorClass = "text-green";
|
||||
fieldtext = "Retried <br class='hidden-lg'>(ignored)";
|
||||
buttontext = "";
|
||||
break;
|
||||
default:
|
||||
colorClass = false;
|
||||
fieldtext = "Unknown (" + parseInt(data[4], 10) + ")";
|
||||
@@ -307,14 +245,9 @@ $(function () {
|
||||
}
|
||||
|
||||
// Check for existence of sixth column and display only if not Pi-holed
|
||||
var replytext,
|
||||
replyid = data[5];
|
||||
|
||||
if (replyid >= 0 && replyid < replyTypes.length) {
|
||||
replytext = replyTypes[replyid];
|
||||
} else {
|
||||
replytext = "? (" + replyid + ")";
|
||||
}
|
||||
var replyid = data[5];
|
||||
var replytext =
|
||||
replyid >= 0 && replyid < replyTypes.length ? replyTypes[replyid] : "? (" + replyid + ")";
|
||||
|
||||
replytext += '<input type="hidden" name="id" value="' + replyid + '">';
|
||||
|
||||
@@ -485,9 +418,9 @@ $(function () {
|
||||
$("#all-queries tbody").on("click", "button", function () {
|
||||
var data = tableApi.row($(this).parents("tr")).data();
|
||||
if (data[4] === "2" || data[4] === "3") {
|
||||
add(data[2], "black");
|
||||
utils.addFromQueryLog(data[2], "black");
|
||||
} else {
|
||||
add(data[2], "white");
|
||||
utils.addFromQueryLog(data[2], "white");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -504,19 +437,19 @@ function tooltipText(index, text) {
|
||||
}
|
||||
|
||||
if (index in tableFilters && tableFilters[index].length > 0) {
|
||||
return "Clear filter on " + colTypes[index] + ' "' + text + '" using Shift + Click.';
|
||||
return "Click to remove " + colTypes[index] + ' "' + text + '" from filter.';
|
||||
}
|
||||
|
||||
return "Add filter on " + colTypes[index] + ' "' + text + '" using Ctrl + Click.';
|
||||
return "Click to add " + colTypes[index] + ' "' + text + '" to filter.';
|
||||
}
|
||||
|
||||
function addColumnFilter(event, colID, filterstring) {
|
||||
// Don't do anything when NOT explicitly requesting multi-selection functions
|
||||
if (!event.ctrlKey && !event.metaKey && !event.shiftKey) {
|
||||
// If the below modifier keys are held down, do nothing
|
||||
if (event.ctrlKey || event.metaKey || event.altKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.shiftKey) {
|
||||
if (tableFilters[colID] === filterstring) {
|
||||
filterstring = "";
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ function quietfilter(ta, data) {
|
||||
|
||||
function eventsource() {
|
||||
var ta = $("#output");
|
||||
var domain = $("#domain").val().trim();
|
||||
// process with the current visible domain input field
|
||||
var domain = $("input[id^='domain']:visible").val().trim();
|
||||
var q = $("#quiet");
|
||||
|
||||
if (domain.length === 0) {
|
||||
@@ -38,7 +39,7 @@ function eventsource() {
|
||||
if (typeof EventSource !== "function") {
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: "scripts/pi-hole/php/queryads.php?domain=" + domain.toLowerCase() + exact + "&IE",
|
||||
url: "scripts/pi-hole/php/queryads.php?domain=" + domain.toLowerCase() + "&" + exact + "&IE",
|
||||
async: false
|
||||
}).done(function (data) {
|
||||
ta.show();
|
||||
@@ -86,41 +87,22 @@ function eventsource() {
|
||||
exact = "";
|
||||
}
|
||||
|
||||
// Handle enter button
|
||||
$(document).keypress(function (e) {
|
||||
if (e.which === 13 && $("#domain").is(":focus")) {
|
||||
// Handle enter key
|
||||
$("#domain_1, #domain_2").keypress(function (e) {
|
||||
if (e.which === 13) {
|
||||
// Enter was pressed, and the input has focus
|
||||
exact = "";
|
||||
eventsource();
|
||||
}
|
||||
});
|
||||
// Handle button
|
||||
$("#btnSearch").on("click", function () {
|
||||
exact = "";
|
||||
eventsource();
|
||||
});
|
||||
// Handle exact button
|
||||
$("#btnSearchExact").on("click", function () {
|
||||
exact = "exact";
|
||||
eventsource();
|
||||
});
|
||||
|
||||
// Wrap form-group's buttons to next line when viewed on a small screen
|
||||
$(window).on("resize", function () {
|
||||
if ($(window).width() < 992) {
|
||||
$(".form-group.input-group").removeClass("input-group").addClass("input-group-block");
|
||||
$(".form-group.input-group-block > input").css("margin-bottom", "5px");
|
||||
$(".form-group.input-group-block > .input-group-btn")
|
||||
.removeClass("input-group-btn")
|
||||
.addClass("btn-block text-center");
|
||||
} else {
|
||||
$(".form-group.input-group-block").removeClass("input-group-block").addClass("input-group");
|
||||
$(".form-group.input-group > input").css("margin-bottom", "");
|
||||
$(".form-group.input-group > .btn-block.text-center")
|
||||
.removeClass("btn-block text-center")
|
||||
.addClass("input-group-btn");
|
||||
// Handle search buttons
|
||||
$("button[id^='btnSearch']").on("click", function () {
|
||||
exact = "";
|
||||
|
||||
if (this.id.match("^btnSearchExact")) {
|
||||
exact = "exact";
|
||||
}
|
||||
});
|
||||
$(function () {
|
||||
$(window).trigger("resize");
|
||||
|
||||
eventsource();
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Please see LICENSE file for your rights under this license. */
|
||||
|
||||
/* global utils:false */
|
||||
var token = $("#token").text();
|
||||
|
||||
$(function () {
|
||||
$("[data-static]").on("click", function () {
|
||||
@@ -283,3 +284,48 @@ $(function () {
|
||||
localStorage.setItem("barchart_chkbox", bargraphs.prop("checked"));
|
||||
});
|
||||
});
|
||||
|
||||
// Delete dynamic DHCP lease
|
||||
$('button[id="removedynamic"]').on("click", function () {
|
||||
var tr = $(this).closest("tr");
|
||||
var ipaddr = utils.escapeHtml(tr.children("#IP").text());
|
||||
var name = utils.escapeHtml(tr.children("#HOST").text());
|
||||
var ipname = name + " (" + ipaddr + ")";
|
||||
|
||||
utils.disableAll();
|
||||
utils.showAlert("info", "", "Deleting DHCP lease...", ipname);
|
||||
$.ajax({
|
||||
url: "api.php",
|
||||
method: "get",
|
||||
dataType: "json",
|
||||
data: {
|
||||
delete_lease: ipaddr,
|
||||
token: token
|
||||
},
|
||||
success: function (response) {
|
||||
utils.enableAll();
|
||||
if (response.delete_lease.startsWith("OK")) {
|
||||
utils.showAlert(
|
||||
"success",
|
||||
"far fa-trash-alt",
|
||||
"Successfully deleted DHCP lease for ",
|
||||
ipname
|
||||
);
|
||||
// Remove column on success
|
||||
tr.remove();
|
||||
// We have to hide the tooltips explicitly or they will stay there forever as
|
||||
// the onmouseout event does not fire when the element is already gone
|
||||
$.each($(".tooltip"), function () {
|
||||
$(this).remove();
|
||||
});
|
||||
} else {
|
||||
utils.showAlert("error", "Error while deleting DHCP lease for " + ipname, response);
|
||||
}
|
||||
},
|
||||
error: function (jqXHR, exception) {
|
||||
utils.enableAll();
|
||||
utils.showAlert("error", "Error while deleting DHCP lease for " + ipname, jqXHR.responseText);
|
||||
console.log(exception); // eslint-disable-line no-console
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+108
-1
@@ -17,11 +17,29 @@ function escapeHtml(text) {
|
||||
"'": "'"
|
||||
};
|
||||
|
||||
if (text === null) return null;
|
||||
|
||||
return text.replace(/[&<>"']/g, function (m) {
|
||||
return map[m];
|
||||
});
|
||||
}
|
||||
|
||||
function unescapeHtml(text) {
|
||||
var map = {
|
||||
"&": "&",
|
||||
"<": "<",
|
||||
">": ">",
|
||||
""": '"',
|
||||
"'": "'"
|
||||
};
|
||||
|
||||
if (text === null) return null;
|
||||
|
||||
return text.replace(/&(?:amp|lt|gt|quot|#039);/g, function (m) {
|
||||
return map[m];
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function for converting Objects to Arrays after sorting the keys
|
||||
function objectToArray(obj) {
|
||||
var arr = [];
|
||||
@@ -167,6 +185,16 @@ function validateIPv6CIDR(ip) {
|
||||
return ipv6validator.test(ip);
|
||||
}
|
||||
|
||||
function validateMAC(mac) {
|
||||
var macvalidator = new RegExp(/^([\da-fA-F]{2}:){5}([\da-fA-F]{2})$/);
|
||||
return macvalidator.test(mac);
|
||||
}
|
||||
|
||||
function validateHostname(name) {
|
||||
var namevalidator = new RegExp(/[^<>;"]/);
|
||||
return namevalidator.test(name);
|
||||
}
|
||||
|
||||
// set bootstrap-select defaults
|
||||
function setBsSelectDefaults() {
|
||||
var bsSelectDefaults = $.fn.selectpicker.Constructor.DEFAULTS;
|
||||
@@ -220,9 +248,85 @@ function getGraphType() {
|
||||
return localStorage.getItem("barchart_chkbox") === "false" ? "line" : "bar";
|
||||
}
|
||||
|
||||
function addFromQueryLog(domain, list) {
|
||||
var token = $("#token").text();
|
||||
var alertModal = $("#alertModal");
|
||||
var alProcessing = alertModal.find(".alProcessing");
|
||||
var alSuccess = alertModal.find(".alSuccess");
|
||||
var alFailure = alertModal.find(".alFailure");
|
||||
var alNetworkErr = alertModal.find(".alFailure #alNetErr");
|
||||
var alCustomErr = alertModal.find(".alFailure #alCustomErr");
|
||||
var alList = "#alList";
|
||||
var alDomain = "#alDomain";
|
||||
|
||||
// Exit the function here if the Modal is already shown (multiple running interlock)
|
||||
if (alertModal.css("display") !== "none") {
|
||||
return;
|
||||
}
|
||||
|
||||
var listtype = list === "white" ? "Whitelist" : "Blacklist";
|
||||
|
||||
alProcessing.children(alDomain).html(domain);
|
||||
alProcessing.children(alList).html(listtype);
|
||||
alertModal.modal("show");
|
||||
|
||||
// add Domain to List after Modal has faded in
|
||||
alertModal.one("shown.bs.modal", function () {
|
||||
$.ajax({
|
||||
url: "scripts/pi-hole/php/groups.php",
|
||||
method: "post",
|
||||
data: {
|
||||
domain: domain,
|
||||
list: list,
|
||||
token: token,
|
||||
action: "replace_domain",
|
||||
comment: "Added from Query Log"
|
||||
},
|
||||
success: function (response) {
|
||||
alProcessing.hide();
|
||||
if (!response.success) {
|
||||
// Failure
|
||||
alNetworkErr.hide();
|
||||
alCustomErr.html(response.message);
|
||||
alFailure.fadeIn(1000);
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 10000);
|
||||
} else {
|
||||
// Success
|
||||
alSuccess.children(alDomain).html(domain);
|
||||
alSuccess.children(alList).html(listtype);
|
||||
alSuccess.fadeIn(1000);
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
// Network Error
|
||||
alProcessing.hide();
|
||||
alNetworkErr.show();
|
||||
alFailure.fadeIn(1000);
|
||||
setTimeout(function () {
|
||||
alertModal.modal("hide");
|
||||
}, 8000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Reset Modal after it has faded out
|
||||
alertModal.one("hidden.bs.modal", function () {
|
||||
alProcessing.show();
|
||||
alSuccess.add(alFailure).hide();
|
||||
alProcessing.add(alSuccess).children(alDomain).html("").end().children(alList).html("");
|
||||
alCustomErr.html("");
|
||||
});
|
||||
}
|
||||
|
||||
window.utils = (function () {
|
||||
return {
|
||||
escapeHtml: escapeHtml,
|
||||
unescapeHtml: unescapeHtml,
|
||||
objectToArray: objectToArray,
|
||||
padNumber: padNumber,
|
||||
showAlert: showAlert,
|
||||
@@ -234,6 +338,9 @@ window.utils = (function () {
|
||||
setBsSelectDefaults: setBsSelectDefaults,
|
||||
stateSaveCallback: stateSaveCallback,
|
||||
stateLoadCallback: stateLoadCallback,
|
||||
getGraphType: getGraphType
|
||||
getGraphType: getGraphType,
|
||||
validateMAC: validateMAC,
|
||||
validateHostname: validateHostname,
|
||||
addFromQueryLog: addFromQueryLog
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
require_once "func.php";
|
||||
|
||||
require_once('auth.php');
|
||||
|
||||
// Authentication checks
|
||||
if (isset($_POST['token'])) {
|
||||
check_cors();
|
||||
check_csrf($_POST['token']);
|
||||
} else {
|
||||
log_and_die('Not allowed (login session invalid or expired, please relogin on the Pi-hole dashboard)!');
|
||||
}
|
||||
|
||||
|
||||
switch ($_POST['action'])
|
||||
{
|
||||
case 'get': echo json_encode(echoCustomCNAMEEntries()); break;
|
||||
case 'add': echo json_encode(addCustomCNAMEEntry()); break;
|
||||
case 'delete': echo json_encode(deleteCustomCNAMEEntry()); break;
|
||||
default:
|
||||
die("Wrong action");
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
require_once "func.php";
|
||||
|
||||
$customDNSFile = "/etc/pihole/custom.list";
|
||||
|
||||
require_once('auth.php');
|
||||
|
||||
// Authentication checks
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
<input id="customTimeout" class="form-control" type="number" value="60">
|
||||
<div class="input-group-btn" data-toggle="buttons">
|
||||
<label class="btn btn-default">
|
||||
<input type="radio"> Secs
|
||||
<input id="selSec" type="radio"> Secs
|
||||
</label>
|
||||
<label id="btnMins" class="btn btn-default active">
|
||||
<input type="radio"> Mins
|
||||
<input id="selMin" type="radio"> Mins
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
@@ -99,6 +99,6 @@
|
||||
|
||||
</div>
|
||||
<!-- ./wrapper -->
|
||||
<script src="scripts/pi-hole/js/footer.js"></script>
|
||||
<script src="scripts/pi-hole/js/footer.js?v=<?=$cacheVer?>"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+174
-29
@@ -81,6 +81,9 @@ function pihole_execute($argument_string, $error_on_failure = true) {
|
||||
return $output;
|
||||
}
|
||||
|
||||
// Custom DNS
|
||||
$customDNSFile = "/etc/pihole/custom.list";
|
||||
|
||||
function echoCustomDNSEntries()
|
||||
{
|
||||
$entries = getCustomDNSEntries();
|
||||
@@ -121,19 +124,8 @@ function getCustomDNSEntries()
|
||||
return $entries;
|
||||
}
|
||||
|
||||
function addCustomDNSEntry($ip="", $domain="", $json_reply=true)
|
||||
function addCustomDNSEntry($ip="", $domain="", $json=true)
|
||||
{
|
||||
function error($msg)
|
||||
{
|
||||
global $json_reply;
|
||||
if($json_reply)
|
||||
return errorJsonResponse($msg);
|
||||
else {
|
||||
echo $msg."<br>";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if(isset($_REQUEST['ip']))
|
||||
@@ -143,18 +135,18 @@ function addCustomDNSEntry($ip="", $domain="", $json_reply=true)
|
||||
$domain = trim($_REQUEST['domain']);
|
||||
|
||||
if (empty($ip))
|
||||
return error("IP must be set");
|
||||
return returnError("IP must be set", $json);
|
||||
|
||||
$ipType = get_ip_type($ip);
|
||||
|
||||
if (!$ipType)
|
||||
return error("IP must be valid");
|
||||
return returnError("IP must be valid", $json);
|
||||
|
||||
if (empty($domain))
|
||||
return error("Domain must be set");
|
||||
return returnError("Domain must be set", $json);
|
||||
|
||||
if (!is_valid_domain_name($domain))
|
||||
return error("Domain must be valid");
|
||||
return returnError("Domain must be valid", $json);
|
||||
|
||||
// Only check for duplicates if adding new records from the web UI (not through Teleporter)
|
||||
if(isset($_REQUEST['ip']) || isset($_REQUEST['domain']))
|
||||
@@ -162,17 +154,17 @@ function addCustomDNSEntry($ip="", $domain="", $json_reply=true)
|
||||
$existingEntries = getCustomDNSEntries();
|
||||
foreach ($existingEntries as $entry)
|
||||
if ($entry->domain == $domain && get_ip_type($entry->ip) == $ipType)
|
||||
return error("This domain already has a custom DNS entry for an IPv" . $ipType);
|
||||
return returnError("This domain already has a custom DNS entry for an IPv" . $ipType, $json);
|
||||
}
|
||||
|
||||
// Add record
|
||||
pihole_execute("-a addcustomdns ".$ip." ".$domain);
|
||||
|
||||
return $json_reply ? successJsonResponse() : true;
|
||||
return returnSuccess("", $json);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return error($ex->getMessage());
|
||||
return error($ex->getMessage(), $json);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,10 +176,10 @@ function deleteCustomDNSEntry()
|
||||
$domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: "";
|
||||
|
||||
if (empty($ip))
|
||||
return errorJsonResponse("IP must be set");
|
||||
return returnError("IP must be set");
|
||||
|
||||
if (empty($domain))
|
||||
return errorJsonResponse("Domain must be set");
|
||||
return returnError("Domain must be set");
|
||||
|
||||
$existingEntries = getCustomDNSEntries();
|
||||
|
||||
@@ -200,15 +192,15 @@ function deleteCustomDNSEntry()
|
||||
}
|
||||
|
||||
if (!$found)
|
||||
return errorJsonResponse("This domain/ip association does not exist");
|
||||
return returnError("This domain/ip association does not exist");
|
||||
|
||||
pihole_execute("-a removecustomdns ".$ip." ".$domain);
|
||||
|
||||
return successJsonResponse();
|
||||
return returnSuccess();
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return errorJsonResponse($ex->getMessage());
|
||||
return returnError($ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,17 +236,170 @@ function deleteAllCustomDNSEntries()
|
||||
}
|
||||
}
|
||||
|
||||
return successJsonResponse();
|
||||
return returnSuccess();
|
||||
}
|
||||
|
||||
function successJsonResponse($message = "")
|
||||
// CNAME
|
||||
$customCNAMEFile = "/etc/dnsmasq.d/05-pihole-custom-cname.conf";
|
||||
|
||||
function echoCustomCNAMEEntries()
|
||||
{
|
||||
return [ "success" => true, "message" => $message ];
|
||||
$entries = getCustomCNAMEEntries();
|
||||
|
||||
$data = [];
|
||||
foreach ($entries as $entry)
|
||||
$data[] = [ $entry->domain, $entry->target ];
|
||||
|
||||
return [ "data" => $data ];
|
||||
}
|
||||
|
||||
function errorJsonResponse($message = "")
|
||||
function getCustomCNAMEEntries()
|
||||
{
|
||||
return [ "success" => false, "message" => $message ];
|
||||
global $customCNAMEFile;
|
||||
|
||||
$entries = [];
|
||||
|
||||
if (!file_exists($customCNAMEFile)) return $entries;
|
||||
|
||||
$handle = fopen($customCNAMEFile, "r");
|
||||
if ($handle)
|
||||
{
|
||||
while (($line = fgets($handle)) !== false) {
|
||||
$line = str_replace("cname=","", $line);
|
||||
$line = str_replace("\r","", $line);
|
||||
$line = str_replace("\n","", $line);
|
||||
$explodedLine = explode (",", $line);
|
||||
|
||||
if (count($explodedLine) <= 1)
|
||||
continue;
|
||||
|
||||
$data = new \stdClass();
|
||||
$data->domains = array_slice($explodedLine, 0, -1);
|
||||
$data->domain = implode(",", $data->domains);
|
||||
$data->target = $explodedLine[count($explodedLine)-1];
|
||||
$entries[] = $data;
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
}
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
function addCustomCNAMEEntry($domain="", $target="", $json=true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(isset($_REQUEST['domain']))
|
||||
$domain = $_REQUEST['domain'];
|
||||
|
||||
if(isset($_REQUEST['target']))
|
||||
$target = $_REQUEST['target'];
|
||||
|
||||
if (empty($domain))
|
||||
return returnError("Domain must be set", $json);
|
||||
|
||||
if (empty($target))
|
||||
return returnError("Target must be set", $json);
|
||||
|
||||
// Check if each submitted domain is valid
|
||||
$domains = array_map('trim', explode(",", $domain));
|
||||
foreach ($domains as $d) {
|
||||
if (!is_valid_domain_name($d))
|
||||
return returnError("Domain '$d' is not valid", $json);
|
||||
}
|
||||
|
||||
$existingEntries = getCustomCNAMEEntries();
|
||||
|
||||
// Check if a record for one of the domains already exists
|
||||
foreach ($existingEntries as $entry)
|
||||
foreach ($domains as $d)
|
||||
if (in_array($d, $entry->domains))
|
||||
return returnError("There is already a CNAME record for '$d'", $json);
|
||||
|
||||
pihole_execute("-a addcustomcname ".$domain." ".$target);
|
||||
|
||||
return returnSuccess("", $json);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return returnError($ex->getMessage(), $json);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteCustomCNAMEEntry()
|
||||
{
|
||||
try
|
||||
{
|
||||
$target = !empty($_REQUEST['target']) ? $_REQUEST['target']: "";
|
||||
$domain = !empty($_REQUEST['domain']) ? $_REQUEST['domain']: "";
|
||||
|
||||
if (empty($target))
|
||||
return returnError("Target must be set");
|
||||
|
||||
if (empty($domain))
|
||||
return returnError("Domain must be set");
|
||||
|
||||
$existingEntries = getCustomCNAMEEntries();
|
||||
|
||||
$found = false;
|
||||
foreach ($existingEntries as $entry)
|
||||
if ($entry->domain == $domain)
|
||||
if ($entry->target == $target) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$found)
|
||||
return returnError("This domain/ip association does not exist");
|
||||
|
||||
pihole_execute("-a removecustomcname ".$domain." ".$target);
|
||||
|
||||
return returnSuccess();
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return returnError($ex->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function deleteAllCustomCNAMEEntries()
|
||||
{
|
||||
try
|
||||
{
|
||||
$existingEntries = getCustomCNAMEEntries();
|
||||
|
||||
foreach ($existingEntries as $entry) {
|
||||
pihole_execute("-a removecustomcname ".$entry->domain." ".$entry->target);
|
||||
}
|
||||
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return returnError($ex->getMessage());
|
||||
}
|
||||
|
||||
return returnSuccess();
|
||||
}
|
||||
|
||||
function returnSuccess($message = "", $json = true)
|
||||
{
|
||||
if ($json) {
|
||||
return [ "success" => true, "message" => $message ];
|
||||
} else {
|
||||
echo $msg."<br>";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function returnError($message = "", $json = true)
|
||||
{
|
||||
if ($json) {
|
||||
return [ "success" => false, "message" => $message ];
|
||||
} else {
|
||||
echo $msg."<br>";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
+153
-13
@@ -77,6 +77,11 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
foreach ($names as $name) {
|
||||
// Silently skip this entry when it is empty or not a string (e.g. NULL)
|
||||
if(!is_string($name) || strlen($name) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':name', $name, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding name: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " groups");
|
||||
@@ -184,7 +189,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
throw new Exception('Error while querying gravity\'s client_by_group table: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$stmt = $FTLdb->prepare('SELECT name FROM network WHERE id = (SELECT network_id FROM network_addresses WHERE ip = :ip);');
|
||||
$stmt = $FTLdb->prepare('SELECT name FROM network_addresses WHERE ip = :ip;');
|
||||
if (!$stmt) {
|
||||
throw new Exception('Error while preparing network table statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -206,6 +211,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
while ($gres = $group_query->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($groups, $gres['group_id']);
|
||||
}
|
||||
$group_query->finalize();
|
||||
$res['groups'] = $groups;
|
||||
array_push($data, $res);
|
||||
}
|
||||
@@ -221,7 +227,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
$QUERYDB = getQueriesDBFilename();
|
||||
$FTLdb = SQLite3_connect($QUERYDB);
|
||||
|
||||
$query = $FTLdb->query('SELECT DISTINCT ip,network.name FROM network_addresses AS name LEFT JOIN network ON network.id = network_id ORDER BY ip ASC;');
|
||||
$query = $FTLdb->query('SELECT DISTINCT id,hwaddr,macVendor FROM network ORDER BY firstSeen DESC;');
|
||||
if (!$query) {
|
||||
throw new Exception('Error while querying FTL\'s database: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -229,7 +235,47 @@ if ($_POST['action'] == 'get_groups') {
|
||||
// Loop over results
|
||||
$ips = array();
|
||||
while ($res = $query->fetchArray(SQLITE3_ASSOC)) {
|
||||
$ips[$res['ip']] = $res['name'] !== null ? $res['name'] : '';
|
||||
$id = intval($res["id"]);
|
||||
|
||||
// Get possibly associated IP addresses and hostnames for this client
|
||||
$query_ips = $FTLdb->query("SELECT ip,name FROM network_addresses WHERE network_id = $id ORDER BY lastSeen DESC;");
|
||||
$addresses = [];
|
||||
$names = [];
|
||||
while ($res_ips = $query_ips->fetchArray(SQLITE3_ASSOC)) {
|
||||
array_push($addresses, utf8_encode($res_ips["ip"]));
|
||||
if($res_ips["name"] !== null)
|
||||
array_push($names,utf8_encode($res_ips["name"]));
|
||||
}
|
||||
$query_ips->finalize();
|
||||
|
||||
// Prepare extra information
|
||||
$extrainfo = "";
|
||||
// Add list of associated host names to info string (if available)
|
||||
if(count($names) === 1)
|
||||
$extrainfo .= "hostname: ".$names[0];
|
||||
else if(count($names) > 0)
|
||||
$extrainfo .= "hostnames: ".implode(", ", $names);
|
||||
|
||||
// Add device vendor to info string (if available)
|
||||
if (strlen($res["macVendor"]) > 0) {
|
||||
if (count($names) > 0)
|
||||
$extrainfo .= "; ";
|
||||
$extrainfo .= "vendor: ".htmlspecialchars($res["macVendor"]);
|
||||
}
|
||||
|
||||
// Add list of associated host names to info string (if available and if this is not a mock device)
|
||||
if (stripos($res["hwaddr"], "ip-") === FALSE) {
|
||||
|
||||
if ((count($names) > 0 || strlen($res["macVendor"]) > 0) && count($addresses) > 0)
|
||||
$extrainfo .= "; ";
|
||||
|
||||
if(count($addresses) === 1)
|
||||
$extrainfo .= "address: ".$addresses[0];
|
||||
else if(count($addresses) > 0)
|
||||
$extrainfo .= "addresses: ".implode(", ", $addresses);
|
||||
}
|
||||
|
||||
$ips[strtoupper($res['hwaddr'])] = $extrainfo;
|
||||
}
|
||||
$FTLdb->close();
|
||||
|
||||
@@ -243,6 +289,9 @@ if ($_POST['action'] == 'get_groups') {
|
||||
if (isset($ips[$res['ip']])) {
|
||||
unset($ips[$res['ip']]);
|
||||
}
|
||||
if (isset($ips["IP-".$res['ip']])) {
|
||||
unset($ips["IP-".$res['ip']]);
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-type: application/json');
|
||||
@@ -262,6 +311,11 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
foreach ($ips as $ip) {
|
||||
// Silently skip this entry when it is empty or not a string (e.g. NULL)
|
||||
if(!is_string($ip) || strlen($ip) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':ip', $ip, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding ip: ' . $db->lastErrorMsg());
|
||||
}
|
||||
@@ -453,18 +507,41 @@ if ($_POST['action'] == 'get_groups') {
|
||||
} catch (\Exception $ex) {
|
||||
JSON_error($ex->getMessage());
|
||||
}
|
||||
} elseif ($_POST['action'] == 'add_domain') {
|
||||
} elseif ($_POST['action'] == 'add_domain' || $_POST['action'] == 'replace_domain') {
|
||||
// Add new domain
|
||||
try {
|
||||
$domains = explode(' ', html_entity_decode(trim($_POST['domain'])));
|
||||
$before = intval($db->querySingle("SELECT COUNT(*) FROM domainlist;"));
|
||||
$total = count($domains);
|
||||
$added = 0;
|
||||
$stmt = $db->prepare('REPLACE INTO domainlist (domain,type,comment) VALUES (:domain,:type,:comment)');
|
||||
if (!$stmt) {
|
||||
|
||||
// Prepare INSERT INTO statement
|
||||
$insert_stmt = $db->prepare('INSERT OR IGNORE INTO domainlist (domain,type) VALUES (:domain,:type)');
|
||||
if (!$insert_stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
// Prepare UPDATE statement
|
||||
$update_stmt = $db->prepare('UPDATE domainlist SET comment = :comment WHERE domain = :domain AND type = :type');
|
||||
if (!$update_stmt) {
|
||||
throw new Exception('While preparing statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
$check_stmt = null;
|
||||
$delete_stmt = null;
|
||||
if($_POST['action'] == 'replace_domain') {
|
||||
// Check statement will reveal any group associations for a given (domain,type) which do NOT belong to the default group
|
||||
$check_stmt = $db->prepare('SELECT EXISTS(SELECT domain FROM domainlist_by_group dlbg JOIN domainlist dl on dlbg.domainlist_id = dl.id WHERE dl.domain = :domain AND dlbg.group_id != 0)');
|
||||
if (!$check_stmt) {
|
||||
throw new Exception('While preparing check statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
// Delete statement will remove this domain from any type of list
|
||||
$delete_stmt = $db->prepare('DELETE FROM domainlist WHERE domain = :domain');
|
||||
if (!$delete_stmt) {
|
||||
throw new Exception('While preparing delete statement: ' . $db->lastErrorMsg());
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_POST['type'])) {
|
||||
$type = intval($_POST['type']);
|
||||
} else if (isset($_POST['list']) && $_POST['list'] === "white") {
|
||||
@@ -473,7 +550,8 @@ if ($_POST['action'] == 'get_groups') {
|
||||
$type = ListType::blacklist;
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':type', $type, SQLITE3_TEXT)) {
|
||||
if (!$insert_stmt->bindValue(':type', $type, SQLITE3_TEXT) ||
|
||||
!$update_stmt->bindValue(':type', $type, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding type: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
@@ -482,11 +560,16 @@ if ($_POST['action'] == 'get_groups') {
|
||||
// Store NULL in database for empty comments
|
||||
$comment = null;
|
||||
}
|
||||
if (!$stmt->bindValue(':comment', $comment, SQLITE3_TEXT)) {
|
||||
if (!$update_stmt->bindValue(':comment', $comment, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding comment: ' . $db->lastErrorMsg());
|
||||
}
|
||||
|
||||
foreach ($domains as $domain) {
|
||||
// Silently skip this entry when it is empty or not a string (e.g. NULL)
|
||||
if(!is_string($domain) || strlen($domain) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$input = $domain;
|
||||
// Convert domain name to IDNA ASCII form for international domains
|
||||
if (extension_loaded("intl")) {
|
||||
@@ -504,7 +587,7 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
}
|
||||
|
||||
if(strlen($_POST['type']) === 2 && $_POST['type'][1] === 'W')
|
||||
if(isset($_POST['type']) && strlen($_POST['type']) === 2 && $_POST['type'][1] === 'W')
|
||||
{
|
||||
// Apply wildcard-style formatting
|
||||
$domain = "(\\.|^)".str_replace(".","\\.",$domain)."$";
|
||||
@@ -527,13 +610,62 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
// First try to delete any occurrences of this domain if we're in
|
||||
// replace mode. Only do this when the domain to be replaced is in
|
||||
// the default group! Otherwise, we would shuffle group settings and
|
||||
// just throw an error at the user to tell them to change this
|
||||
// domain manually. This ensures user's will really get what they
|
||||
// want from us.
|
||||
if($_POST['action'] == 'replace_domain') {
|
||||
if (!$check_stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain to check: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
|
||||
$check_result = $check_stmt->execute();
|
||||
if (!$check_result) {
|
||||
throw new Exception('While executing check: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
|
||||
// Check return value of CHECK query (0 = only default group, 1 = special group assignments)
|
||||
$only_default_group = (($check_result->fetchArray(SQLITE3_NUM)[0]) == 0) ? true : false;
|
||||
if(!$only_default_group) {
|
||||
throw new Exception('Domain ' . $domain . ' is configured with special group settings.<br>'.
|
||||
'Please modify the domain on the respective group management pages.');
|
||||
}
|
||||
|
||||
if (!$delete_stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
|
||||
if (!$delete_stmt->execute()) {
|
||||
throw new Exception('While executing: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!$insert_stmt->bindValue(':domain', $domain, SQLITE3_TEXT) ||
|
||||
!$update_stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
|
||||
if (!$stmt->execute()) {
|
||||
throw new Exception('While executing: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
// First execute INSERT OR IGNORE statement to create a record for
|
||||
// this domain (ignore if already existing)
|
||||
if (!$insert_stmt->execute()) {
|
||||
throw new Exception('While executing INSERT OT IGNORE: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
|
||||
// Then update the record with a new comment (and modification date
|
||||
// due to the trigger event) We are not using REPLACE INTO to avoid
|
||||
// the initial DELETE event (loosing group assignments in case an
|
||||
// entry did already exist).
|
||||
if (!$update_stmt->execute()) {
|
||||
throw new Exception('While executing UPDATE: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " domains");
|
||||
}
|
||||
$added++;
|
||||
@@ -764,6 +896,11 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
foreach ($addresses as $address) {
|
||||
// Silently skip this entry when it is empty or not a string (e.g. NULL)
|
||||
if(!is_string($address) || strlen($address) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(preg_match("/[^a-zA-Z0-9:\/?&%=~._()-;]/", $address) !== 0) {
|
||||
throw new Exception('<strong>Invalid adlist URL ' . htmlentities($address) . '</strong><br>'.
|
||||
'Added ' . $added . " out of ". $total . " adlists");
|
||||
@@ -906,7 +1043,10 @@ if ($_POST['action'] == 'get_groups') {
|
||||
}
|
||||
|
||||
foreach ($domains as $domain) {
|
||||
$input = $domain;
|
||||
// Silently skip this entry when it is empty or not a string (e.g. NULL)
|
||||
if(!is_string($domain) || strlen($domain) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$stmt->bindValue(':domain', $domain, SQLITE3_TEXT)) {
|
||||
throw new Exception('While binding domain: <strong>' . $db->lastErrorMsg() . '</strong><br>'.
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
$hostname = gethostname() ? gethostname() : "";
|
||||
|
||||
check_cors();
|
||||
|
||||
// Create cache busting version
|
||||
$cacheVer = filemtime(__FILE__);
|
||||
|
||||
// Generate CSRF token
|
||||
if(empty($_SESSION['token'])) {
|
||||
@@ -185,29 +188,31 @@
|
||||
html { background-color: #000; }
|
||||
</style>
|
||||
<?php } ?>
|
||||
<link rel="stylesheet" href="style/vendor/SourceSansPro/SourceSansPro.css">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/font-awesome/css/all.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/datatables.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/daterangepicker.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/AdminLTE.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/SourceSansPro/SourceSansPro.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap/css/bootstrap.min.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/font-awesome/css/all.min.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/datatables.min.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/daterangepicker.min.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/AdminLTE.min.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/select2.min.css?v=<?=$cacheVer?>">
|
||||
|
||||
<?php if (in_array($scriptname, array("groups.php", "groups-adlists.php", "groups-clients.php", "groups-domains.php"))){ ?>
|
||||
<link rel="stylesheet" href="style/vendor/animate.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap-select.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap-toggle.min.css">
|
||||
<link rel="stylesheet" href="style/vendor/animate.min.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap-select.min.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/vendor/bootstrap-toggle.min.css?v=<?=$cacheVer?>">
|
||||
<?php } ?>
|
||||
<link rel="stylesheet" href="style/pi-hole.css">
|
||||
<link rel="stylesheet" href="style/themes/<?php echo $theme; ?>.css">
|
||||
<noscript><link rel="stylesheet" href="style/vendor/js-warn.css"></noscript>
|
||||
<link rel="stylesheet" href="style/pi-hole.css?v=<?=$cacheVer?>">
|
||||
<link rel="stylesheet" href="style/themes/<?php echo $theme; ?>.css?v=<?=$cacheVer?>">
|
||||
<noscript><link rel="stylesheet" href="style/vendor/js-warn.css?v=<?=$cacheVer?>"></noscript>
|
||||
|
||||
<script src="scripts/vendor/jquery.min.js"></script>
|
||||
<script src="style/vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script src="scripts/vendor/adminlte.min.js"></script>
|
||||
<script src="scripts/vendor/bootstrap-notify.min.js"></script>
|
||||
<script src="scripts/vendor/datatables.min.js"></script>
|
||||
<script src="scripts/vendor/moment.min.js"></script>
|
||||
<script src="scripts/vendor/Chart.min.js"></script>
|
||||
<script src="scripts/vendor/jquery.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="style/vendor/bootstrap/js/bootstrap.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/adminlte.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/bootstrap-notify.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/select2.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/datatables.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/moment.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/vendor/Chart.min.js?v=<?=$cacheVer?>"></script>
|
||||
</head>
|
||||
<body class="hold-transition sidebar-mini <?php if($boxedlayout){ ?>layout-boxed<?php } ?>">
|
||||
<noscript>
|
||||
@@ -343,7 +348,7 @@ if($auth) {
|
||||
{
|
||||
echo "text-vivid-blue";
|
||||
}
|
||||
?>"\"></i> Temp: <span id="rawtemp" hidden><?php echo $celsius;?></span><span id="tempdisplay"></span><?php
|
||||
?>"></i> Temp: <span id="rawtemp" hidden><?php echo $celsius;?></span><span id="tempdisplay"></span></span><?php
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -454,12 +459,6 @@ if($auth) {
|
||||
<i class="fa fa-ban"></i> <span>Blacklist</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Local DNS Records -->
|
||||
<li<?php if($scriptname === "dns_records.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="dns_records.php">
|
||||
<i class="fa fa-address-book"></i> <span>Local DNS Records</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Group Management -->
|
||||
<li class="treeview<?php if (in_array($scriptname, array("groups.php", "groups-adlists.php", "groups-clients.php", "groups-domains.php"))){ ?> active<?php } ?>">
|
||||
<a href="#">
|
||||
@@ -601,6 +600,27 @@ if($auth) {
|
||||
<i class="fa fa-cogs"></i> <span>Settings</span>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Local DNS Records -->
|
||||
<li class="treeview <?php if(in_array($scriptname, array("dns_records.php", "cname_records.php"))){ ?>active<?php } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-address-book"></i> <span>Local DNS</span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu">
|
||||
<li<?php if($scriptname === "dns_records.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="dns_records.php">
|
||||
<i class="fa fa-address-book"></i> <span>DNS Records</span>
|
||||
</a>
|
||||
</li>
|
||||
<li<?php if($scriptname === "cname_records.php"){ ?> class="active"<?php } ?>>
|
||||
<a href="cname_records.php">
|
||||
<i class="fa fa-address-book"></i> <span>CNAME Records</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- Logout -->
|
||||
<?php
|
||||
// Show Logout button if $auth is set and authorization is required
|
||||
|
||||
@@ -30,34 +30,34 @@
|
||||
<input type="password" id="loginpw" name="pw" class="form-control" placeholder="Password" autofocus>
|
||||
<span class="fa fa-key form-control-feedback"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-12">
|
||||
<button type="submit" class="btn btn-primary form-control"><i class="fas fa-sign-in-alt"></i> Log in</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="col-xs-8 hidden-xs hidden-sm">
|
||||
<ul>
|
||||
<li><kbd>Return</kbd> → Log in and go to requested page (<?php echo $scriptname; ?>)</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>Return</kbd> → Log in and go to Settings page</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><kbd>Return</kbd> → Log in and go to requested page (<?php echo $scriptname; ?>)</li>
|
||||
<li><kbd>Ctrl</kbd>+<kbd>Return</kbd> → Log in and go to Settings page</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-4">
|
||||
<div class="pull-right">
|
||||
<div>
|
||||
<input type="checkbox" id="logincookie" name="persistentlogin">
|
||||
<label for="logincookie">Remember me for 7 days</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" id="logincookie" name="persistentlogin">
|
||||
<label for="logincookie">Remember me for 7 days</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary pull-right"><i class="fas fa-sign-in-alt"></i> Log in</button>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<div class="box box-<?php if (!$wrongpassword) { ?>info<?php } else { ?>danger<?php }
|
||||
if (!$wrongpassword) { ?> collapsed-box<?php } ?> box-solid">
|
||||
<div class="box box-<?php if (!$wrongpassword) { ?>info collapsed-box<?php } else { ?>danger<?php }?>">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">Forgot password</h3>
|
||||
|
||||
<h3 class="box-title">Forgot password?</h3>
|
||||
<div class="box-tools pull-right">
|
||||
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i
|
||||
class="fa <?php if ($wrongpassword) { ?>fa-minus<?php } else { ?>fa-plus<?php } ?>"></i>
|
||||
<button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa <?php if ($wrongpassword) { ?>fa-minus<?php } else { ?>fa-plus<?php } ?>"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -738,7 +738,11 @@ function addStaticDHCPLease($mac, $ip, $hostname) {
|
||||
// Flush network table
|
||||
case "flusharp":
|
||||
$output = pihole_execute("arpflush quiet");
|
||||
$error = implode("<br>", $output);
|
||||
$error = "";
|
||||
if(is_array($output))
|
||||
{
|
||||
$error = implode("<br>", $output);
|
||||
}
|
||||
if(strlen($error) == 0)
|
||||
{
|
||||
$success .= "The network table has been flushed";
|
||||
|
||||
@@ -523,6 +523,34 @@ if(isset($_POST["action"]))
|
||||
$importedsomething = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_POST["localcnamerecords"]) && $file->getFilename() === "05-pihole-custom-cname.conf")
|
||||
{
|
||||
if($flushtables) {
|
||||
// Defined in func.php included via auth.php
|
||||
deleteAllCustomCNAMEEntries();
|
||||
}
|
||||
|
||||
$num = 0;
|
||||
$localcnamerecords = process_file(file_get_contents($file));
|
||||
foreach($localcnamerecords as $record) {
|
||||
$line = str_replace("cname=","", $record);
|
||||
$line = str_replace("\r","", $line);
|
||||
$line = str_replace("\n","", $line);
|
||||
$explodedLine = explode (",", $line);
|
||||
|
||||
$domain = implode(",", array_slice($explodedLine, 0, -1));
|
||||
$target = $explodedLine[count($explodedLine)-1];
|
||||
|
||||
if(addCustomCNAMEEntry($domain, $target, false))
|
||||
$num++;
|
||||
}
|
||||
|
||||
echo "Processed local CNAME records (".$num." entries)<br>\n";
|
||||
if($num > 0) {
|
||||
$importedsomething = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($importedsomething)
|
||||
|
||||
Vendored
+2
File diff suppressed because one or more lines are too long
+19
-22
@@ -14,7 +14,7 @@ $piholeFTLConf = piholeFTLConfig();
|
||||
|
||||
// Handling of PHP internal errors
|
||||
$last_error = error_get_last();
|
||||
if($last_error["type"] === E_WARNING || $last_error["type"] === E_ERROR)
|
||||
if(isset($last_error) && ($last_error["type"] === E_WARNING || $last_error["type"] === E_ERROR))
|
||||
{
|
||||
$error .= "There was a problem applying your settings.<br>Debugging information:<br>PHP error (".htmlspecialchars($last_error["type"])."): ".htmlspecialchars($last_error["message"])." in ".htmlspecialchars($last_error["file"]).":".htmlspecialchars($last_error["line"]);
|
||||
}
|
||||
@@ -318,7 +318,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Time FTL started:</th>
|
||||
<td><?php print_r(get_FTL_data("start")); ?></td>
|
||||
<td><?php print_r(get_FTL_data("lstart")); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">User / Group:</th>
|
||||
@@ -397,7 +397,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
<br/>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<button type="button" class="btn btn-danger confirm-flushlogs btn-block">Flush logs</button>
|
||||
<button type="button" class="btn btn-danger confirm-flushlogs btn-block">Flush logs (last 24 hours)</button>
|
||||
</div>
|
||||
<p class="hidden-md hidden-lg"></p>
|
||||
<div class="col-md-4">
|
||||
@@ -618,8 +618,8 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div><input type="checkbox" name="useIPv6" id="useIPv6" class="DHCPgroup" <?php if ($DHCPIPv6){ ?>checked<?php }; if (!$DHCP){ ?> disabled<?php } ?>> <label for="useIPv6"><strong>Enable IPv6 support (SLAAC + RA)</strong></label></div>
|
||||
<div><input type="checkbox" name="DHCP_rapid_commit" id="DHCP_rapid_commit" class="DHCPgroup" <?php if ($DHCP_rapid_commit){ ?>checked<?php }; if (!$DHCP){ ?> disabled<?php } ?>> <label for="DHCP_rapid_commit"><strong>Enable DHCP rapid commit (fast address assignment)</strong></label></div>
|
||||
<div><input type="checkbox" name="DHCP_rapid_commit" id="DHCP_rapid_commit" class="DHCPgroup" <?php if ($DHCP_rapid_commit){ ?>checked<?php }; if (!$DHCP){ ?> disabled<?php } ?>> <label for="DHCP_rapid_commit"><strong>Enable DHCPv4 rapid commit (fast address assignment)</strong></label></div>
|
||||
<div><input type="checkbox" name="useIPv6" id="useIPv6" class="DHCPgroup" <?php if ($DHCPIPv6){ ?>checked<?php }; if (!$DHCP){ ?> disabled<?php } ?>> <label for="useIPv6"><strong>Enable IPv6 support (SLAAC + RA)</strong></label></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -674,7 +674,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
$type = 4;
|
||||
}
|
||||
|
||||
$host = $line[3];
|
||||
$host = htmlentities($line[3]);
|
||||
if ($host == "*") {
|
||||
$host = "<i>unknown</i>";
|
||||
}
|
||||
@@ -715,8 +715,11 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
title="Lease type: IPv<?php echo $lease["type"]; ?><br/>Remaining lease time: <?php echo $lease["TIME"]; ?><br/>DHCP UID: <?php echo $lease["clid"]; ?>">
|
||||
<td id="MAC"><?php echo $lease["hwaddr"]; ?></td>
|
||||
<td id="IP" data-order="<?php echo bin2hex(inet_pton($lease["IP"])); ?>"><?php echo $lease["IP"]; ?></td>
|
||||
<td id="HOST"><?php echo htmlentities($lease["host"]); ?></td>
|
||||
<td id="HOST"><?php echo $lease["host"]; ?></td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-danger btn-xs" id="removedynamic">
|
||||
<span class="fas fas fa-trash-alt"></span>
|
||||
</button>
|
||||
<button type="button" id="button" class="btn btn-warning btn-xs" data-static="alert">
|
||||
<span class="fas fas fa-file-import"></span>
|
||||
</button>
|
||||
@@ -970,21 +973,11 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
queries, Pi-hole requests the DNSSEC records needed to validate
|
||||
the replies. If a domain fails validation or the upstream does not
|
||||
support DNSSEC, this setting can cause issues resolving domains.
|
||||
Use Google, Cloudflare, DNS.WATCH, Quad9, or another DNS
|
||||
server which supports DNSSEC when activating DNSSEC. Note that
|
||||
Use an upstream DNS server which supports DNSSEC 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="https://dnssec.vs.uni-due.de/" rel="noopener" target="_blank">here</a>.</p>
|
||||
</div>
|
||||
<p>Validate DNS replies and cache DNSSEC data. When forwarding DNS
|
||||
queries, Pi-hole requests the DNSSEC records needed to validate
|
||||
the replies. If a domain fails validation or the upstream does not
|
||||
support DNSSEC, this setting can cause issues resolving domains.
|
||||
Use Google, Cloudflare, DNS.WATCH, Quad9, or another DNS
|
||||
server which supports DNSSEC 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="https://dnssec.vs.uni-due.de/" rel="noopener" target="_blank">here</a>.</p>
|
||||
<br>
|
||||
<h4>Conditional forwarding</h4>
|
||||
<p>If not configured as your DHCP server, Pi-hole typically won't be able to
|
||||
@@ -994,7 +987,7 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
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 DHCP server and which addresses belong to your local network.
|
||||
Exemplary inout is given below as placeholder in the text boxes (if empty).</p>
|
||||
Exemplary input is given below as placeholder in the text boxes (if empty).</p>
|
||||
<p>If your local network spans 192.168.0.1 - 192.168.0.255, then you will have to input
|
||||
<code>192.168.0.0/24</code>. If your local network is 192.168.47.1 - 192.168.47.255, it will
|
||||
be <code>192.168.47.0/24</code> and similar. If your network is larger, the CIDR has to be
|
||||
@@ -1383,6 +1376,10 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
<input type="checkbox" name="localdnsrecords" id="tele_localdnsrecords" value="true" checked>
|
||||
<label for="tele_localdnsrecords">Local DNS Records</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="checkbox" name="localcnamerecords" id="tele_localcnamerecords" value="true" checked>
|
||||
<label for="tele_localcnamerecords">Local CNAME Records</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
@@ -1426,9 +1423,9 @@ if (isset($_GET['tab']) && in_array($_GET['tab'], array("sysadmin", "adlists", "
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="scripts/vendor/jquery.confirm.min.js"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js"></script>
|
||||
<script src="scripts/pi-hole/js/settings.js"></script>
|
||||
<script src="scripts/vendor/jquery.confirm.min.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/utils.js?v=<?=$cacheVer?>"></script>
|
||||
<script src="scripts/pi-hole/js/settings.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
@@ -300,3 +300,7 @@
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-results > .select2-results__options {
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
+1
-1
@@ -24,7 +24,7 @@
|
||||
<label for="chk2">Automatic scrolling on update</label>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/taillog-FTL.js"></script>
|
||||
<script src="scripts/pi-hole/js/taillog-FTL.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
<label for="chk2">Automatic scrolling on update</label>
|
||||
</div>
|
||||
|
||||
<script src="scripts/pi-hole/js/taillog.js"></script>
|
||||
<script src="scripts/pi-hole/js/taillog.js?v=<?=$cacheVer?>"></script>
|
||||
|
||||
<?php
|
||||
require "scripts/pi-hole/php/footer.php";
|
||||
|
||||
Reference in New Issue
Block a user